Creating fixtures with Playwright is a simple process that can greatly enhance

I have a requirement to set up fixtures where the first fixture is always available (acting as a base class) and the second fixture will vary in different test files (like a derived class). I've implemented the following code which seems to be working as expected. Just wondering if this approach is acceptable or if there are better options out there?

//baseFixture.js
import { test as base} from '@playwright/test';
interface MyFixtures {
  fixture1: string;
}

export const test = base.extend<MyFixtures>({
  fixture1: "fixture-one"
}, );

//derivedFixture.js

import {test as test1} from 'baseFixture'
interface MyFixtures2 {
  fixture2: string;
}

export const test = test1.extend<MyFixtures2>({
  fixture2: "fixture-two"
}, );


//in test_file.js

import {test} from 'derivedFixture'

  test('should allow me use composed fixture', async ({ page, fixture1, fixture2 }) => {
     console.log(`from first fixture ${fixture1}`)
     console.log(`from second fixture ${fixture2}`)
  });

Answer №1

It appears that you may be utilizing fixtures like POMs and possibly overcomplicating your tests. If this approach suits your needs and aligns with your goals, then go ahead and use it. If my assumption is accurate, instead of passing fixtures to another fixture, consider passing POMs. You can even perform steps here to ensure a certain state is achieved for the page, as shown in this example from the playwright page:

// my-test.js
const base = require('@playwright/test');
const { TodoPage } = require('./todo-page');
const { SettingsPage } = require('./settings-page');

// Extend base test by providing "todoPage" and "settingsPage".
// This new "test" can be used in multiple test files, and each of them will get the fixtures.
exports.test = base.test.extend({
  todoPage: async ({ page }, use) => {
    // Set up the fixture.
    const todoPage = new TodoPage(page);
    await todoPage.goto();
    await todoPage.addToDo('item1');
    await todoPage.addToDo('item2');

    // Use the fixture value in the test.
    await use(todoPage);

    // Clean up the fixture.
    await todoPage.removeAll();
  },

  settingsPage: async ({ page }, use) => {
    await use(new SettingsPage(page));
  },
});
exports.expect = base.expect;

In your test, simply pass {todoPage} or {settingsPage}, or both:

const { test, expect } = require('./my-test');

test.beforeEach(async ({ settingsPage }) => {
  await settingsPage.switchToDarkMode();
});

test('basic test', async ({ todoPage, page }) => {
  await todoPage.addToDo('something nice');
  await expect(page.locator('.todo-item')).toContainText(['something nice']);
});

You can also chain your fixtures and reuse them. For example, you could pass todoPage to the settingsPage fixture:

  settingsPage: async ({ todoPage}, use) => {
    await use(new SettingsPage(page));
  },

This way, everything in todoPage will be executed before settingsPage, and this combined fixture is what you pass to your test, which I believe is what you were aiming for.

Answer №2

To increase efficiency, I implement the base fixture as a dependent fixture within a derivative fixture:

import { test as base } from "@playwright/test"
interface MyFixtures1 {
    fixture1: string
}

export const testBase = base.extend<{}, MyFixtures1>({
    fixture1: [
        async ({}, use) => {
            console.log("fixture1 setup once per worker")
            use("one")
            console.log("fixture1 teardown once per worker")
        },
        { scope: "worker" }
    ]
})

interface MyFixtures2 {
    fixture2: string
}

export const test = testBase.extend<MyFixtures2>({
    fixture2: async ({ fixture1 }, use) => {
        console.log("fixture2 setup for each test")
        use(`two-${fixture1}`)
        console.log("fixture2 teardown for each test")
    },
})

test("should allow me use composed fixture", async ({ fixture1, fixture2 }) => {
    console.log(`from first fixture ${fixture1}`)
    console.log(`from second fixture ${fixture2}`)
})

test("should base", async ({ fixture1 }) => {
    console.log(`from first fixture ${fixture1}`)
})

test("should derived", async ({ fixture2 }) => {
    console.log(`from second fixture ${fixture2}`)
})

For additional details on utilizing fixtures, refer to the official documentation

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Unable to programmatically uncheck a checkbox after it has been manually checked: Angular

After being selected through the UI by clicking on the checkbox, I am encountering an issue where I cannot unselect the checkbox programmatically. To see this behavior in action, visit the sample app, where you can click on the checkbox to select it and t ...

Guide on navigating to a specific page with ngx-bootstrap pagination

Is there a way to navigate to a specific page using ngx-bootstrap pagination by entering the page number into an input field? Check out this code snippet: ***Template:*** <div class="row"> <div class="col-xs-12 col-12"> ...

Angular 13 implementation of a double-loop structure for fetching data from an API

I'm facing an issue with retrieving specific data fields label and svm from a JSON file. The desired fields are nested inside PORTFOLIO > REGROUPEMENT > ELEMENT. You can access the JSON file here. img(1) I've attempted to display the dat ...

TypeScript failing to correctly deduce the interface from the property

Dealing with TypeScript, I constantly encounter the same "challenge" where I have a list of objects and each object has different properties based on its type. For instance: const widgets = [ {type: 'chart', chartType: 'line'}, {typ ...

Unleashing the potential of an endless animation by incorporating pauses between each iteration

I am trying to create an infinite animation using animate css but I want to add a delay between each iteration. After exploring various options, I first attempted to achieve this using plain JavaScript. Here is the HTML snippet: <div id="item" class= ...

The identifier 'id' is not recognized within the 'never' type in Angular

Hello there, I am currently working on a small project for a store website. You can find the project here. However, I have encountered an issue when trying to move items to the cart. Specifically, in the source code file app/components/product-list/produc ...

How to access an element through the router-outlet in Angular 6?

<side-navigation [navigationTitle]="navTitle"></side-navigation> <router-outlet> </router-outlet> Within my project, I have a navigation bar located in the root component. I have set up [navigationTitle] as an @Input Decorator wit ...

Typescript with Angular: Despite having 7 values in the map, Map.get is returning undefined

Why does Map.get always return undefined when using a number from a form element (extra1) in this code snippet? extraById = new Map<number,Extra>(); @Input() extra1: number = -1; formChanged(carConfigurationFormChanged : any) { const index ...

The implementation of CommonJS modules allows for efficient modularization

While using Nx for my Angular workspace, I noticed something that sparked a question in my mind. Why is it necessary to use CommonJS modules in all tsconfig.spec.json files for libs? Looking at Nx examples, I observed that not all libs include it, only app ...

The interconnectivity between ngAfterViewInit in Angular's LifeCycle and observables

enable.service.ts @Injectable({ providedIn: 'root' }) export class EnableService { isEnabled$ = from(this.client.init()).pipe( switchMap(() => this.client.getEnabled()), map(([enabled, isAdmin]) => ({enabled: true, isAdmin: fals ...

Wondering how to implement HubSpot Conversations SDK in a Typescript/Angular application?

Recently, I came across some useful javascript code on this website window.HubSpotConversations.widget.load(); window.HubSpotConversations.widget.refresh(); window.HubSpotConversations.widget.open(); window.HubSpotConversations.widget.close(); Now, I am l ...

Setting default property values in a React component using Typescript

   Is there a way to define default property values in React components when using Typescript? I came across a post on SE suggesting the use of a static class variable called defaultProps with key-value pairs for properties. However, this method didn&a ...

Unable to append item to document object model

Within my component, I have a snippet of code: isLoaded($event) { console.log($event); this.visible = $event; console.log(this.visible); this.onClick(); } onClick() { this.listImage = this.imageService.getImage(); let span = docu ...

Typescript is throwing an error with code TS2571, indicating that the object is of type 'unknown'

Hey there, I'm reaching out for assistance in resolving a specific error that has cropped up. try{ } catch { let errMsg; if (error.code === 11000) { errMsg = Object.keys(error.keyValue)[0] + "Already exists"; } return res.status ...

End the pipeline execution in a chain of RxJS observables

Here is a scenario where a series of Observables are chained together. Is it possible to prevent the subsequent chain from executing if an error is thrown by 'parentObs1'? import { throwError } from "rxjs"; import { mergeMap, catchError ...

Yarn Plug'n'Play was unable to locate the module during the next build

Currently, I am in the process of developing a Next.js project with yarn's Plug'n'Play feature. In this project, I have created several pages and added various packages, including mathjs: '^10.3.0' to assist me in parsing user inpu ...

Issue with closing the active modal in Angular TypeScript

Can you help me fix the issue with closing the modal window? I have written a function, but the button does not respond when clicked. Close Button: <button type="submit" (click)="activeModal.close()" ...

Postgres Array intersection: finding elements common to two arrays

I'm currently developing a search function based on tags, within a table structure like this CREATE TABLE permission ( id serial primary key, tags varchar(255)[], ); After adding a row with the tags "artist" and "default," I aim ...

Postpone the initial click action triggered by the specified directive

Is it possible to create a directive that prompts for confirmation when a button is clicked? This would involve storing the original event and only executing it once the user confirms their choice. A similar behavior has been mocked here: https://stackbl ...

Move information from one page to another

I'm currently using Ionic 2 and attempting to pass data from one page to another. Specifically, I want to transfer the data of a selected list item from the first page (quickSearch) to the second page (quickSearchDetail). To illustrate this, refer to ...