octaltree / playwright-rust

Playwright port to Rust
316 stars 36 forks source link

How do I pass a Paramaters to JS script with evaluate_on_selector? #40

Closed foragerDev closed 2 years ago

octaltree commented 2 years ago

You can use it as follows https://github.com/octaltree/playwright-rust/blob/a672ce7311eb596459acf3bdeb1d09e177a488d1/tests/page/mod.rs#L546-L552

octaltree commented 2 years ago

Maybe this is occurring. https://github.com/octaltree/playwright-rust/issues/28

foragerDev commented 2 years ago

@octaltree I have seen this, suppose I have something like this in scripts/greet.js file.

async greet(...args) {
     let [first_name, last_name] = args;
     console.log(first_name, last_name);

     let data = await fetch ("www.test.com", {
      body: JSON.stringify {
          first_name: first_name,
          last_name: last_name
      }
      method: "post"
     };

     let response = data.json();
     return response;
}

Now I want to call it something like this:

const GREET: &str = include_str!("./scripts/greet.js");

fn greet(page: Arc<Page>){
    let name = vec!["Mark".to_string(), "Butcher".to_string()
    let response = page.evaluate_on_selector<Vec<String>, serde_json::value>("body", GREET, Some(name)).await?;
}

It did not work. What is the proper way to accomplish this, I was doing the same thing in rust-headless-chrome. But now able to this in playwright-rust. Can you please share the example?

octaltree commented 2 years ago

I have compiled your sentiments.

use playwright::{api::Page, Playwright};

#[tokio::main]
async fn main() -> Result<(), playwright::Error> {
    let playwright = Playwright::initialize().await?;
    playwright.prepare()?; // Install browsers
    let chromium = playwright.chromium();
    let browser = chromium.launcher().headless(true).launch().await?;
    let context = browser.context_builder().build().await?;
    let page = context.new_page().await?;
    page.goto_builder("https://example.com/").goto().await?;
    f(&page).await?;
    Ok(())
}

const S: &str = r#"
async function s(...args) {
     let [element, [first_name, last_name]] = args;
     return [element.innerText, first_name, last_name];
}
"#;

async fn f(page: &Page) -> Result<(), playwright::Error> {
    let name = vec!["Mark".to_string(), "Butcher".to_string()];
    let response = page
        .evaluate_on_selector::<Vec<String>, serde_json::Value>("body", S, Some(name))
        .await?;
    dbg!(response);
    Ok(())
}
[src/main.rs:28] response = Array [
    String("Example Domain\n\nThis domain is for use in illustrative examples in documents. You may use this domain in literature without prior coordination or asking for permission.\n\nMore information..."),
    String("Mark"),
    String("Butcher"),
]
foragerDev commented 2 years ago

@octaltree thanks for sharing the full solution. But I am running into an error, when I run the code above do not get an error, but I get Null, but I am expecting String, May be my script is not working. How do I know what is going wrong with my script?

Here is how I am using the above code:

        let args =  vec![options.campaign_url.clone()];

        let json_result = page.evaluate_on_selector::<Vec<String>, Value>("body", CAMPAIGN_URL, Some(args)).await.unwrap();
octaltree commented 2 years ago

I intentionally made an error, if you see ErrorResponded on unwrap, your js code is causing an error.

const S: &str = r#"
async function s(...args) {
     let [element, [first_name, last_name]] = args;
     throw new Error('panic');
     return [element.innerText, first_name, last_name];
}
"#;
Error: Arc(ErrorResponded(ErrorMessage { name: "Error", message: "Evaluation failed: Error: panic\n    at s (eval at evaluate (:303:29), <anonymous>:3:12)\n    at UtilityScript.evaluate (<anonymous>:313:26)\n    at UtilityScript.<anonymous> (<anonymous>:1:44)", stack: "Error: Evaluation failed: Error: panic\n    at s (eval at evaluate (:303:29), <anonymous>:3:12)\n    at UtilityScript.evaluate (<anonymous>:313:26)\n    at UtilityScript.<anonymous> (<anonymous>:1:44)\n    at CRExecutionContext.evaluateWithArguments (/home/octaltree/.cache/ms-playwright/playwright-rust/driver/package/lib/server/chromium/crExecutionContext.js:90:19)\n    at async evaluateExpression (/home/octaltree/.cache/ms-playwright/playwright-rust/driver/package/lib/server/javascript.js:186:16)\n    at async FrameManager.waitForSignalsCreatedBy (/home/octaltree/.cache/ms-playwright/playwright-rust/driver/package/lib/server/frames.js:109:24)\n    at async evaluateExpressionAndWaitForSignals (/home/octaltree/.cache/ms-playwright/playwright-rust/driver/package/lib/server/javascript.js:194:12)\n    at async ElementHandle.evaluateExpressionAndWaitForSignals (/home/octaltree/.cache/ms-playwright/playwright-rust/driver/package/lib/server/javascript.js:96:23)\n    at async Frame.evalOnSelectorAndWaitForSignals (/home/octaltree/.cache/ms-playwright/playwright-rust/driver/package/lib/server/frames.js:603:24)\n    at async FrameDispatcher.evalOnSelector (/home/octaltree/.cache/ms-playwright/playwright-rust/driver/package/lib/dispatchers/frameDispatcher.js:69:62)\n    at async DispatcherConnection.dispatch (/home/octaltree/.cache/ms-playwright/playwright-rust/driver/package/lib/dispatchers/dispatcher.js:232:22)" }))

On the other hand, if you want to debug playwright-rust internals, use env_logger. env_logger::init() and RUST_LOG=playwright=trace cargo run.

[2022-10-06T08:42:51Z DEBUG playwright::imp::core::transport] SEND Req { id: 5, guid: "frame@5d0511c35a2bfb135d8588390f3c73f1", method: "evalOnSelector", params: {"arg": Object {"handles": Array [], "value": Object {"a": Array [Object {"s": String("Mark")}, Object {"s": String("Butcher")}]}}, "expression": String("\nasync function s(...args) {\n     let [element, [first_name, last_name]] = args;\n     return [element.innerText, first_name, last_name];\n}\n"), "selector": String("body")} }
[2022-10-06T08:42:51Z TRACE playwright::imp::core::remote_object] poll WaitData
[2022-10-06T08:42:51Z DEBUG playwright::imp::core::transport] RECV {"guid":"frame@5d0511c35a2bfb135d8588390f3c73f1","method":"loadstate","params":{"add":"domcontentloaded"}}
[2022-10-06T08:42:51Z DEBUG playwright::imp::core::transport] RECV {"guid":"page@0b1f5395277faaa4800e9a82757b781e","method":"domcontentloaded","params":{}}
[2022-10-06T08:42:51Z DEBUG playwright::imp::core::transport] RECV {"id":5,"result":{"value":{"a":[{"s":"Example Domain\n\nThis domain is for use in illustrative examples in documents. You may use this domain in literature without prior coordination or asking for permission.\n\nMore information..."},{"s":"Mark"},{"s":"Butcher"}]}}}
[2022-10-06T08:42:51Z TRACE playwright::imp::core::remote_object] poll WaitData
[2022-10-06T08:42:51Z TRACE playwright::imp::core::message::de] any [Object {"a": Array [Object {"s": String("Example Domain\n\nThis domain is for use in illustrative examples in documents. You may use this domain in literature without prior coordination or asking for permission.\n\nMore information...")}, Object {"s": String("Mark")}, Object {"s": String("Butcher")}]}]
[2022-10-06T08:42:51Z TRACE playwright::imp::core::message::de] any [Object {"s": String("Example Domain\n\nThis domain is for use in illustrative examples in documents. You may use this domain in literature without prior coordination or asking for permission.\n\nMore information...")}]
[2022-10-06T08:42:51Z TRACE playwright::imp::core::message::de] str [Object {"s": String("Example Domain\n\nThis domain is for use in illustrative examples in documents. You may use this domain in literature without prior coordination or asking for permission.\n\nMore information...")}]
[2022-10-06T08:42:51Z TRACE playwright::imp::core::message::de] any [Object {"s": String("Mark")}]
[2022-10-06T08:42:51Z TRACE playwright::imp::core::message::de] str [Object {"s": String("Mark")}]
[2022-10-06T08:42:51Z TRACE playwright::imp::core::message::de] any [Object {"s": String("Butcher")}]
[2022-10-06T08:42:51Z TRACE playwright::imp::core::message::de] str [Object {"s": String("Butcher")}]
[src/main.rs:29] response = Array [
    String("Example Domain\n\nThis domain is for use in illustrative examples in documents. You may use this domain in literature without prior coordination or asking for permission.\n\nMore information..."),
    String("Mark"),
    String("Butcher"),
]
foragerDev commented 2 years ago

Thanks @octaltree . damn it finally able to make my script work, There was a comment on top of the script which was causing it to fail. Seems like there might be an issue with comment parsing. This was the comment. It was causing my JS to fail without telling me anything. This code does not work in playwright but works in headless chrome

const S: &str = r#"
/**
 * 
 * @param {*} url to be converted into sales api URL
 */
async function s(...args) {
     let [first_name, last_name] = args;
     throw new Error('panic');
     return [element.innerText, first_name, last_name];
}
"#;
async fn f(page: &Page) -> Result<(), playwright::Error> {
    let name = vec!["Mark".to_string(), "Butcher".to_string()];    
    let response = page
        .evaluate::<Vec<String>, serde_json::Value>(S, name)
        .await?;
    dbg!(response);
    Ok(())
}

Here are the logs:

 [2022-10-06T19:22:24Z TRACE playwright::imp::core::remote_object] poll WaitData
[2022-10-06T19:22:24Z DEBUG playwright::imp::core::transport] SEND Req { id: 5, guid: "frame@0af5465ddc2f4c6cbb8dc2b0c85fb1ce", method: "evaluateExpression", params: {"arg": Object {"handles": Array [], "value": Object {"a": Array [Object {"s": String("Mark")}, Object {"s": String("Butcher")}]}}, "expression": String("\n/**\n * \n * @param {*} url to be converted into sales api URL\n */\nasync function s(...args) {\n     let [first_name, last_name] = args;\n     throw new Error('panic');\n     return [element.innerText, first_name, last_name];\n}\n")} }
[2022-10-06T19:22:24Z TRACE playwright::imp::core::remote_object] poll WaitData
[2022-10-06T19:22:24Z DEBUG playwright::imp::core::transport] RECV {"guid":"frame@0af5465ddc2f4c6cbb8dc2b0c85fb1ce","method":"loadstate","params":{"add":"domcontentloaded"}}
[2022-10-06T19:22:24Z DEBUG playwright::imp::core::transport] RECV {"guid":"page@742279c4fb6b0bf8bfcc21cb6e8f6777","method":"domcontentloaded","params":{}}
[2022-10-06T19:22:24Z DEBUG playwright::imp::core::transport] RECV {"id":5,"result":{"value":{"v":"undefined"}}}
[2022-10-06T19:22:24Z TRACE playwright::imp::core::remote_object] poll WaitData
[2022-10-06T19:22:24Z TRACE playwright::imp::core::message::de] any [Object {"v": String("undefined")}]
[2022-10-06T19:22:24Z TRACE playwright::imp::core::message::de] unit [Object {"v": String("undefined")}]
[src/main.rs:33] response = Null

And the bad part is it run successfully without reporting the error.