如何在不崩溃节点js

时间:2017-06-17 21:57:53

标签: javascript node.js selenium selenium-webdriver webdriver

是否可以在不停止节点的情况下停止selenium webdriver?

我有以下问题: 我尝试创建一个API工具,在获取get请求时执行一些Web自动化。所以我基本上在通过Express获取/启动的get请求上运行selenium webdriver。我希望该工具检查不同的元素,当它以某种方式失败时,我希望它停止selenium而不是节点实例(因为可以发送新的get请求)。

到目前为止,这是我的代码:

"use strict";

const webdriver = require('selenium-webdriver'),
      Express = require('express'),
      By = webdriver.By,
      until = webdriver.until,
      Keys = webdriver.Key,
      app = new Express();

app.get("/automate", (req, res) => {
  start(res);
});

function start(res) {
    const driver = new webdriver.Builder().forBrowser('chrome').build();

    driver.get('https://www.google.com/');

    // # --- foo --- #
    let errMessage = {pos: "FOO", message: "Ooops friendly robot has some troubles!"}
    checkStep("#foo", errMessage);

    driver.findElement(By.id("foo"))
          .sendKeys("fooz");

    // # --- bar --- #
    errMessage = {pos: "BAR", message: "Ooops friendly robot has some troubles!"}
    checkStep("#bar", errMessage);

    driver.findElement(By.id("bar"))
          .sendKeys("baz");

    // etc…

    function checkStep(selector, errMessage) {
        driver.findElement(By.css(selector))
          .then(() => {
            console.log(`${selector} => found`);
          })
          .catch(err => {
            console.log(`Error: ${err}`);
            res.send(errMessage);
            driver.quit();
          });
    }
}

app.get("*", (req, res) => {
  res.send("Hello World");
});

// start the server
const port = process.env.PORT || 3000;
const env = process.env.NODE_ENV || 'production';
app.listen(port, err => {
  if (err) { return console.error(err); }
  console.info(`Server running on http://localhost:${port} [${env}]`);
});
到目前为止,它实际上是在工作时,当selenium找不到元素时,API的响应是正确的。在Selenium我回来了:

{
  "pos": "FOO",
  "message": "Ooops friendly robot has some troubles!"
}

到目前为止一切都很好。 不幸的是, BUT 停止selenium也阻止Node运行。

我得到的错误如下:

throw error;
        ^

WebDriverError: no such session
  (Driver info: chromedriver=2.30.477690 (c53f4ad87510ee97b5c3425a14c0e79780cdf262),platform=Ma
c OS X 10.12.5 x86_64)
    at WebDriverError

请帮助,谢谢!

ps:我没有使用webdriverio,因为你可以看到我使用这个包:https://www.npmjs.com/package/selenium-webdriver

1 个答案:

答案 0 :(得分:1)

好的,我有这个工作。这是一个有点困难的解决方案,但它有效:

使用子进程

基本上,每次应用程序向get收到/automate请求时,它现在将在运行selenium脚本的节点中创建子进程(子进程有点像使用另一个线程。是一个非常好的tutorial on child processes):

index.js

"use strict";

const Express = require('express');
const { spawn } = require('child_process');
const data = require('./data.json');

const app = new Express();

app.get("/automate", (req, res) => {

  const child = spawn(
    process.execPath,
    [`${__dirname}/test.js`, JSON.stringify(data)],
    { stdio: ['inherit', 'inherit', 'inherit', 'pipe'] }
  );

  child.stdio[3].on('data', data => {
    const response = JSON.parse(data.toString());
    res.send(response);
    console.log(response);
    child.kill();
  });

});

app.get("*", (req, res) => {
  res.send("Hello World");
});

const port = process.env.PORT || 3000;
const env = process.env.NODE_ENV || 'production';
app.listen(port, err => {
  if (err) { return console.error(err); }
  console.info(`Server running on http://localhost:${port} [${env}]`);
});

test.js

"use strict";

// hook with argument 3, that is "pipe" from parent
const Net = require('net'),
      pipe = new Net.Socket({ fd: 3 });

const data = JSON.parse(process.argv[2]);

const webdriver = require('selenium-webdriver'),
      By = webdriver.By,
      until = webdriver.until,
      Keys = webdriver.Key;

function start() {
  const driver = new webdriver.Builder().forBrowser('chrome').build();

  driver.get('https://www.google.com/');

  // # --- foo --- #
  let errMessage = {pos: "lst-ib", message: "Ooops friendly robot has some troubles!"}
  checkStep("#lst-ib")
    .sendKeys("fooz");

  driver.get('https://www.facebook.com/');
  driver.get('https://www.google.com/');
  driver.get('https://www.facebook.com/');

  // # --- bar --- #
  errMessage = {pos: "BAR", message: "Ooops friendly robot has some troubles!"}
  checkStep("#bar")
    .sendKeys("baz");

  function checkStep(selector) {
    driver.findElement(By.css(selector))
    .then(() => {
      console.log(`${selector} => found`);
    })
    .catch(err => {
      console.log(`${selector} => not found`);
      publish(errMessage);
      driver.quit();
    });
  }
}

function publish(message) {
  pipe.write(JSON.stringify(message));
}

start();

它的工作方式就像一个魅力:在每个请求打开一个新的子进程并杀死该子进程,如果它发送一些消息,同时还响应消息给客户端。像这样,您可以轻松地同时拥有多个selenium实例。

欢迎你。

ps:如果你讨厌所有这些来自Selenium webdriver-sync的asyncron东西似乎是一个不错的选择。它基本上将selenium代码包装为syncon而不是asyncron。就像我一样,我可以使用try {} catch {}driver.quit();而不会出现任何错误。 (但这有一个缺点:它实际上阻止了你的其他nodejs代码。)