preface
Page interaction has always been the most important part of the front end, but it has always been a difficult problem to implement automated testing at the UI level. In the past, we could only manually click the page to test page interaction and data transfer. Therefore, E2E testing is born out of necessity. E2E testing is extremely convenient to solve the user level interaction testing.
Puppeteer
Puppeteer. As the name suggests, Puppeteer is a Node library that provides a number of advanced apis for controlling Chromium or Chrome via the DevTools protocol.
The Puppeteer API is hierarchical and reflects the browser structure.
- Browser: a Browser instance that can have a Browser context and can create a Browser object through puppeteer.launch or puppeteer.connect.
- BrowserContext: This instance defines a browser context that can have multiple pages. A browser context is created by default when a browser instance is created (it cannot be closed). In addition to using the createIncognitoBrowserContext () to create an anonymous browsers context (not Shared with other context browser cookies/cache).
- Page: Contains at least one main frame, in addition to the main frame may exist other frames, such as iframe.
- Frame: The Frame in the page. At each point in time, the page exposes the details of the current Frame through the page.mainframe () and frame.childframes () methods. There is at least one execution context for the framework
- ExecutionCOntext: represents a JavaScript ExecutionCOntext.
- Worker: Has a single execution context for easy interaction with WebWorkers.
What can puppeteer do?
- Generate screen captures and PDF of the page.
- Grab the SPA and generate pre-rendered content (that is, “SSR”).
- Automatic form submission, UI testing, keyboard input, etc.
- Create the latest automated test environment. Run tests directly in the latest version of Chrome using the latest JavaScript and browser features.
- Capture [timeline tracking] of your site
- Implement a simple crawler
Why choose puppeteer ?
Framework or library | Description | cross-browser | Auto-generated code | The community is active | Core principles |
---|---|---|---|---|---|
PhantomJS [star] 28.5 k | A webKit-based headless browser with no UI. | No | No | Yes | Headless browser based on WebKit kernel |
Nightwatch [STR 10.8 k] | Node.js invokes the end-to-end (E2E) testing framework implemented by the Webdriver API. | Yes | No | No | webdriver |
The Puppeteer [star] 72.2 k | Puppeteer is a software provider built on the DevTools protocol (devtools-protocol), controls the high-level API node libraries for Chrome or Chromium. | No | Yes | Yes | ChromeDevTools agreement |
The Selenium [star] 21.2 k | Selenium is a browser automation testing library. Written in multiple languages, Selenium can be used to do any task that requires interaction with a browser. Selenium IDE was officially released from maintenance in 2018. Selenium IDE is Dead. | Yes | No | No | webdriver |
Nightwatch and Selenium are both webDriver types. Webdriver operates directly on elements of the browser page, and even the browser itself (taking screenshots, window sizes, starting, closing, installing plug-ins, configuring certificates, and so on). Thanks to the use of the browser’s native API, the speed is greatly improved, as long as the browser supports WebDriver can be implemented. PhantomJS, which is a home browser, is all about internal clicking, page-turning, and other human-related operations that require programming.
Puppeteer is built on the ChromeDevTools protocol, which allows tools to examine, debug, and configure Chrome and other Blink-based browsers. Chrome Development tools uses this protocol and the team maintains its API. Tools can be divided into multiple domains including DOM, debugger, network, and so on. Each field defines the many commands it supports and the events it generates. Commands and events are serialized JSON objects with fixed structures.
The key is to automate the generation of test code and online testing by extending Handless Recorder to Google at the same time.
To sum up, Puppeteer has Google dad behind it, star is growing year by year, and the community is active. If you need to develop across browsers, use Nightwatch
How to use puppeteer ?
Install dependencies
Execute command under new Node project:
npm install puppeteer –save
According to the need to introduce
const puppeteer = require(‘puppeteer’)
Basic usage and common functions
The overall use of the artifact is relatively simple, the following begins our use of the road.
Start the Browser
The core function is to call puppeteer.launch() asynchronously and create an instance of Browser according to the corresponding configuration parameters.
; (async() = > {const path = require('path');
const puppeteer = require('puppeteer');
const chromiumPath = path.join(__dirname, '.. / '.'chromium/chromium/chrome.exe');
// Start chrome
const browser = await puppeteer.launch({
// Specify the path to the browser
executablePath: chromiumPath,
// Whether the browser mode is headless. The default mode is headless
headless: false}); }) ()Copy the code
To access the page
To access a page, you first create a browser context, then create a new page based on that context, and finally specify the URL to visit.
; (async() = > {const puppeteer = require('puppeteer')
const browser = await puppeteer.launch({
// Set header mode (default is true, headless mode)
headless: false,})// A new page is created in a default browser context
const page1 = await browser.newPage()
// Blank page access asks the specified url
await page1.goto('https://www.baidu.com')
// Wait for the jump to end
const navigationPromise = page1.waitForNavigation()
await navigationPromise
// Create an anonymous browser context
const browserContext = await browser.createIncognitoBrowserContext()
// Create a new page in this context
const page2 = await browserContext.newPage()
page2.goto('https://www.baidu.com')
})()
Copy the code
Common operations on the page
- page.goto(url, options)
- page.goBack(options)
- page.goForward(options)
- page.reload(options)
- browser.createIncognitoBrowserContext()
The device emulation
Often, browsing results for different types of devices are needed, so device simulation can be used. Here is a simulation of the browser results for an iPhone X device
; (async() = > {try {
const puppeteer = require('puppeteer')
// Start chrome
const browser = await puppeteer.launch({
// Whether the browser mode is headless. The default mode is headless
headless: false,})const page = await browser.newPage()
// Device emulation: Emulates an iPhone X
// user agent
await page.setUserAgent(
'the Mozilla / 5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/11.0 Mobile/15A372 Safari/604.1'
)
// makeLinkJump
await page.goto('https://www.baidu.com')
// Viewport simulation
// await page.setViewport({ width: 1680, height: 841 })
/ / set the cookie
// await page.setCookie([{ name: 'yhx', birthday: '1998' }])
} catch (err) {
console.log(err)
}
})()
Copy the code
Get DOM node
There are two ways to get a DOM node. One way is to call the native function that comes with the page directly, and the other way is to get it by executing JS code.
The following methods are commonly used:
- page.$(selector)
- page.$$(selector)
Similar to the document. QuerySelector and document. QuerySelectorAll.
The object they return is <Promise<? ElementHandle>>, which is used to determine whether an element exists
- page.$eval(selector, pageFunction[, …args])
- page.$$eval(selector, pageFunction[, …args])
- page.evaluate(pageFunction[, …args])
On the basis of 1 and 2, 3 and 4 can execute some methods and are more commonly used in practice
; (async() = > {const puppeteer = require('puppeteer')
// Start chrome
const browser = await puppeteer.launch({
// Whether the browser mode is headless. The default mode is headless
headless: false,})// A new page is created in a default browser context
const page1 = await browser.newPage()
await page1.setViewport({ width: 1200.height: 1000 })
// Blank page to access the specified url
await page1.goto('https://www.baidu.com')
// Wait for the title node to appear
await page1.waitForSelector('.s_btn')
// Get the node using the page method
const baiduBtnDomText1 = await page1.$eval('.s_btn'.(el) = > el.value)
console.log(baiduBtnDomText1) // Do a search
await page1.waitForTimeout(1000)
// Use evaluate to insert the js script
const baiduBtnDomText2 = await page1.evaluate(() = > {
const baiduBtnDom = document.querySelector('.s_btn')
baiduBtnDom.value = Zaihui Technology
return baiduBtnDom.value
})
console.log(baiduBtnDomText2) // Do a search
await page1.waitForTimeout(1000)
})()
Copy the code
Simulating user operations
Page itself provides the original Mouse and Keyboard analog input classes.
- page.mouse
- page.keyboard
However, it also provides more convenient and fast functions to simulate user actions
- Page. Click (selector[, options]) simulates clicking on the selected element
- Page. Type (selector, text[, options]) is entered in the selected input box
- Page. Hover (selector) simulates mouse movement over the selected element
- page.select(selector, … Values) simulates the select option on the selected element
- Page. Tap (selector) simulates touch on the selected element
Simulating user actions is often accompanied by delayed waiting
Wait for the operating
The following wait functions are provided
- page.waitForTimeout(number)
- page.waitForNavigation(options)
- page.waitForSelector(selector[, options])
- page.waitForXPath(xpath[, options])
- page.waitForFunction(pageFunction[, options[, …args]])
- page.waitFor(selectorOrFunctionOrTimeout[, options[, …args]])
Listening to the
close | frameattached | pageerror |
---|---|---|
console | framedetached | request |
dialog | framenavigated | requestfailed |
domcontentloaded | load | requestfinished |
error | metrics | response |
Take the example of listening for requests and responses
The following is to monitor the request and response of a JS script in Baidu. The request event is to monitor the request, and the response event is to monitor the response.
; (async() = > {// Start chrome
const puppeteer = require('puppeteer')
const browser = await puppeteer.launch({
// Set header mode (default is true, headless mode)
headless: false,})// A new page is created in a default browser context
const page1 = await browser.newPage()
page1.on('request'.(request) = > {
if (
request.url() ===
'https://dss0.bdstatic.com/5aV1bjqh_Q23odCf/static/superman/js/lib/esl-ef22c5ed31.js'
) {
console.log(request.resourceType())
console.log(request.method())
console.log(request.headers())
}
})
page1.on('response'.(response) = > {
if (
response.url() ===
'https://dss0.bdstatic.com/5aV1bjqh_Q23odCf/static/superman/js/lib/esl-ef22c5ed31.js'
) {
console.log(response.headers())
}
})
// Blank page to access the specified url
await page1.goto('https://www.baidu.com')
})()
Copy the code
Intercept a request
By default only read-only property request events, are not able to intercept the request, if you want to intercept the request requires through page. SetRequestInterception < Boolean > request interceptor (value) to start, It then uses the Request. abort, request.continue, and request.respond methods to decide what to do next with the request.
; (async() = > {// Start chrome
const puppeteer = require('puppeteer')
const browser = await puppeteer.launch({
// Whether the browser mode is headless. The default mode is headless
headless: false,})// A new page is created in a default browser context
const page1 = await browser.newPage()
// Request interception is enabled
await page1.setRequestInterception(true) // true to enable, false to disable
page1.on('request'.(request) = > {
if (
request.url() ===
'https://dss0.bdstatic.com/5aV1bjqh_Q23odCf/static/superman/js/lib/esl-ef22c5ed31.js'
) {
// Terminate the request
request.abort()
console.log('This request has been terminated!! ')}else {
// Continue the request
request.continue()
}
})
// The blank page just asks the specified url
await page1.goto('https://www.baidu.com')
})()
Copy the code
screenshots
A screenshot is a very useful function. You can save a snapshot by capturing it, which is convenient for troubleshooting later. (Note: Screenshot in headless mode)
; (async() = > {// Start chrome
const puppeteer = require('puppeteer')
const browser = await puppeteer.launch({
// Whether the browser mode is headless. The default mode is headless
headless: false,})const page1 = await browser.newPage()
await page1.goto('https://www.baidu.com')
// A new page is created in a default browser context
// Screenshot operation, use Page. Screenshot function
// Screenshot of the entire Page :Page. Screenshot function default screenshot of the entire Page, plus the fullPage parameter is a full screen screenshot
await page1.screenshot({
path: '.. /imgs/fullScreen.png'.fullPage: true,})// Capture the contents of an area of the screen
await page1.screenshot({
path: '.. /imgs/partScreen.jpg'.type: 'jpeg'.quality: 80.clip: {
x: 0.y: 0.width: 375.height: 300,
},
})
browser.close()
})()
Copy the code
Generating PDF
In addition to preserving snapshots using screenshots, you can also preserve snapshots using PDF.
; (async() = > {// Start chrome
const puppeteer = require('puppeteer')
const browser = await puppeteer.launch({
// Whether the browser mode is headless. The default mode is headless
headless: true,})const page1 = await browser.newPage()
await page1.goto('https://www.baidu.com')
// Generate a PDF file from the Page content, using page.pdf -- note: this can only be called in headless mode
await page1.pdf({
path: '.. /pdf/baidu.pdf',
})
browser.close()
})()
Copy the code
Google Extension
Handleless Recorder
Handleless Recorder is a Chrome extension that logs your browser interactions and generates Puppeteer scripts. Manual writing E2E tests has always been a difficult problem, and with the help of the Handleless Recorder extension program can greatly help us to write E2E tests. To put it simply, Handleless Recorder helps you record actions performed in Chrome and generate Punppeter code. The implementation of “one operation, test generation” greatly shortens the time for us to write E2E tests, reduces the amount of handwritten test code, and frees up our hands. Take the hassle out of writing E2E tests.
E2E test
End To End (E2E) is an end-to-end sandbox test. Test cases are written To simulate user operations To ensure that components communicate normally and data is transferred as expected during page interaction.
So for a Web application, the user environment is the browser, and the user actions are moving, clicking, and requesting, and those are the parts that we need to simulate, so let’s jump right into the environment and practice.
How to start?
One of the most original test requirements
Expect -> assert results -> cause of failure
; (async() = > {const puppeteer = require('puppeteer')
// Start chrome
const browser = await puppeteer.launch({
// Whether the browser mode is headless. The default mode is headless
headless: false,})try {
const page = await browser.newPage()
await page.goto('https://www.baidu.com')
await page.waitForTimeout(1500)
page.goton()
console.table([`define: test goto baidu.com`.'result: success'])}catch (err) {
console.table([
`define: test goto baidu.com`.'result: fail'.`errmsg: The ${String(err)}`,])}await browser.close()
})()
Copy the code
Puppeteer can be used in conjunction with the Jest testing framework
- Jest: Is a full-featured testing framework developed by Facebook. It requires very little configuration and can be used directly
- Puppeteer: a Google-created Node.js library that provides a convenient API for controlling Headless Chrome
npm install jest –save
npm install jest-puppeteer –save
Create the jest.config.js configuration file
module.exports = {
preset: 'jest-puppeteer'.// Call preset so that Jest can be used with Puppeteer
testPathIgnorePatterns: ['<rootDir>/node_modules/'.'<rootDir>/src/'].testMatch: ['* * /? (*) +(e2e|spec|test).[tj]s? (x)'].// Specify the file to test
}
Copy the code
Create the jest-puppeteer.config.js configuration file
module.exports = { launch: { headless: process.env.HEADLESS ! == 'false', devtools: true, }, }Copy the code
Write a single test using Jest and Puppeteer
const puppeteer = require('puppeteer')
const timeout = 30000
let browser
let page
describe('crm login'.() = > {
beforeAll(async () => {
browser = await puppeteer.launch({
// Set header mode (default is true, headless mode)
headless: false,
})
page = await browser.newPage()
})
...
it('descripe your test'.async() = > {// await page something
},timeout)
...
afterAll(async() = > {await browser.close()
})
})
Copy the code
The test report
First we install jest-html-report
cnpm install jest-html-report –save-dev
In jest. Config. js, configure the attributes of jest- html-Reporter
Attributes used:
Property | Description | Type | Default |
---|---|---|---|
pageTitle | The title of the document | string | “Test Suite” |
outputPath | The path to where the plugin will output the HTML report | string | “./test-report.html” |
includeFailureMsg | If this setting is set to true, this will output the detailed failure message for each failed test. | boolean | false |
For other attributes, see the official documentation: github.com/Hargne/jest…
! [image-20210721145220237](/Users/apple/Library/Application Support/typora-user-images/image-20210721145220237.png)
Juejin. Cn/post / 696937…
Juejin. Cn/post / 684490…
Juejin. Cn/post / 685041…
Copyfuture.com/blogs-detai…