Nightmare 是来自 Segment 的高级浏览器自动化库。
目标是公开一些模拟用户操作(如goto, type and click)的简单方法,使用一个让每个脚本块感觉同步的 API,而不是深度嵌套的回调。 它最初设计用于跨没有 API 的站点自动执行任务,但最常用于 UI 测试和抓取。
它基于 Electron 的框架,类似于 PhantomJS,但速度大约是其两倍并且更现代
⚠️ 安全警告:我们已经实施了 Electron 概述的许多安全建议以确保您的安全,但仍可能存在未被发现的漏洞允许恶意网站在您的计算机上执行代码。 避免访问不受信任的网站。
🛠 迁移到 3.x:您需要在升级前查看此问题。 我们一直在努力改进 nightmare,同时限制破坏性更改,您很有可能不需要做任何事情。
Niffy 是一个基于 Nightmare 的感知差异工具。 它可以帮助您检测跨 Web 应用程序版本的 UI 更改和错误。
Daydream 是由 @stevenmiller888 构建的补充 chrome 扩展,可在您浏览时为您生成 Nightmare 脚本。
非常感谢@matthewmueller 和@rosshinkley 对 Nightmare 的帮助。
Examples
让我们在 DuckDuckGo 上搜索:
const Nightmare = require('nightmare')
const nightmare = Nightmare({ show: true })
nightmare
.goto('https://duckduckgo.com')
.type('#search_form_input_homepage', 'github nightmare')
.click('#search_button_homepage')
.wait('#r1-0 a.result__a')
.evaluate(() => document.querySelector('#r1-0 a.result__a').href)
.end()
.then(console.log)
.catch(error => {
console.error('Search failed:', error)
})
您可以使用以下命令运行它:
npm install --save nightmare
node example.js
或者,让我们运行一些 mocha 测试:
const Nightmare = require('nightmare')
const chai = require('chai')
const expect = chai.expect
describe('test duckduckgo search results', () => {
it('should find the nightmare github link first', function(done) {
this.timeout('10s')
const nightmare = Nightmare()
nightmare
.goto('https://duckduckgo.com')
.type('#search_form_input_homepage', 'github nightmare')
.click('#search_button_homepage')
.wait('#links .result__a')
.evaluate(() => document.querySelector('#links .result__a').href)
.end()
.then(link => {
expect(link).to.equal('https://github.com/segmentio/nightmare')
done()
})
})
})
您可以在此处查看测试中每个函数的示例:https://github.com/segmentio/nightmare/blob/master/test/index.js
要开始使用 UI 测试,请查看此快速入门指南:https://segment.com/blog/ui-testing-with-nightmare/
安装依赖项
npm install
运行 mocha 测试
npm test
Node版本
Nightmare 旨在 NodeJS 4.x 或更高版本上运行。
API
Nightmare(options)
创建一个可以在 Web 中导航的新实例。 此处记录了可用选项以及以下nightmare-specific选项:https://github.com/electron/electron/blob/main/docs/api/browser-window.md#new-browserwindowoptions
waitTimeout (default: 30s)
如果 .wait() 在设定的时间范围内没有返回 true,则抛出异常。
const nightmare = Nightmare({
waitTimeout: 1000 // in ms
})
gotoTimeout (default: 30s)
如果 .goto() 未在设定的时间范围内完成加载,则会引发异常。 请注意,尽管 goto 通常会等待页面上的所有资源加载完毕,但只有在 DOM 本身尚未加载时才会引发超时异常。
const nightmare = Nightmare({
gotoTimeout: 1000 // in ms
})
loadTimeout (default: infinite)
如果由操作(例如,.click())引起的页面转换未在设定的时间范围内完成,则强制 Nightmare 继续前进。 如果 loadTimeout 比 gotoTimeout 短,则 gotoTimeout 抛出的异常将被抑制。
const nightmare = Nightmare({
loadTimeout: 1000 // in ms
})
executionTimeout (default: 30s)
等待 .evaluate() 语句完成的最长时间。
const nightmare = Nightmare({
executionTimeout: 1000 // in ms
})
paths
Electron 知道的默认系统路径。 以下是可用路径的列表:https://github.com/atom/electron/blob/master/docs/api/app.md#appgetpathname
您可以通过执行以下操作在 Nightmare 中覆盖它们:
const nightmare = Nightmare({
paths: {
userData: '/user/data'
}
})
switches
Chrome 浏览器使用的命令行开关也受 Electron 支持。 以下是受支持的 Chrome 命令行开关列表: https://github.com/atom/electron/blob/master/docs/api/chrome-command-line-switches.md
const nightmare = Nightmare({
switches: {
'proxy-server': '1.2.3.4:5678',
'ignore-certificate-errors': true
}
})
electronPath
预构建 Electron 二进制文件的路径。 这对于在不同版本的 Electron 上进行测试很有用。 请注意,Nightmare 仅支持此软件包所依赖的版本。 使用此选项的风险由您自行承担。
dock (OS X)
一个布尔值,可选择在 Dock 中显示 Electron 图标(默认为 false)这对于测试目的很有用。
const nightmare = Nightmare({
dock: true
})
openDevTools
可选择使用 true 在 Electron 窗口中显示 DevTools,或使用包含模式: 'detach' 的对象哈希在单独的窗口中显示。 散列被传递给 content.openDevTools() 进行处理。 这对于测试目的也很有用。 请注意,仅当 show 设置为 true 时才会使用此选项。
const nightmare = Nightmare({
openDevTools: {
mode: 'detach'
},
show: true
})
typeInterval (default: 100ms)
使用 .type() 时在两次击键之间等待多长时间。
const nightmare = Nightmare({
typeInterval: 20
})
pollInterval (default: 250ms)
检查 .wait() 条件成功的间隔时间。
const nightmare = Nightmare({
pollInterval: 50 //in ms
})
maxAuthRetries (default: 3)
定义使用 .authenticate() 设置时重试身份验证的次数。
const nightmare = Nightmare({
maxAuthRetries: 3
})
certificateSubjectName
用于确定 Electron 选择的客户端证书的字符串。 如果设置了此选项,select-client-certificate 事件将设置为循环遍历 certificateList 并找到与 Electron 证书对象上的主题名称匹配的第一个证书。
const nightmare = Nightmare({
certificateSubjectName: 'tester'
})
.engineVersions()
获取 Electron 和 Chromium 的版本。
.useragent(useragent)
设置 Electron 使用的 Useragent。
.authentication(user, password)
设置用于使用基本身份验证访问网页的用户和密码。 请务必在调用 .goto(url) 之前设置它。
.end()
完成任何队列操作,断开和关闭 Electron 进程。 请注意,如果您使用 Promise,则必须在 .end() 之后调用 .then() 以运行 .end() 任务。 另请注意,如果使用 .end() 回调,则 .end() 调用等效于调用 .end() 后跟 .then(fn)。
考虑:
nightmare
.goto(someUrl)
.end(() => 'some value')
//prints "some value"
.then(console.log)
.halt(error, done)
清除所有排队的操作,杀死 Electron 进程,并将错误消息或'Nightmare Halted'传递给未解决的承诺。 Done 将在进程退出后调用。
Interact with the Page
.goto(url[, headers])
在 url 加载页面。 可选地,可以提供标头哈希来设置 goto 请求的标头。
当页面加载成功时,goto 返回一个包含页面加载元数据的对象,包括:
url
: 加载的URLcode
: HTTP 状态代码(例如 200、404、500)method
: 使用的 HTTP 方法(例如“GET”、“POST”)referrer
: 窗口在此加载之前显示的页面,如果这是第一个页面加载,则为空字符串。headers
: 表示请求的响应标头的对象,如 {header1-name: header1-value, header2-name: header2-value}
如果页面加载失败,错误将是一个具有以下属性的对象:
message
: 描述错误类型的字符串code
: 描述出了什么问题的底层错误代码。 请注意,这不是 HTTP 状态代码。 有关可能的值,请参阅 https://code.google.com/p/chromium/codesearch#chromium/src/net/base/net_error_list.hdetails
: 包含有关错误的其他详细信息的字符串。 这可能是 null 或空字符串。url
: 加载失败的 URL
请注意,来自服务器的任何有效响应都被视为“成功”。 这意味着诸如 404“未找到”错误之类的东西是 goto 的成功结果。 只有那些会导致浏览器窗口中不显示页面的事情,例如在给定地址上没有服务器响应、服务器在响应中间挂断或 URL 无效,才是错误。
您还可以通过在 Nightmare 构造函数上设置 gotoTimeout 选项来调整 goto 在超时之前等待的时间。
.back()
返回上一页。
.forward()
进入下一页。
.refresh()
刷新当前页面。
.click(selector)
点击一次选择器元素。
.mousedown(selector)
鼠标向下选择器元素一次。
.mouseup(selector)
鼠标向上选择器元素一次。
.mouseover(selector)
将鼠标悬停在选择器元素上一次。
.mouseout(selector)
鼠标移开选择器元素一次。
.type(selector[, text])
输入提供给选择器元素的文本。 为文本提供的空值或假值将清除选择器的值。
.type()
模仿用户在文本框中输入并发出正确的键盘事件。
也可以使用带有 .type() 的 Unicode 值触发按键。 例如,如果您想触发回车键,您可以编写 .type('body', '\u000d')。
如果您不需要键盘事件,请考虑改用 .insert() ,因为它会更快、更可靠。
.insert(selector[, text])
与 .type() 类似,.insert() 将提供的文本输入到选择器元素中。 为文本提供的空值或假值将清除选择器的值。
.insert() 比 .type() 快,但不会触发键盘事件。
.check(selector)
检查选择器复选框元素。
.uncheck(selector)
取消选中选择器复选框元素。
.select(selector, option)
将选择器下拉元素更改为具有 [value=option] 属性的选项
.scrollTo(top, left)
将页面滚动到所需位置。 top 和 left 始终相对于文档的左上角。
.viewport(width, height)
设置视口大小。
.inject(type, file)
将本地文件注入当前页面。 文件类型必须是 js 或 css。
.evaluate(fn[, arg1, arg2,...])
使用 arg1、arg2、...调用页面上的 fn。所有 args 都是可选的。 完成后,它返回 fn 的返回值。 用于从页面中提取信息。 下面是一个例子:
const selector = 'h1'
nightmare
.evaluate(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(text => {
// ...
})
作为 evaluate() 的一部分,支持错误优先回调。 如果传递的参数比被评估函数预期的参数少一个,则评估将作为最后一个参数传递给函数。 例如:
const selector = 'h1'
nightmare
.evaluate((selector, done) => {
// now we're executing inside the browser scope.
setTimeout(
() => done(null, document.querySelector(selector).innerText),
2000
)
}, selector)
.then(text => {
// ...
})
请注意,回调仅支持一个值参数(例如 function(err, value))。最终,回调将被包装在本机 Promise 中,并且只能解析单个值。
Promise 也作为evaluate() 的一部分得到支持。 如果函数的返回值具有 then 成员,则 .evaluate() 假定它正在等待承诺。 例如:
const selector = 'h1';
nightmare
.evaluate((selector) => (
new Promise((resolve, reject) => {
setTimeout(() => resolve(document.querySelector(selector).innerText), 2000);
)}, selector)
)
.then((text) => {
// ...
})
.wait(ms)
等待 ms 毫秒,例如 .等待(5000)。
.wait(selector)
等到元素选择器出现,例如 .wait('#pay-button')。
.wait(fn[, arg1, arg2,...])
等待直到在页面上使用 arg1、arg2、... 评估的 fn 返回 true。 所有参数都是可选的。 有关用法,请参阅 .evaluate()。
.header(header, value)
为所有 HTTP 请求添加标头覆盖。 如果未定义标头,标头覆盖将被重置。