Selenium (Node.js) 截图速度太慢

Selenium (Node.js) screenshots are too slow

我正在使用 Selenium + Mocha(在 Node.js 中)在站点上进行一些 UI 测试。每次测试后,我想截屏以便 review/share 可以看到结果。

但是,在完成基本的 UI 更改后(即插入文本并点击提交),有一个 AJAX 调用 + JavaScript 动画会在之后触发,我想成为我的屏幕截图的一部分。

我得到的屏幕截图似乎是在下一步已经开始之后拍摄的。 (因此,第一个测试运行 > 触发屏幕截图 > 第二个测试开始 > 实际捕获屏幕截图)

我试过 .sleep() 和超时 -- 但我的使用可能是错误的。

function takeScreenshot(name='out'){
      driver.takeScreenshot().then(function(data){
        var base64Data = data.replace(/^data:image\/png;base64,/,"")
        fs.writeFile(`screenshots/${name}.png`, base64Data, 'base64', function(err) {
          if(err) console.log(err);
        });
      });
}

describe('UI Tests', async function() {

  before(async function() {
    driver = await new Builder().forBrowser('chrome').build();
    await driver.get('http://localhost:8000').then( async function(){ await getAssets(); });
    /* search | submit | reset are declared in getAssets() */
  });

  after(async function() {
    driver.quit();
  });

  afterEach(async function(){
    step++;
    driver.sleep(3000).then(
      takeScreenshot(step) 
    );
  });

  describe('User Types/Modifies Input and Submits Query', async function() {

    it('type in search + click submit', async function(){
        search.sendKeys("Puppies and Kittens");
        submit.click();
    });

    it('click the X icon to clear search', async function(){
        reset.click();
    });

  });

});

在上面的代码片段中,第一个测试 ('type in search + click submit') 的屏幕截图将显示输入字段已从第二个测试中清除。

发送到 selenium 的命令是异步的,这意味着一个函数将工作安排到 运行 在未来的某个时间点,但立即 return 因此代码继续进行。

在 selenium-webdrivers 的情况下,函数 return a "promise" 将在将来的某个时间点解析。 Mocha 可以使用 promise 来等待结果。

Promises 和 Mocha

Mocha 使用测试的 return 值来确定它是否应该等待承诺解决。您可以使用 await.then.

控制承诺流程

如果正在使用 Promise A+ 代码,那么测试中将始终有 return someFormOfPromise

it(waits, function(){
  return Promise.resolve(true)
    .then(res => expect(res).to.be.true)
})

如果 async is being used, that guarantees a promise is returned but you must await 任何异步或测试将继续

it(waits, async function(){
  let res = await Promise.resolve(true)
  expect(res).to.be.true
})

回调类似,但它们使用 done 函数来指示测试继续进行。

it(waits, function(done){
  someTrueCallback((err, res) => {
    if (err) return done(err)
    expect(res).to.be.true
    done()
  }) 
})

发布的代码中混合了很多Promise A+async,甚至还抛出了一个回调。选择一种方式并坚持下去在可能的情况下,我将使用 async/await.

回调到 Promise

任何异步回调都可以转换为 promise,因此从 fs.writeFile 回调函数开始,将其转换为 promise:

function writeFile(path, data, format){
  return new Promise((resolve, reject)=>{
    fs.writeFile(path, data, format, function(err){
      if (err) return reject(err)
      resolve(true)
    })
  })
}

幸运的是,这是非常标准的,Node.js 在 util.promisify 中有一个帮助者,所以帮助者可以成为:

const writeFile = util.promisify(fs.writeFile)

function async takeScreenshot(name='out'){
    let data = await driver.takeScreenshot()
    var base64Data = data.replace(/^data:image\/png;base64,/,"")
    return await writeFile(`screenshots/${name}.png`, base64Data, 'base64')
}

测试

await 可用时,无需使用 .then

每当调用承诺的函数 return 时,如果您想使用已解析的值,您可能需要 awaitlet res = await...

describe('UI Tests', function() {

  before(async function() {
    driver = await new Builder().forBrowser('chrome').build();
    await driver.get('http://localhost:8000')
    await getAssets()
    /* search | submit | reset are declared in getAssets() */
  });

  after(async function() {
    await driver.quit();
  });

  afterEach(async function(){
    step++;
    await takeScreenshot(step) 
  });

  describe('User Types/Modifies Input and Submits Query', function() {

    it('type in search + click submit', async function(){
        await search.sendKeys("Puppies and Kittens");
        await submit.click();
    });

    it('click the X icon to clear search', async function(){
        await reset.click();
    });

  });

});

附带说明一下,这些测试实际上并未检查任何内容。通常是像 chai would be used to ensure things are the way you expect 这样的断言库。