nightmare 截图问题
发布于 6 年前 作者 songabc 4345 次浏览 来自 问答

准备用 nightmare 写自动化测试. 遇到了一个解决不了的问题, 多方查找无果,尝试来 node 社区咨询下: 一个待测试的页面的 window 对象挂了一个方法,方法被调用会改变页面,并返回一个对象. 需求是, 用 nightmare 调用一次这个方法,然后截一次图. 我的实现代码如下:

var Nightmare = require('nightmare'),
  nightmare = Nightmare();
  
nightmare.goto('http://127.0.0.1/demo');

nightmare
.wait(5000)
.run(function(){
        nightmare.evaluate(function(){
            return window.a.playNextFrame({toggle: true, pId: null})
        })
        .then(function(result){
            console.log(result)
            nightmare.screenshot('/test.png')
            .end()
        })
})

但是代码运行结果是: Screen Shot 2016-09-26 at 20.48.49.png 只输出了 result, 并没有生成截图文件 但是按照这种方式,却可以正常生成图片,并打印标题:

nightmare.goto('http://127.0.0.1/demo')
.wait(5000)
.screenshot('/test.png')
.evaluate(function(){
    return document.title;
  })
.end()
.then(function(title){
    console.log(title);
  })

请教各位,问题出在哪里? 先谢过了

6 回复

你的第一段代码, nightmare.goto(‘http://127.0.0.1/demo’) 应该要链式调用吧,还有要想执行代码后截图得用 inject,像下面这样

const Nightmare = require('nightmare');
const nightmare = Nightmare();

nightmare.goto('http://127.0.0.1/demo')
	.wait(5000)
	.inject('js', 'play.js')
	.screenshot('/test.png')
	.evaluate(function () {
		return globalResult;
	})
	.end()
	.then(function (result) {
		console.log(result);
	});

play.js

globalResult = window.a.playNextFrame({toggle: true, pId: null})

@IchiNiNiIchi 谢谢回复. 我在官方的api 文档里面看到这段 demo:

var selector = 'h1';
nightmare
  .evaluate(function (selector) {
    // now we're executing inside the browser scope.
    return document.querySelector(selector).innerText;
   }, selector) // <-- that's how you pass parameters from Node scope to browser scope
  .then(function(text) {
    // ...
  })

所以我觉得 evaluate 沙盘里面应该可以直接调用预先挂到全局变量上的方法. 我原来的代码的确也调用到了.

@songabc 刚刚写了个小脚本试了一下,你说的对。另外nightmare 的 end 之后一定得调用 then 才会有截图。下面用百度的首页做例子,你可以照着自己改一下

const Nightmare = require('nightmare');
var nightmare = Nightmare();

nightmare.goto('https://www.baidu.com');
nightmare.wait(5000)
  .evaluate(function () {
	document.getElementById('su').value = 'Boom';
	return 'Boom';
  })
  .then(function (result) {
	console.log(result);
	nightmare.screenshot('fun.png').end().then();
  });

另外,如果不关心 evaluate 返回的值的话,可以直接 evaluate 之后直接 screenshot

const Nightmare = require('nightmare');
var nightmare = Nightmare();

nightmare.goto('https://www.baidu.com')
  .wait(5000)
  .evaluate(function () {
	document.getElementById('su').value = 'Boom';
	return 'Boom';
  })
  .screenshot('fun.png')
  .end()
  .then();

@IchiNiNiIchi 的确是你说的那样,需要在 end() 后面添加 then() 才能截图( 不明白为什么) 然后,如果我有进一步需求,需要根据 evaluate 返回的值判断是否调用. 我的代码是这样的:

nightmare.goto('http://127.0.0.1/demo')
.wait(5000)
.evaluate(function(){
	return window.test()
 })
.then(function(result){
  if(result){
  	nightmare.screenshot('/test.png')
  }
})

结果是,并没有生成截图但是result 有值.

@songabc 应该是之前调用的方法都是把要执行的东西放在一个容器里面,调用了 then 之后才真正地执行。 其实你又忘了加 then 了。

nightmare
  .goto('http://127.0.0.1/demo')
  .wait(5000)
  .evaluate(function(){
	return window.test()
  })
  .then(function(result){
	  if(result){
		nightmare.screenshot('/test.png').end().then(); // SHOULD BE FIXED
	}
  });

@songabc 这是因为每一个 nightmare 实例都有一个操作队列,而这个操作队列保存着 nightmare 的一系列操作。而 nightmare 的每一个链式调用只是将操作保存到队列里面,并没有立刻执行操作。

例如下面这段代码:

nightmare
    .goto('https://github.com')
    .wait(1000)
    .evaluate(function(){})
    .end()

如果仅仅执行这段代码,在打开浏览窗口之后不会有任何操作结果。这是因为,这段代码是将两个操作 goto 和 wait 放到了操作队列中,但是并没有执行这两个操作。而 end 的作用是在执行完队列中所有的操作之后,关闭 electron 进程即浏览窗口会被关闭(end 也是一个操作)。由于没有队列里面的操作没有执行,所以浏览窗口也会一直打开。

回到顶部