再次使用相同的实例webdriverJS

时间:2017-04-11 18:09:08

标签: javascript node.js selenium selenium-webdriver

我对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码并登录。现在我有不同的javascript文件来执行删除等操作,清除web.whatsapp.com内的聊天等...

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

编辑第2天:仍在搜索解决方案。我尝试下面的代码来保存对象并重用它..这是正确的方法吗?我收到了JSON解析错误。

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);

1 个答案:

答案 0 :(得分:1)

我花了一些时间和几种不同的方法,但我设法解决了一些问题,我认为可以解决你的问题并允许以相当不错的方式开发测试。

因为它没有直接回答如何在Selenium中重复使用浏览器会话的问题(使用他们的JavaScript API),我将首先介绍我提出的解决方案,然后简要讨论其他方法我试过了。它可能会给别人一个想法并帮助他们以更好/更好的方式解决这个问题。谁知道。至少我的尝试将被记录下来。

提议的解决方案(已测试并正常工作)

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

  1. 在一个文件中有一个主循环(比如init.js)并在单独的文件中进行测试(test.js)。
  2. 主循环打开浏览器实例并保持打开状态。它还公开了某种类型的CLI,允许用户运行测试(来自test.js),检查错误,并关闭浏览器实例并停止主循环。
  3. test.js中的测试导出主循环正在执行的测试函数。它传递一个驱动程序实例来使用。此处发生的任何错误都会被主循环捕获。
  4. 因为浏览器实例只打开一次,所以我们必须只使用WhatsApp(扫描QR码)进行一次身份验证的手动过程。之后,运行测试将重新加载web.whatsapp.com,但它会记住我们已经过身份验证,因此可以立即运行我们在test.js中定义的任何测试。

    为了使主循环保持活动,我们必须捕获可能在测试中发生的每个错误。不幸的是,我不得不求助于uncaughtException

    实施

    这是我想出的上述想法的实现。如果你想这样做,可以让这个更加漂亮。我在这里很简单(希望我能管理)。

    <强> 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
    这是上述想法的考验。我写了一些东西只是为了测试主循环和一些WebDriver功能。这里几乎可以做任何事情。我已经使用promises使测试执行与主循环很好地协同工作。

    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将关闭浏览器实例并退出主循环。

    $ 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
    

    只需调用它们即可在其他文件中运行测试。假设您有一个文件test-foo.js,然后在上面的提示中执行run test-foo来运行它。所有测试都将共享相同的Chrome实例。

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

    使用我的开发工具检查页面时,我注意到它似乎使用了localStorage。可以export将其作为JSON,将write作为文件。在下次调用时,此文件可以是readparsed并在重新加载页面之前写入新的浏览器实例存储。

    不幸的是,WhatsApp仍然要求我扫描QR码。我试图弄清楚我错过了什么(cookies,sessionStorage,...),但没有管理。一段时间过后,WhatsApp可能会将浏览器注册为断开连接。或者它使用其他浏览器属性(会话ID?)来识别浏览器。这是我个人的纯粹推测。

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

    通过WebDriver启动的每个浏览器实例都有一个会话ID。可以检索此ID,因此我认为可以启动会话,然后从测试用例连接到它,然后从单独的文件运行(您可以看到这是最终解决方案的前身)。不幸的是,我无法找到设置会话ID的方法。这可能实际上是一个安全问题,我不确定。更多使用WebDriver的人可能会在这里澄清。

    我确实发现可以retrieve a list of window handles并在them之间切换。不幸的是,Windows只在一个会话中共享,而不是在会话中共享。