Thursday, May 29, 2025

Add global beforeEach / afterEach hooks using Playwright automatic fixtures

In this example we will explore how to use global beforeEach and afterEach implementation using playwright automatic fixtures. Playwright’s test fixtures are a powerful feature for sharing setup and logic across tests, promoting code reuse and maintainability. With custom fixtures, you can share code across multiple tests, resulting in a test base that is easier to expand and easier to maintain.

Add global beforeEach / afterEach hooks using Playwright automatic fixtures


Playwright automatic fixtures

Follow the below steps to step up auto fixtures in playwright.

  1. Define a fixture in a separate file (e.g., fixtures.ts).
  2. Use test.extend to create a new test type with your fixture.
  3. Within the fixture function, use async ({}, use) => { ... } to define setup and teardown logic.
  4. The use function is where the test execution happens.
  5. Automatic fixtures are declared with the auto: true option.

NOTE : This auto fixture will execute for each tests present in playwright spec files. In this example we are tracing start and end time of execution but we can leverage this functionality for multiple purpose based  on requirement.

fixtures.ts

import { test as base } from "@playwright/test";

type MyFixtures = {
    timeLogger: void,
};

 export const test = base.extend<MyFixtures>({
        timeLogger: [
            async ({}, use, testInfo) => {
                const startTime = new Date().toISOString();
                console.log(`[${testInfo.title}] Started`);

                test.info().annotations.push({
                    type: "Start",
                    description: startTime,
                });

                await use();

                const endTime = new Date().toISOString();
                console.log(`[${testInfo.title}] Ended`);

                test.info().annotations.push({
                    type: "End",
                    description: endTime,
                });
            },
            { auto: true }, 
        ],
    };


Use the Fixture in Tests:

  1. Import the extended test object from your fixture file.
  2. Tests will automatically use the timeLogger fixture due to auto: true.

example.spec.ts
import { test } from './fixtures';
    import { expect } from '@playwright/test';
    
    test('my test', async ({ page }) => {
      await page.goto('https://example.com');
      expect(await page.title()).toBe('Example Domain');
    });
    
    test('another test', async ({ page }) => {
     await page.goto('https://example.com');
     expect(await page.title()).toBe('Example Domain');
    });

Output :

In this report, we can see the start and end time in annotation section.

Add global beforeEach / afterEach hooks using Playwright automatic fixtures

Also we can use this auto fixture to track error in playwright script.

import { test as base } from "@playwright/test";

export const test = base.extend<{
  exceptionLogger: void;
  timeLogger: void;
}>({
  page: async ({ page }, use) => {
    await use(page);
  },
  exceptionLogger: [
    async ({ page }, use) => {
      // before test
      const errors: Error[] = [];
      page.on("pageerror", (error) => errors.push(error));

      await use();

      // after test
      if (errors.length > 0) {
        await test.info().attach("frontend-exceptions", {
          body: errors
            .map((error) => `${error.message}\n${error.stack}`)
            .join("\n-----\n"),
        });

        throw new Error("Something went wrong in JS land");
      }
    },
    { auto: true },
  ],
});

export { expect } from "@playwright/test";

Output :

Add global beforeEach / afterEach hooks using Playwright automatic fixtures


Complete code :

import { test as base } from "@playwright/test";

export const test = base.extend<{
  exceptionLogger: void;
  timeLogger: void;
}>({
  page: async ({ page }, use) => {
    await use(page);
  },
  timeLogger: [
    async ({}, use) => {
      // before test
      test.info().annotations.push({
        type: "Start",
        description: new Date().toISOString(),
      });

      await use();

      // after test
      test.info().annotations.push({
        type: "End",
        description: new Date().toISOString(),
      });
    },
    { auto: true },
  ],
  exceptionLogger: [
    async ({ page }, use) => {
      // before test
      const errors: Error[] = [];
      page.on("pageerror", (error) => errors.push(error));

      await use();

      // after test
      if (errors.length > 0) {
        await test.info().attach("frontend-exceptions", {
          body: errors
            .map((error) => `${error.message}\n${error.stack}`)
            .join("\n-----\n"),
        });

        throw new Error("Something went wrong in JS land");
      }
    },
    { auto: true },
  ],
});

export { expect } from "@playwright/test";

This is all about playwright automatic fixtures.


No comments:

Post a Comment