Mitchell Early
Mitchell Early

Reputation: 21

Why won't test.beforeEach from extended fixture run before each test when running multiple spec files?

Using Playwright TypeScript, I have a base fixture file and then another fixture file that extends this base fixture and adds specific logic for a set of api tests, including a beforeEach hook. The folder of api spec files intended for execution uses the extended fixture for their test cases.

Base fixture file:

import { test as base } from "@playwright/test";
import { ApiTest, ApiTestFixture } from "{custom-package}";

export const test = base.extend<ApiTestFixture>({
    apiTest: async ({}, use) => {
        const apiTest = new ApiTest();
        await use(apiTest);
    },
});

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

Extended fixture file:

import { ApiTestFixture } from "{custom-package}";
import { test as base } from "@playwright/test";
import { UserClient } from "../../../shared/clients/users/user-client";
import { ProjectClient } from "../../../shared/clients/projects/project-client";

export type UserApiFixture = ApiTestFixture & {
    projectClient: ProjectClient;
    userClient: UserClient;
};

export const test = base.extend<UserApiFixture>({
    projectClient: async ({ }, use) => {
        const projectClient = new ProjectClient();
        await use(projectClient);
    },
    userClient: async ({ }, use) => {
        const userClient = new UserClient();
        await use(userClient);
    }
});

test.beforeEach(async ({ apiTest }) => {
    await apiTest.beforeEach(test, "Users");
});

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

When I run the api folder containing multiple spec files, I would expect the beforeEach hook in the extended fixture file to be run before every test in every file.

What actually happens is the beforeEach hook will run before each test in whichever spec file happens to execute first. This beforeEach hook then does not get executed for further spec files in the run using this fixture.

If run the spec files individually, everything runs as I would expect.

Does anybody know anything that may help me?

Upvotes: 2

Views: 68

Answers (1)

mwopitz
mwopitz

Reputation: 648

One issue I see in your fixture files is that your extended test function does not extend your base test function. Both your base test and extended test just extend Playwright's base test and add different sets of fixtures.

Your are saying that "the beforeEach hook will run before each test in whichever spec file happens to execute first." I'm not quite sure why that happens. It seems your beforeEach hook would throw an error when executed, because the apiTest fixture is not defined for the extended test function. Maybe I'm missing something here.

I'll showcase a setup of layered fixture files that works for me:

// base-fixtures.ts
import { test as base } from "@playwright/test";
import type { Locator, Page } from "@playwright/test";

export class ExamplePage {
  readonly url = "https://example.org/";
  readonly page: Page;

  readonly moreInformationLink: Locator;

  constructor(page: Page) {
    this.page = page;
    this.moreInformationLink = this.page.getByRole('link', { name: 'More information...' })
  }

  async goto() {
    await this.page.goto(this.url);
  }
}

export type BaseFixtures = {
  examplePage: ExamplePage;
}

export const test = base.extend<BaseFixtures>({
  examplePage: async ({ page }, use) => {
    use(new ExamplePage(page));
  }
});

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

// extended-fixtures.ts
import { test as base } from "./base-fixtures";
import type { Locator, Page } from "@playwright/test";

export class ExampleHelpPage {
  readonly page: Page
  readonly url = "http://www.iana.org/help/example-domains";

  readonly heading: Locator;

  constructor(page: Page) {
    this.page = page;
    this.heading = this.page.getByRole('heading', { name: 'Example Domains' });
  }
}

export type ExtendedFixtures = {
  exampleHelpPage: ExampleHelpPage;
}

export const test = base.extend<ExtendedFixtures>({
  exampleHelpPage: async ({ page }, use) => {
    use(new ExampleHelpPage(page));
  }
});

test.beforeEach(async ({ examplePage }) => {
  await examplePage.goto();
});

export { expect } from "./base-fixtures";
// example.spec.ts
import { test, expect } from './extended-fixtures';

test('has title', async ({ page }) => {
  await expect(page).toHaveTitle("Example Domain");
});

test('get more information', async ({ page, examplePage, exampleHelpPage }) => {;
  await examplePage.moreInformationLink.click();
  await expect(exampleHelpPage.heading).toBeVisible();
});

When I execute the example tests, both of them are passing for me. The beforeEach hook seems to trigger as expected and navigates to the example page both times.

Upvotes: 0

Related Questions