Puppeteer can be integrated with various testing frameworks to implement end-to-end testing, unit testing, and integration testing. Here are common integration methods and best practices.
1. Integration with Jest
Install Dependencies:
bashnpm install --save-dev puppeteer jest jest-puppeteer @types/puppeteer
Configure Jest:
javascript// jest.config.js module.exports = { preset: 'jest-puppeteer', testMatch: ['**/*.test.js'], setupFilesAfterEnv: ['./jest.setup.js'] };
Setup File:
javascript// jest.setup.js beforeEach(async () => { await page.goto('http://localhost:3000'); });
Write Tests:
javascriptdescribe('Puppeteer with Jest', () => { test('page title', async () => { await expect(page.title()).resolves.toMatch('My App'); }); test('user can login', async () => { await page.type('#username', 'testuser'); await page.type('#password', 'password'); await page.click('#login-button'); await expect(page).toMatch('Welcome'); }); });
2. Integration with Mocha
Install Dependencies:
bashnpm install --save-dev puppeteer mocha chai
Configure Mocha:
javascript// mocha.config.js module.exports = { timeout: 10000, require: ['mocha-setup.js'] };
Setup File:
javascript// mocha-setup.js const puppeteer = require('puppeteer'); const { expect } = require('chai'); let browser; let page; before(async () => { browser = await puppeteer.launch(); page = await browser.newPage(); }); after(async () => { await browser.close(); }); beforeEach(async () => { await page.goto('http://localhost:3000'); }); global.page = page; global.expect = expect;
Write Tests:
javascriptdescribe('Puppeteer with Mocha', () => { it('should display correct title', async () => { const title = await page.title(); expect(title).to.equal('My App'); }); it('should allow user to login', async () => { await page.type('#username', 'testuser'); await page.type('#password', 'password'); await page.click('#login-button'); const welcomeText = await page.$eval('.welcome', el => el.textContent); expect(welcomeText).to.include('Welcome'); }); });
3. Integration with Playwright
Install Dependencies:
bashnpm install --save-dev @playwright/test
Configure Playwright:
javascript// playwright.config.js module.exports = { testDir: './tests', use: { headless: true, screenshot: 'only-on-failure' } };
Write Tests:
javascriptconst { test, expect } = require('@playwright/test'); test.describe('Puppeteer migration', () => { test('basic navigation', async ({ page }) => { await page.goto('http://localhost:3000'); await expect(page).toHaveTitle('My App'); }); test('form submission', async ({ page }) => { await page.goto('http://localhost:3000'); await page.fill('#username', 'testuser'); await page.fill('#password', 'password'); await page.click('#login-button'); await expect(page.locator('.welcome')).toBeVisible(); }); });
4. Integration with Cypress
Install Cypress:
bashnpm install --save-dev cypress
Configure Cypress:
javascript// cypress.config.js const { defineConfig } = require('cypress'); module.exports = defineConfig({ e2e: { baseUrl: 'http://localhost:3000', setupNodeEvents(on, config) { on('task', { puppeteer({ url, action }) { return require('./puppeteer-task')(url, action); } }); } } });
Puppeteer Task:
javascript// puppeteer-task.js const puppeteer = require('puppeteer'); module.exports = async (url, action) => { const browser = await puppeteer.launch(); const page = await browser.newPage(); await page.goto(url); let result; if (action === 'screenshot') { result = await page.screenshot({ encoding: 'base64' }); } else if (action === 'pdf') { result = await page.pdf({ encoding: 'base64' }); } await browser.close(); return result; };
Cypress Tests:
javascript// cypress/e2e/puppeteer.spec.js describe('Puppeteer integration', () => { it('should take screenshot with Puppeteer', () => { cy.task('puppeteer', { url: 'http://localhost:3000', action: 'screenshot' }).then(screenshot => { cy.log('Screenshot taken'); }); }); });
5. End-to-End Testing Best Practices
Test Structure:
javascriptdescribe('User Flow', () => { before(async () => { // Setup test environment await setupTestDatabase(); }); after(async () => { // Cleanup test environment await cleanupTestDatabase(); }); beforeEach(async () => { // Preparation before each test await page.goto('http://localhost:3000'); }); test('complete user registration flow', async () => { // Test steps await page.click('#register-button'); await page.type('#username', 'newuser'); await page.type('#email', 'newuser@example.com'); await page.type('#password', 'password123'); await page.click('#submit-button'); // Verify results await expect(page).toMatch('Registration successful'); }); });
Test Data Management:
javascript// test-data.js module.exports = { validUser: { username: 'testuser', email: 'test@example.com', password: 'password123' }, invalidUser: { username: '', email: 'invalid-email', password: '123' } }; // Use test data const { validUser } = require('./test-data'); test('register with valid data', async () => { await page.type('#username', validUser.username); await page.type('#email', validUser.email); await page.type('#password', validUser.password); await page.click('#submit-button'); await expect(page).toMatch('Success'); });
6. Visual Regression Testing
Using Percy:
bashnpm install --save-dev @percy/puppeteer
javascriptconst percy = require('@percy/puppeteer'); describe('Visual regression tests', () => { beforeAll(async () => { await percy.start(); }); afterAll(async () => { await percy.stop(); }); test('homepage visual', async () => { await page.goto('http://localhost:3000'); await percy.snapshot(page, 'Homepage'); }); });
Using BackstopJS:
javascript// backstop.config.js module.exports = { scenarios: [ { label: 'Homepage', url: 'http://localhost:3000', selectors: ['#header', '#main', '#footer'] } ], paths: { bitmaps_reference: 'backstop_data/bitmaps_reference', bitmaps_test: 'backstop_data/bitmaps_test', html_report: 'backstop_data/html_report' } };
7. Performance Testing
Using Lighthouse:
bashnpm install --save-dev lighthouse puppeteer
javascriptconst lighthouse = require('lighthouse'); const puppeteer = require('puppeteer'); describe('Performance tests', () => { test('page performance score', async () => { const browser = await puppeteer.launch(); const page = await browser.newPage(); const runnerResult = await lighthouse('http://localhost:3000', { port: new URL(browser.wsEndpoint()).port, output: 'json' }); const score = runnerResult.lhr.categories.performance.score * 100; expect(score).toBeGreaterThan(80); await browser.close(); }); });
8. Test Reporting
Using Allure:
bashnpm install --save-dev allure-commandline
javascriptconst allure = require('allure-js-commons'); describe('Allure reporting', () => { test('with Allure steps', async () => { const epic = new allure.Epic('User Management'); const feature = new allure.Feature('Registration'); const story = new allure.Story('User can register'); epic.addFeature(feature); feature.addStory(story); await page.click('#register-button'); await page.type('#username', 'testuser'); await page.click('#submit-button'); story.addStep('Click register button'); story.addStep('Enter username'); story.addStep('Submit form'); }); });
9. CI/CD Integration
GitHub Actions Configuration:
yamlname: Puppeteer Tests on: [push, pull_request] jobs: test: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - name: Setup Node.js uses: actions/setup-node@v2 with: node-version: '16' - name: Install dependencies run: npm ci - name: Install Chrome run: | sudo apt-get update sudo apt-get install -y chromium-browser - name: Run tests run: npm test env: CI: true
Docker Configuration:
dockerfileFROM node:16-alpine # Install Chrome RUN apk add --no-cache chromium # Install dependencies COPY package*.json ./ RUN npm ci # Copy test files COPY . . # Run tests CMD ["npm", "test"]
10. Best Practices Summary
1. Test Isolation:
javascript// Use separate context for each test beforeEach(async () => { const context = await browser.createIncognitoBrowserContext(); page = await context.newPage(); }); afterEach(async () => { await context.close(); });
2. Wait Strategies:
javascript// Use explicit waits await page.waitForSelector('.element', { visible: true }); // Avoid hardcoded delays // await page.waitForTimeout(5000); // Not recommended
3. Error Handling:
javascripttest('with error handling', async () => { try { await page.click('#button'); } catch (error) { // Save failure screenshot await page.screenshot({ path: 'failure.png' }); throw error; } });
4. Test Data Cleanup:
javascriptafterEach(async () => { // Cleanup test data await cleanupTestData(); });
5. Parallel Testing:
javascript// Jest configuration module.exports = { maxWorkers: 4, // Run tests in parallel preset: 'jest-puppeteer' };