Using with puppeteer

With the Global Setup/Teardown and Async Test Environment APIs, Jest can work smoothly with puppeteer.

Generating code coverage for test files using Puppeteer is currently not possible if your test uses page.$eval, page.$$eval or page.evaluate as the passed function is executed outside of Jest’s scope. Check out issue #7962 on GitHub for a workaround.

Use jest-puppeteer Preset

Jest Puppeteer provides all required configuration to run your tests using Puppeteer.

  1. First install jest-puppeteer
  1. yarn add --dev jest-puppeteer
  1. Specify preset in your Jest configuration:
  1. {
  2. "preset": "jest-puppeteer"
  3. }
  1. Write your test
  1. describe('Google', () => {
  2. beforeAll(async () => {
  3. await page.goto('https://google.com');
  4. });
  5. it('should be titled "Google"', async () => {
  6. await expect(page.title()).resolves.toMatch('Google');
  7. });
  8. });

There’s no need to load any dependencies. Puppeteer’s page and browser classes will automatically be exposed

See documentation.

Custom example without jest-puppeteer preset

You can also hook up puppeteer from scratch. The basic idea is to:

  1. launch & file the websocket endpoint of puppeteer with Global Setup
  2. connect to puppeteer from each Test Environment
  3. close puppeteer with Global Teardown

Here’s an example of the GlobalSetup script

  1. // setup.js
  2. const path = require('path');
  3. const fs = require('fs');
  4. const os = require('os');
  5. const mkdirp = require('mkdirp');
  6. const puppeteer = require('puppeteer');
  7. const DIR = path.join(os.tmpdir(), 'jest_puppeteer_global_setup');
  8. module.exports = async function () {
  9. const browser = await puppeteer.launch();
  10. // store the browser instance so we can teardown it later
  11. // this global is only available in the teardown but not in TestEnvironments
  12. global.__BROWSER_GLOBAL__ = browser;
  13. // use the file system to expose the wsEndpoint for TestEnvironments
  14. mkdirp.sync(DIR);
  15. fs.writeFileSync(path.join(DIR, 'wsEndpoint'), browser.wsEndpoint());
  16. };

Then we need a custom Test Environment for puppeteer

  1. // puppeteer_environment.js
  2. const fs = require('fs');
  3. const path = require('path');
  4. const os = require('os');
  5. const puppeteer = require('puppeteer');
  6. const NodeEnvironment = require('jest-environment-node');
  7. const DIR = path.join(os.tmpdir(), 'jest_puppeteer_global_setup');
  8. class PuppeteerEnvironment extends NodeEnvironment {
  9. constructor(config) {
  10. super(config);
  11. }
  12. async setup() {
  13. await super.setup();
  14. // get the wsEndpoint
  15. const wsEndpoint = fs.readFileSync(path.join(DIR, 'wsEndpoint'), 'utf8');
  16. if (!wsEndpoint) {
  17. throw new Error('wsEndpoint not found');
  18. }
  19. // connect to puppeteer
  20. this.global.__BROWSER__ = await puppeteer.connect({
  21. browserWSEndpoint: wsEndpoint,
  22. });
  23. }
  24. async teardown() {
  25. await super.teardown();
  26. }
  27. runScript(script) {
  28. return super.runScript(script);
  29. }
  30. }
  31. module.exports = PuppeteerEnvironment;

Finally we can close the puppeteer instance and clean-up the file

  1. // teardown.js
  2. const os = require('os');
  3. const path = require('path');
  4. const rimraf = require('rimraf');
  5. const DIR = path.join(os.tmpdir(), 'jest_puppeteer_global_setup');
  6. module.exports = async function () {
  7. // close the browser instance
  8. await global.__BROWSER_GLOBAL__.close();
  9. // clean-up the wsEndpoint file
  10. rimraf.sync(DIR);
  11. };

With all the things set up, we can now write our tests like this:

  1. // test.js
  2. const timeout = 5000;
  3. describe(
  4. '/ (Home Page)',
  5. () => {
  6. let page;
  7. beforeAll(async () => {
  8. page = await global.__BROWSER__.newPage();
  9. await page.goto('https://google.com');
  10. }, timeout);
  11. it('should load without error', async () => {
  12. const text = await page.evaluate(() => document.body.textContent);
  13. expect(text).toContain('google');
  14. });
  15. },
  16. timeout,
  17. );

Finally, set jest.config.js to read from these files. (The jest-puppeteer preset does something like this under the hood.)

  1. module.exports = {
  2. globalSetup: './setup.js',
  3. globalTeardown: './teardown.js',
  4. testEnvironment: './puppeteer_environment.js',
  5. };

Here’s the code of full working example.