跳转至主内容
版本:29.6

与 puppeteer 一起使用

通过 Global Setup/TeardownAsync Test Environment 这些 API, Jest 可以很丝滑地和 puppeteer一起使用。

备注

如果你的测试在 jest 范围之外通过传递page.$eval, page.$$eval or page.evaluate函数来实现。目前无法通过 Puppeteer 生成正确的代码覆盖率。 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
npm install --save-dev jest-puppeteer
  1. Specify preset in your Jest configuration:
{
"preset": "jest-puppeteer"
}
  1. Write your test
describe('Google', () => {
beforeAll(async () => {
await page.goto('https://google.com');
});

it('should be titled "Google"', async () => {
await expect(page.title()).resolves.toMatch('Google');
});
});

无需加载任何依赖项。 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: 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

setup.js
const {mkdir, writeFile} = require('fs').promises;
const os = require('os');
const path = require('path');
const puppeteer = require('puppeteer');

const DIR = path.join(os.tmpdir(), 'jest_puppeteer_global_setup');

module.exports = async function () {
const browser = await puppeteer.launch();
// store the browser instance so we can teardown it later
// this global is only available in the teardown but not in TestEnvironments
globalThis.__BROWSER_GLOBAL__ = browser;

// use the file system to expose the wsEndpoint for TestEnvironments
await mkdir(DIR, {recursive: true});
await writeFile(path.join(DIR, 'wsEndpoint'), browser.wsEndpoint());
};

Then we need a custom Test Environment for puppeteer

puppeteer_environment.js
const {readFile} = require('fs').promises;
const os = require('os');
const path = require('path');
const puppeteer = require('puppeteer');
const NodeEnvironment = require('jest-environment-node').TestEnvironment;

const DIR = path.join(os.tmpdir(), 'jest_puppeteer_global_setup');

class PuppeteerEnvironment extends NodeEnvironment {
constructor(config) {
super(config);
}

async setup() {
await super.setup();
// get the wsEndpoint
const wsEndpoint = await readFile(path.join(DIR, 'wsEndpoint'), 'utf8');
if (!wsEndpoint) {
throw new Error('wsEndpoint not found');
}

// connect to puppeteer
this.global.__BROWSER_GLOBAL__ = await puppeteer.connect({
browserWSEndpoint: wsEndpoint,
});
}

async teardown() {
if (this.global.__BROWSER_GLOBAL__) {
this.global.__BROWSER_GLOBAL__.disconnect();
}
await super.teardown();
}

getVmContext() {
return super.getVmContext();
}
}

module.exports = PuppeteerEnvironment;

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

teardown.js
const fs = require('fs').promises;
const os = require('os');
const path = require('path');

const DIR = path.join(os.tmpdir(), 'jest_puppeteer_global_setup');
module.exports = async function () {
// close the browser instance
await globalThis.__BROWSER_GLOBAL__.close();

// clean-up the wsEndpoint file
await fs.rm(DIR, {recursive: true, force: true});
};

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

test.js
const timeout = 5000;

describe(
'/ (Home Page)',
() => {
let page;
beforeAll(async () => {
page = await globalThis.__BROWSER_GLOBAL__.newPage();
await page.goto('https://google.com');
}, timeout);

it('should load without error', async () => {
const text = await page.evaluate(() => document.body.textContent);
expect(text).toContain('google');
});
},
timeout,
);

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

module.exports = {
globalSetup: './setup.js',
globalTeardown: './teardown.js',
testEnvironment: './puppeteer_environment.js',
};

Here's the code of full working example.