@cypress/puppeteer [beta]
Utilize Puppeteer's browser API within Cypress with a single command.
This plugin is in public beta, so we'd love to get your feedback to improve it. Please leave any feedback you have in this discussion.
Table of Contents
Installation
npm
npm install --save-dev @cypress/puppeteer
yarn
yarn add --dev @cypress/puppeteer
With TypeScript
Add the following in tsconfig.json
:
{
"compilerOptions": {
"types": ["cypress", "@cypress/puppeteer/support"]
}
}
Compatibility
@cypress/puppeteer
requires Cypress version 13.6.0 or greater.
Only Chromium-based browsers (e.g. Chrome, Chromium, Electron) are supported.
Usage
@cypress/puppeteer
is set up in your Cypress config and support file, then executed in your spec. See API and Examples below for more details.
While the cy.puppeteer()
command is executed in the browser, the majority of the Puppeteer execution is run in the Node process via your Cypress config. You pass a string message name to cy.puppeteer()
that indicates which message handler to execute in the Cypress config. This is similar to how cy.task() operates.
In your Cypress config (e.g. cypress.config.ts
):
import { setup } from '@cypress/puppeteer'
export default defineConfig({
e2e: {
setupNodeEvents (on) {
setup({
on,
onMessage: {
async myMessageHander (browser) {
},
},
})
},
},
}
In your support file (e.g. cypress/support/e2e.ts
):
import '@cypress/puppeteer/support'
In your spec (e.g. spec.cy.ts
):
it('switches to and tests a new tab', () => {
cy.visit('/')
cy.get('button').click()
cy
.puppeteer('myMessageHander')
.should('equal', 'You said: Hello from Page 1')
})
API
Cypress Config - setup
This sets up @cypress/puppeteer
message handlers that run Puppeteer browser automation.
setup(options)
Options
on
required: The on
event registration function provided by setupNodeEvents
onMessage
required: An object with string keys and function values (see more details below)puppeteer
optional: The puppeteer
library imported from puppeteer-core
, overriding the default version of puppeteer-core
used by this plugin
onMessage
The keys provided in this are used to invoke their corresponding functions by calling cy.puppeteer(key)
in your Cypress test.
The functions should contain Puppeteer code for automating the browser. The code is executed within Node.js and not within the browser, so Cypress commands and DOM APIs cannot be utilized.
The functions receive the following arguments:
browser
A puppeteer browser instance connected to the Cypress-launched browser.
...args
The rest of the arguments are any de-serialized arguments passed to the cy.puppeteer()
command from your Cypress test.
Cypress Config - retry
This is a utility function provided to aid in retrying actions that may initially fail.
retry(functionToRetry[, options])
functionToRetry
required
A function that will run and retry if an error is thrown. If an error is not thrown, retry
will return the value returned by this function.
The function will continue to run at the default or configured interval until the default or configured timeout, at which point retry
will throw an error and cease retrying this function.
Options
optional
timeout
optional: The total time in milliseconds during which to attempt retrying the function. Default: 4000ms
delayBetweenTries
optional: The time to wait between retries. Default: 200ms
Cypress Spec - cy.puppeteer()
cy.puppeteer(messageName[, ...args])
messageName
required
A string matching one of the keys passed to the onMessage
option of setup
in your Cypress config.
...args
optional
Values that will be passed to the message handler. These values must be JSON-serializable.
Example:
cy.puppeteer('testNewTab', 'value 1', 42, [true, false])
setup({
on,
onMessage: {
testNewTab (browser, stringArg, numberArg, arrayOfBooleans) {
}
}
})
Examples
These examples can be found and run in the Cypress tests of this package with this project's cypress.config.ts.
While these examples use tabs, they could just as easily apply to windows. Tabs and windows are essentially the same things as far as Puppeteer is concerned and encapsulated by instances of the Page class.
Switching to a new tab
This example demonstrates the following:
- Switching to a tab opened by an action in the Cypress test
- Getting the page instance via Puppeteer utilizing the
retry
function - Getting page references and content via puppeteer
- Passing that content back to be asserted on in Cypress
spec.cy.ts
it('switches to a new tab', () => {
cy.visit('/cypress/fixtures/page-1.html')
cy.get('input').type('Hello from Page 1')
cy.get('button').click()
cy
.puppeteer('switchToTabAndGetContent')
.should('equal', 'You said: Hello from Page 1')
})
cypress.config.ts
import { defineConfig } from 'cypress'
import type { Browser as PuppeteerBrowser, Page } from 'puppeteer-core'
import { setup, retry } from '@cypress/puppeteer'
export default defineConfig({
e2e: {
setupNodeEvents (on) {
setup({
on,
onMessage: {
async switchToTabAndGetContent (browser: PuppeteerBrowser) {
const page = await retry<Promise<Page>>(async () => {
const pages = await browser.pages()
const page = pages.find((page) => page.url().includes('page-2.html'))
if (!page) throw new Error('Could not find page')
return page
})
await page.bringToFront()
const paragraph = (await page.waitForSelector('p'))!
const paragraphText = await page.evaluate((el) => el.textContent, paragraph)
paragraph.dispose()
await page.close()
return paragraphText
},
},
})
},
},
})
Creating a new tab
This example demonstrates the following:
- Passing a non-default version of puppeteer to
@cypress/puppeteer
- Passing arguments from
cy.puppeteer()
to the message handler - Creating a new tab and visiting a page via Puppeteer
- Getting page references and content via puppeteer
- Passing that content back to be asserted on in Cypress
spec.cy.ts
it('creates a new tab', () => {
cy.visit('/cypress/fixtures/page-3.html')
cy.get('#message').invoke('text').then((message) => {
cy.puppeteer('createTabAndGetContent', message)
.should('equal', 'I approve this message: Cypress and Puppeteer make a great combo')
})
})
cypress.config.ts
import { defineConfig } from 'cypress'
import puppeteer, { Browser as PuppeteerBrowser, Page } from 'puppeteer-core'
import { setup, retry } from '@cypress/puppeteer'
export default defineConfig({
e2e: {
setupNodeEvents (on) {
setup({
on,
puppeteer,
onMessage: {
async createTabAndGetContent (browser: PuppeteerBrowser, text: string) {
const page = await browser.newPage()
await page.goto(`http://localhost:8000/cypress/fixtures/page-4.html?text=${text}`)
const paragraph = (await page.waitForSelector('p'))!
const paragraphText = await page.evaluate((el) => el.textContent, paragraph)
paragraph.dispose()
await page.close()
return paragraphText
},
},
})
},
},
})
Troubleshooting
Error: Cannot communicate with the Cypress Chrome extension. Ensure the extension is enabled when using the Puppeteer plugin.
If you receive this error in your command log, the Puppeteer plugin was unable to communicate with the Cypress extension. This extension is necessary in order to re-activate the main Cypress tab after a Puppeteer command, when running in open mode.
- Ensure this extension is enabled in the instance of Chrome that Cypress launches by visiting chrome://extensions/
- Ensure the Cypress extension is allowed by your company's security policy by its extension id,
caljajdfkjjjdehjdoimjkkakekklcck
Contributing
Build the TypeScript files:
yarn build
Watch the TypeScript files and rebuild on file change:
yarn watch
Open Cypress tests:
yarn cypress:open
Run Cypress tests once:
yarn cypress:run
Run all unit tests once:
yarn test
Run unit tests in watch mode:
yarn test-watch