Introduction of Nightmare.js
What is Nightmare.js
A high-level browser library
More about Nightmare.js
Benchmarks
Nightmare running via Phantom and Electron
Getting Started
Installation
Install NodeJS 4.x or higher and NPM first
npm install nightmare --save
Run the example
// example1.test.js
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)
})
Result
Popup a browser within new window
Result
Show the result in the console
Combination with Javascript Test Framework
Installation
npm install mocha --save
npm install mocha -g
npm install Chai --save
Chai.js - BDD / TDD assertion library
Run test with mocha
// example2.test.js
const Nightmare = require('nightmare')
const nightmare = Nightmare({ show: true })
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')
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()
})
})
})
Run test with mocha
// example2.test.js
const Nightmare = require('nightmare')
const nightmare = Nightmare({ show: true })
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()
})
})
})
*/
Import library
Popup debug window
Run test with mocha
// example2.test.js
/*
const Nightmare = require('nightmare')
const nightmare = Nightmare({ show: true })
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')
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()
})
})
})
Test Case
Result
Result
Total Execution time : 9s
Mocha API Timeout Setting
// example2.test.js
/*
const Nightmare = require('nightmare')
const nightmare = Nightmare({ show: true })
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')
/*
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()
})
})
})
*/
Mocha test case
default timeout: 10s
Mocha API Timeout Setting
// example2.test.js
/*
const Nightmare = require('nightmare')
const nightmare = Nightmare({ show: true })
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')
nightmare.wait(5000)
/*
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()
})
})
})
*/
We add more 5s execution time
Mocha API Timeout Setting
Mocha API Timeout Setting
// example2.test.js
/*
const Nightmare = require('nightmare')
const nightmare = Nightmare({ show: true })
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('15s')
/*
nightmare.wait(5000)
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()
})
})
})
*/
We modify timeout setting to 15s
Mocha API Timeout Setting
Nightmare API
Nightmare API List
API List
Set up an instance
Basic
Advanced
waitTimeout(default: 30s)
gotoTimeout (default: 30s)
loadTimeout (default: infinite)
executionTimeout (default: 30s)
paths
switches
dock (OS X)
penDevTools
typeInterval (default: 100ms)
pollInterval (default: 250ms)
maxAuthRetries (default: 3)
API List
Interact with the page
Extract from the page
Http
Mouse
Checkbox
Select
Others
API List - Advanced
Cookie
Proxies
Promises
Extending Nightmare
API List - goto
// example1.test.js
// const Nightmare = require('nightmare')
// const nightmare = Nightmare({ show: true })
/*
@ name goto(url, [headers])
@ return {
url: The URL that was loaded
code: The HTTP status code (e.g. 200, 404, 500)
method: The HTTP method used (e.g. "GET", "POST")
referrer: The page that the window was displaying prior to this load or an empty string if this is the first page load.
headers: An object representing the response headers for the request as in
{header1-name: header1-value, header2-name: header2-value}
}
*/
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)
})
@ goto(url, [headers])
- Load the page at url
@ return successful {
}
API List - goto
// example1.test.js
// const Nightmare = require('nightmare')
// const nightmare = Nightmare({ show: true })
/*
@ name goto(url, [headers])
@ return {
url: The URL that was loaded
code: The HTTP status code (e.g. 200, 404, 500)
method: The HTTP method used (e.g. "GET", "POST")
referrer: The page that the window was displaying prior to this load or an empty string if this is the first page load.
headers: An object representing the response headers for the request as in
{header1-name: header1-value, header2-name: header2-value}
}
*/
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)
})
@ goto(url, [headers])
- Load the page at url
@ return failed {
}
API List - type
// example1.test.js
// const Nightmare = require('nightmare')
// const nightmare = Nightmare({ show: true })
/*
@ name goto(url, [headers])
@ return {
url: The URL that was loaded
code: The HTTP status code (e.g. 200, 404, 500)
method: The HTTP method used (e.g. "GET", "POST")
referrer: The page that the window was displaying prior to this load or an empty string if this is the first page load.
headers: An object representing the response headers for the request as in
{header1-name: header1-value, header2-name: header2-value}
}
*/
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)
})
@ type(selector [, text])
- Simulate user typing text in selector element
- Empty or falsey values provided for text
API List - click
// example1.test.js
// const Nightmare = require('nightmare')
// const nightmare = Nightmare({ show: true })
/*
@ name goto(url, [headers])
@ return {
url: The URL that was loaded
code: The HTTP status code (e.g. 200, 404, 500)
method: The HTTP method used (e.g. "GET", "POST")
referrer: The page that the window was displaying prior to this load or an empty string if this is the first page load.
headers: An object representing the response headers for the request as in
{header1-name: header1-value, header2-name: header2-value}
}
*/
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)
})
@ click(selector)
- Clicks the selector element once.
API List - wait
// example1.test.js
// const Nightmare = require('nightmare')
// const nightmare = Nightmare({ show: true })
/*
@ name goto(url, [headers])
@ return {
url: The URL that was loaded
code: The HTTP status code (e.g. 200, 404, 500)
method: The HTTP method used (e.g. "GET", "POST")
referrer: The page that the window was displaying prior to this load or an empty string if this is the first page load.
headers: An object representing the response headers for the request as in
{header1-name: header1-value, header2-name: header2-value}
}
*/
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)
})
@ wait (ms|selector|fn[, arg1, arg2,...])
- ms: Waits for ms milliseconds e.g. wait(5000).
- selector: Waits until the element selector is present
- Waits until the fn evaluated on the page with arg1, arg2,... returns true.
API List - end
// example1.test.js
// const Nightmare = require('nightmare')
// const nightmare = Nightmare({ show: true })
/*
@ name goto(url, [headers])
@ return {
url: The URL that was loaded
code: The HTTP status code (e.g. 200, 404, 500)
method: The HTTP method used (e.g. "GET", "POST")
referrer: The page that the window was displaying prior to this load or an empty string if this is the first page load.
headers: An object representing the response headers for the request as in
{header1-name: header1-value, header2-name: header2-value}
}
*/
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)
})
@ end(fn)
- Completes any queue operations, disconnect and close the electron process.
API List - end
// example1.test.js
// const Nightmare = require('nightmare')
// const nightmare = Nightmare({ show: true })
/*
@ name goto(url, [headers])
@ return {
url: The URL that was loaded
code: The HTTP status code (e.g. 200, 404, 500)
method: The HTTP method used (e.g. "GET", "POST")
referrer: The page that the window was displaying prior to this load or an empty string if this is the first page load.
headers: An object representing the response headers for the request as in
{header1-name: header1-value, header2-name: header2-value}
}
*/
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)
})
.then() must be called after .end() to run the .end() task.
API List - end
// example1.test.js
// const Nightmare = require('nightmare')
// const nightmare = Nightmare({ show: true })
/*
@ name goto(url, [headers])
@ return {
url: The URL that was loaded
code: The HTTP status code (e.g. 200, 404, 500)
method: The HTTP method used (e.g. "GET", "POST")
referrer: The page that the window was displaying prior to this load or an empty string if this is the first page load.
headers: An object representing the response headers for the request as in
{header1-name: header1-value, header2-name: header2-value}
}
*/
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(rs => {
console.log('end rs', rs) // rs: segmentio/nightmare
return rs + '123'
})
.then(console.log) // output: segmentio/nightmare123
// .catch(error => {
// console.error('Search failed:', error)
})
if using an .end() callback, the .end() call is equivalent to calling .end() followed by .then(fn)
API List - evaluate
// example1.test.js
// const Nightmare = require('nightmare')
// const nightmare = Nightmare({ show: true })
/*
@ name goto(url, [headers])
@ return {
url: The URL that was loaded
code: The HTTP status code (e.g. 200, 404, 500)
method: The HTTP method used (e.g. "GET", "POST")
referrer: The page that the window was displaying prior to this load or an empty string if this is the first page load.
headers: An object representing the response headers for the request as in
{header1-name: header1-value, header2-name: header2-value}
}
*/
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)
})
@ evaluate(fn[, arg1, arg2,...]
- Useful for extracting information from the page.
- fn creates a the browser env, all web apis can be used in the function.
- Like promise, use .then() to get the value return from fn, and use .catch() to get error message.
API List - evaluate
/* example3.test.js */
// Get the text content of H1 element
/*
const Nightmare = require('nightmare')
const nightmare = Nightmare({ show: true })
*/
const selector2 = 'h2'
/*
const selector = 'h1'
nightmare
.goto('https://github.com/segmentio/nightmare')
*/
.evaluate(function (selector) {
// now we're executing inside the browser scope.
// return document.querySelector(selector).innerText
return selector2
}, selector) // <-- that's how you pass parameters from Node scope to browser scope
// .end()
.then(rs => {
console.log(rs) // undefined
}).catch(error => {
console.error('Search failed:', error)
})
Search failed: { ReferenceError: selector2 is not defined ... }
Bad case:
selector2 isn't defined in this scope
API List - evaluate
/* example3.test.js */
// Get the text content of H1 element
/*
const Nightmare = require('nightmare')
const nightmare = Nightmare({ show: true })
const selector = 'h1'
*/
nightmare
// .goto('https://github.com/segmentio/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
.end()
.then(rs => {
console.log(rs) // undefined
})
//.catch(error => {
// console.error('Search failed:', error)
//})
.then() must be called after .end() to run the .end() task.
Can I Use ES7 Async/Await?
API List - evaluate
/* example3.test.js */
const Nightmare = require('nightmare')
const nightmare = Nightmare({ show: true })
const selector = 'h1'
async function asynchronous(params) {
try {
const rs = await nightmare
.goto('https://github.com/segmentio/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
.end()
// nightmare
} catch (error) {
console.log(error)
}
}
asynchronous()
Yes, but the Node.js version should at least 7.10.1+
Debugging
Debug Flags
Install cross-env
npm install cross-env --save //local
npm install cross-env -g --save //global
cross-env DEBUG=nightmare:actions* node [entry file]
Run
With 'DEBUG=nightmare:actions*'
Without 'DEBUG=nightmare:actions*'
Test Report Library
About mochawesome
Installation
Install mochawesome
npm install mochawesome --save //local
npm install mochawesome -g --save //global
mocha [file.js] --reporter mochawesome
Run
Result
source: https://www.npmjs.com/package/mochawesome
References
https://www.linode.com/docs/development/nodejs/use-nightmarejs-to-automate-headless-browsing/
https://github.com/segmentio/nightmare
https://node.green/