【问题标题】:Executing JavaScript library and function from Java Selenium从 Java Selenium 执行 JavaScript 库和函数
【发布时间】:2019-12-13 06:40:42
【问题描述】:

我在 Eclipse 中,编写 Java GUI/脚本,运行无头 chromedriver,但在尝试加载 html2canvas.js 时遇到问题 [1][2] 库,然后在浏览器中调用我在该库上编写的函数;我收到此代码的未定义错误:

String ss1ScriptLoc = "C:\\Users\\me\\Desktop\\resources\\html2canvas.min.js";
String ss2ScriptLoc = "C:\\Users\\me\\Desktop\\resources\\takeScreenShot.js";

je.executeScript(
        "var headID1 = document.getElementsByTagName('head')[0]; "
        + "var newScript1 = document.createElement('script'); "
        + "newScript1.type = 'text/javascript'; "
        + "newScript1.src = '" + ss1ScriptLoc + "'; "
        + "headID1.appendChild(newScript1); "
        + "var headID2 = document.getElementsByTagName('head')[0]; "
        + "var newScript2 = document.createElement('script'); "
        + "newScript2.type = 'text/javascript'; "
        + "newScript2.src = '" + ss2ScriptLoc + "'; "
        + "headID2.appendChild(newScript2); "
        + "$(document).ready( function () { takeScreenShot(); });"
    );

这会导致未定义函数“takeScreenShot();”的错误我以为我已经在本地 .js 文件中定义了。

Starting ChromeDriver 75.0.3770.140 (-refs/branch-heads/3770@{#1155}) on port 48415
Only local connections are allowed.
Please protect ports used by ChromeDriver and related test frameworks to prevent access by malicious code.
Aug 05, 2019 9:36:53 AM org.openqa.selenium.remote.ProtocolHandshake createSession
INFO: Detected dialect: W3C
java.util.concurrent.ExecutionException: org.openqa.selenium.JavascriptException: javascript error: takeScreenShot is not defined
  (Session info: headless chrome=75.0.3770.142)
Caused by: org.openqa.selenium.JavascriptException: javascript error: takeScreenShot is not defined
  (Session info: headless chrome=75.0.3770.142)
Build info: version: '3.141.59', revision: 'e82be7d358', time: '2018-11-14T08:25:48'
System info: host: '', ip: '', os.name: 'Windows 10', os.arch: 'amd64', os.version: '10.0', java.version: '1.8.0_191'
Driver info: org.openqa.selenium.chrome.ChromeDriver
Capabilities {acceptInsecureCerts: false, browserName: chrome, browserVersion: 75.0.3770.142, chrome: {chromedriverVersion: 75.0.3770.140 (2d9f97485c7b..., userDataDir: C:\Users\me\AppData\Lo...}, goog:chromeOptions: {debuggerAddress: localhost:55906}, javascriptEnabled: true, networkConnectionEnabled: false, pageLoadStrategy: normal, platform: XP, platformName: XP, proxy: Proxy(), setWindowRect: true, strictFileInteractability: false, timeouts: {implicit: 0, pageLoad: 300000, script: 30000}, unhandledPromptBehavior: dismiss and notify}
Session ID: 
    at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
    at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
    at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
    at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
    at org.openqa.selenium.remote.http.W3CHttpResponseCodec.createException(W3CHttpResponseCodec.java:187)
    at org.openqa.selenium.remote.http.W3CHttpResponseCodec.decode(W3CHttpResponseCodec.java:122)
    at org.openqa.selenium.remote.http.W3CHttpResponseCodec.decode(W3CHttpResponseCodec.java:49)
    at org.openqa.selenium.remote.HttpCommandExecutor.execute(HttpCommandExecutor.java:158)
    at org.openqa.selenium.remote.service.DriverCommandExecutor.execute(DriverCommandExecutor.java:83)
    at org.openqa.selenium.remote.RemoteWebDriver.execute(RemoteWebDriver.java:552)
    at org.openqa.selenium.remote.RemoteWebDriver.executeScript(RemoteWebDriver.java:489)
    at App.getScreenShot(App.java:170)
    at javax.swing.SwingWorker$1.call(SwingWorker.java:295)
    at java.util.concurrent.FutureTask.run(FutureTask.java:266)
    at javax.swing.SwingWorker.run(SwingWorker.java:334)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
    at java.lang.Thread.run(Thread.java:748)

edit1:添加了这个 sn-p,经过测试,可以工作,但实际上并没有做任何事情。

je.executeScript("");
je.executeScript("document.body.onload = myCustomFunc();"
    + "function myCustomFunc() { console.log('hello world'); }"
);

我将这两个库 .js 文件读入 List,然后制作了一个大的 StringBuilder,将所有元素附加到 StringBuilder,然后添加了 takeScreenShot() 函数来代替 myCustomFunc()。不会抛出任何错误,但也不会生成下载的屏幕截图文件。


edit2:我的加载函数

private String fileToJSString(final String inScriptFile) {
        Path p = Paths.get(inScriptFile);
        if (!Files.exists(p)) {
            System.err.println("fileToJSString : error, file does not exist");
            return null;
        }
        List<String> lines = null;
        try {
            lines = Files.readAllLines(Paths.get(inScriptFile));
        } catch (IOException e) {
            e.printStackTrace();
        }
        if (lines == null) {
            System.err.println("fileToJSString : error, no lines read from file");
            return null;
        }
        StringBuilder sb = new StringBuilder();
        for (String s : lines)
            sb.append(s.trim() + " ");
        return sb.toString();
    }

edit3:根据@jay-mattinson 的建议,我尝试加载脚本,但是运行时字符串变量不适用于 html2canvas.js 库(特殊字符太多 - 请参阅前面的实际文件的链接/以上在这篇文章中)

String ss1ScriptLoc = "C:\\Users\\me\\Desktop\\resources\\html2canvas.min.js";
String ss2ScriptLoc = "C:\\Users\\me\\Desktop\\resources\\takeScreenShot.js";
String jsScript1 = fileToJSString(ss1ScriptLoc);
String jsScript2 = fileToJSString(ss2ScriptLoc);
JavascriptExecutor je = (JavascriptExecutor) this.driver;
je.executeScript(
    "var headID = document.getElementsByTagName('head')[0]; "
    + "var newScript = document.createElement('script'); "
    + "newScript.type = 'text/javascript'; "
    + "var code = {" + jsScript1 + " " + jsScript2 + "};"
    + "newScript.appendChild(document.createTextNode(code));"
    + "headID.appendChild(newScript); "
    + "$(document).ready( function () { takeScreenShot(); });"
);

运行时抛出的特定错误;

Caused by: org.openqa.selenium.WebDriverException: unknown error: Runtime.evaluate threw exception: SyntaxError: Unexpected token !

edit4:这是我编写的基于 html2canvas 的函数,用于抓取网页上的表格图像;

function takeScreenShot() {
    if (document.getElementsByClassName("abc-top") == null) {
        console.log('not on the right web page with the right content. stopping script');
        return;
    }

    var hiddenLinkObj = document.createElement('a');
    hiddenLinkObj.setAttribute('href', '');
    hiddenLinkObj.setAttribute('id', '');
    document.body.insertBefore(hiddenLinkObj, document.body.childNodes[0]);

    var s1 = window.location.href;
    var idx = s1.lastIndexOf('/');
    var res = s1.substring(idx + 1, s1.length);
    hiddenLinkObj.setAttribute('download', 's' + res + '-s.png');

    var partA = document.querySelector('[abc-show="showInfo"]');
    var partB = partA.getElementsByClassName('row');
    var partC = partB[3].childNodes[0];
    partC.setAttribute('style', 'background-color:#fafafa');
    var result = partC;

    html2canvas(
        result, {
            onrendered: function(canvas) {
                var myImage = canvas.toDataURL("image/png");
                hiddenLinkObj.setAttribute('href', myImage.replace("image/png", "image/octet-stream"));
                hiddenLinkObj.click();
            }
        }
    );
}

所以我的问题/问题:为什么在 takeScreenShot.js 文件中定义和加载它时会出现未定义的函数错误? - 如何加载合适大小的 html2canvas JS 库和第二个/short 库包含我基于 html2canvas 库编写的单个 JS 函数并在 selenium 中执行我的函数? -

我猜这是因为我缺乏 JavaScript 知识,而且我错过了一个明显的错误。任何和所有帮助表示赞赏。

【问题讨论】:

  • 您正在将本地路径传递到页面的脚本 SRC。此外,您不需要为脚本创建标记。 Selenium 将为您做到这一点。作为旁注,Selenium 可以为您截取屏幕截图: File full_scrn = ((TakesScreenshot)driver).getScreenshotAs(OutputType.FILE);

标签: javascript java selenium selenium-webdriver


【解决方案1】:

这是我使用 Selenium 执行/注入本地存储的“.js”文件的方法:

  File newJSFile = new File(path_to_local_js_file);
        if (newJSFile.exists())
        {
            try
            {
                  Scanner sc = new Scanner(new FileInputStream(newJSFile));
        String js_TxtFile = ""; 
            while (sc.hasNext()) {          
                String[] s = sc.next().split("\r\n");   
                for (int i = 0; i < s.length; i++) {
                    js_TxtFile += s[i];
                    js_TxtFile += " ";
                }   

            }
              try
                {
                ((JavascriptExecutor)driver).executeScript(js_TxtFile);
                }
                catch (Exception ex)
                {
                     System.out.println ("Exception when running Javascript: " + ex.toString());
                }

            }
            catch (Exception ex)
            {
            System.out.println(ex.toString());
            }
        }

【讨论】:

  • 我认为这不像您示例的代码那样是加载问题。我以类似的方式加载了 JS 代码,但这似乎无法解决问题。 (在我的加载功能中编辑到原始帖子)。
【解决方案2】:

出于安全原因,Chrome 不允许您加载本地资源。您需要将文件放在驱动程序可以访问的位置(网络驱动器/服务器/repo 等),或者读取文件并将其作为参数传递。

下面是第二个选项的工作方式:不要在脚本元素上指定src,而是创建一个文本节点并将其附加到您的文件文本中,您应该可以开始了:

je.executeScript(
    $"var headID1 = document.getElementsByTagName('head')[0]; "
    + "var newScript1 = document.createElement('script'); "
    + "newScript1.type = 'text/javascript'; "
    + "var code = {fileText}; "
    + "newScript1.appendChild(document.createTextNode(code)); "
    + "headID1.appendChild(newScript1); "
    + "takeScreenShot();"
);

【讨论】:

  • 谢谢,这是我不知道的好信息。我在一个加载函数中将脚本写入字符串,但是,我不确定这种方法是否会削减它,因为我认为 html2canvas.js 库在运行时不会遵循字符串变量规定Java 代码。 (参见 OP 中的 edit3)
  • 我猜由于复杂性/特殊字符的数量庞大,通过网络链接资源加载第一个脚本可能更容易,并以这种方式直接加载包含单个函数的第二个文件?你有什么想法?
  • 也许可以尝试“min”版本并首先删除顶部的评论部分。此外,不要使用不会触发的 document.ready...,因为在 Selenium 注入脚本时页面已经加载。
  • pcalkins 是的,我的意思是删除文档。准备就绪,我已经编辑了我的答案以反映这一点。 Nick Bell,请问您使用这个 js 库的目标是什么?一个更简单的解决方案可能是按照 pcalkins 的建议进行操作并使用内置的屏幕截图工具,并根据需要进行修改。如果您正在处理这个 github 项目并希望让它在 selenium 上运行,我可能会建议您制作一个插件并将其加载到您的驱动程序中。
  • 谢谢,我会注意删除文档就绪行。我还没有研究过 selenium TakesScreenshot 方法,但在这一点上看起来更容易完成。 html2canvas 库我已经开始在网页上获取表格的 png 图像(请参阅 OP 中的 edit4 并添加 html2canvas 代码)。 -- 我还尝试将 html2canvas 库加载为 web url 资源和自定义下载功能(包含在 edit4 中),但是它在运行时会引发 selenium 语法错误。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-07-18
  • 2019-04-03
  • 1970-01-01
  • 2012-03-04
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多