再次使用相同的实例webdriverJS

我对Selenium真的很陌生。 我设法使用下面的nodejs代码打开一个网站

var webdriver = require('selenium-webdriver'); var driver = new webdriver.Builder() .forBrowser('chrome') .build(); console.log(driver); driver.get('https://web.whatsapp.com'); //perform all other operations here. 

https://web.whatsapp.com打开,我手动扫描QR码和login。现在我有不同的JavaScript文件来执行操作,如删除,在web.whatsapp.com内清除聊天等…

现在,如果我得到一些错误,我debugging,当我使用node test.js再次运行脚本,需要另外2分钟来加载页面,并执行我需要的步骤。 我只是想重新打开已经打开的选项卡,并继续我的脚本,而不是新的窗口打开。

编辑第2天:仍在search解决scheme。 我试着下面的代码保存对象,并重用它..这是正确的方法? 虽然我得到一个JSONparsing错误。

 var o = new chrome.Options(); o.addArguments("user-data-dir=/Users/vishnu/Library/Application Support/Google/Chrome/Profile 2"); o.addArguments("disable-infobars"); o.addArguments("--no-first-run"); var driver = new webdriver.Builder().withCapabilities(webdriver.Capabilities.chrome()).setChromeOptions(o).build(); var savefile = fs.writeFile('data.json', JSON.stringify(util.inspect(driver)) , 'utf-8'); var parsedJSON = require('./data.json'); console.log(parsedJSON); 

我花了一些时间和几种不同的方法,但是我设法解决了你的问题,并且允许以相当好的方式开发testing。

因为它不直接回答如何在Selenium中重新使用浏览器会话(使用JavaScript API )的问题,所以我将首先介绍我提出的解决scheme,然后简要讨论我尝试的其他方法。 它可能会给别人一个想法,帮助他们以更好更好的方式解决这个问题。 谁知道。 至less我的尝试将被logging。

build议的解决scheme(testing和工作)

因为我没有设法重新使用浏览器会话(见下文),我想我可以尝试其他的东西。 方法如下。

理念

  1. 在一个文件(例如init.js )中有一个主循环,并在一个单独的文件( test.js )中进行testing。
  2. 主循环打开一个浏览器实例并保持打开状态。 它还暴露了一些CLI,允许运行testing(从test.js ),检查发生的错误,closures浏览器实例并停止主循环。
  3. test.js的testing导出正在由主循环执行的testing函数。 它通过一个驱动程序实例来处理。 这里发生的任何错误都被主循环所捕获。

由于浏览器实例只打开一次,我们只需要进行一次WhatsApp(扫描QR码)validation的手动过程。 之后,运行一个testing会重新加载web.whatsapp.com ,但是它会记住我们已经通过了authentication,因此可以立即运行我们在test.js定义的任何testing。

为了保持主循环的正常运行,捕获我们testing中可能发生的每一个错误是至关重要的。 我不幸的是不得不求助于uncaughtException

履行

这是我提出的上述想法的实现。 如果你想这样做的话,可以做得更好。 我在这里简单地去(希望我pipe理)。

init.js
这是上述想法的主要循环。

 var webdriver = require('selenium-webdriver'), by = webdriver.By, until = webdriver.until, driver = null, prompt = '> ', testPath = 'test.js', lastError = null; function initDriver() { return new Promise((resolve, reject) => { // already opened a browser? done if (driver !== null) { resolve(); return; } // open a new browser, let user scan QR code driver = new webdriver.Builder().forBrowser('chrome').build(); driver.get('https://web.whatsapp.com'); process.stdout.write("Please scan the QR code within 30 seconds...\n"); driver.wait(until.elementLocated(by.className('chat')), 30000) .then(() => resolve()) .catch((timeout) => { process.stdout.write("\b\bTimed out waiting for code to" + " be scanned.\n"); driver.quit(); reject(); }); }); } function recordError(err) { process.stderr.write(err.name + ': ' + err.message + "\n"); lastError = err; // let user know that test failed process.stdout.write("Test failed!\n"); // indicate we are ready to read the next command process.stdout.write(prompt); } process.stdout.write(prompt); process.stdin.setEncoding('utf8'); process.stdin.on('readable', () => { var chunk = process.stdin.read(); if (chunk === null) { // happens on initialization, ignore return; } // do various different things for different commands var line = chunk.trim(), cmds = line.split(/\s+/); switch (cmds[0]) { case 'error': // print last error, when applicable if (lastError !== null) { console.log(lastError); } // indicate we are ready to read the next command process.stdout.write(prompt); break; case 'run': // open a browser if we didn't yet, execute tests initDriver().then(() => { // carefully load test code, report SyntaxError when applicable var file = (cmds.length === 1 ? testPath : cmds[1] + '.js'); try { var test = require('./' + file); } catch (err) { recordError(err); return; } finally { // force node to read the test code again when we // require it in the future delete require.cache[__dirname + '/' + file]; } // carefully execute tests, report errors when applicable test.execute(driver, by, until) .then(() => { // indicate we are ready to read the next command process.stdout.write(prompt); }) .catch(recordError); }).catch(() => process.stdin.destroy()); break; case 'quit': // close browser if it was opened and stop this process if (driver !== null) { driver.quit(); } process.stdin.destroy(); return; } }); // some errors somehow still escape all catches we have... process.on('uncaughtException', recordError); 

test.js
这是来自上述想法的testing。 我写了一些东西只是为了testing主循环和一些WebDriverfunction。 这里几乎任何东西都是可能的。 我已经用promise来使testing执行和主循环一起工作。

 var driver, by, until, timeout = 5000; function waitAndClickElement(selector, index = 0) { driver.wait(until.elementLocated(by.css(selector)), timeout) .then(() => { driver.findElements(by.css(selector)).then((els) => { var element = els[index]; driver.wait(until.elementIsVisible(element), timeout); element.click(); }); }); } exports.execute = function(d, b, u) { // make globally accessible for ease of use driver = d; by = b; until = u; // actual test as a promise return new Promise((resolve, reject) => { // open site driver.get('https://web.whatsapp.com'); // make sure it loads fine driver.wait(until.elementLocated(by.className('chat')), timeout); driver.wait(until.elementIsVisible( driver.findElement(by.className('chat'))), timeout); // open menu waitAndClickElement('.icon.icon-menu'); // click profile link waitAndClickElement('.menu-shortcut', 1); // give profile time to animate // this prevents an error from occurring when we try to click the close // button while it is still being animated (workaround/hack!) driver.sleep(500); // close profile waitAndClickElement('.btn-close-drawer'); driver.sleep(500); // same for hiding profile // click some chat waitAndClickElement('.chat', 3); // let main script know we are done successfully // we do so after all other webdriver promise have resolved by creating // another webdriver promise and hooking into its resolve driver.wait(until.elementLocated(by.className('chat')), timeout) .then(() => resolve()); }); }; 

输出示例

这里是一些输出示例。 run test的第一次调用将打开一个Chrome的实例。 其他调用将使用同一个实例。 发生错误时,可以如图所示进行检查。 执行quit将closures浏览器实例并退出主循环。

 $ node init.js > run test > run test WebDriverError: unknown error: Element <div class="chat">...</div> is not clickable at point (163, 432). Other element would receive the click: <div dir="auto" contenteditable="false" class="input input-text">...</div> (Session info: chrome=57.0.2987.133) (Driver info: chromedriver=2.29.461571 (8a88bbe0775e2a23afda0ceaf2ef7ee74e822cc5),platform=Linux 4.9.0-2-amd64 x86_64) Test failed! > error <prints complete stacktrace> > run test > quit 

您可以通过简单地调用它们在其他文件中运行testing。 假设你有一个文件test-foo.js ,然后在上面的提示中执行run test-foo来运行它。 所有testing将共享相同的Chrome实例。


尝试失败#1:保存和恢复存储

当使用我的开发工具检查页面时,我注意到它似乎使用localStorage 。 可以将其导出为JSON并将其写入文件。 在下一次调用时,可以在重新加载页面之前,将该文件读取 , parsing并写入新的浏览器实例存储。

不幸的是,WhatsApp仍然需要我扫描QR码。 我试图找出我错过了什么(cookies, sessionStorage ,…),但没有pipe理。 WhatsApp可能会在一段时间后注册浏览器断开连接。 或者它使用其他浏览器属性(会话ID?)来识别浏览器。 尽pipe这是纯粹的猜测。


失败的尝试#2:切换会话/窗口

每个通过WebDriver启动的浏览器实例都有一个会话ID。 这个ID可以被检索到,所以我觉得可能会启动一个会话,然后从testing用例连接到它,然后从一个单独的文件运行(你可以看到这是最终解决scheme的前身)。 不幸的是,我一直无法找出设置会话ID的方法。 这实际上可能是一个安全问题,我不确定。 使用WebDriver的人更多的专家也许可以在这里澄清。

我发现有可能检索一个窗口句柄列表并在它们之间切换。 不幸的是,窗口只能在一个会话中共享,而不能跨会话共享。