从 Java Selenium 执行 JavaScript 库和函数
我在 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:添加了此代码片段,经过测试,可以运行,但似乎并没有真正执行任何操作。
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 库和一个包含我基于 html2canvas 库编写的单个 JS 函数的第二个/短库并在 selenium 中执行我的函数? -
我猜这是因为我缺乏 JavaScript 知识,而且我忽略了一个明显的错误。任何帮助都值得赞赏。
以下是我使用 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());
}
}
出于安全原因,Chrome 不允许您加载本地资源。您需要将文件放置在驱动程序可访问的位置(网络驱动器/服务器/存储库等),或者读取文件并将其作为参数传递。
第二种方法的工作原理如下:无需在脚本元素上指定
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();"
);