'Playwright before each for all spec files

I very new to Playwright. Due to my test suites, I need to login into my application before running each test. Inside a single spec file that is easy, I can simply call test.beforeEach. My issue is: I need to before the login before each test of each spec file.

test.describe('Test', () => {
    //I need to make the code inside this beforeEach a exported 
    //function to call inside the before each of every spec file I have
    test.beforeEach(async ({ page }) => {
        await page.goto('/login');
        await page.click('text=Log in with Google account');
        await page.fill('id=identifierId', LoginAutomationCredentials.USER);
        await page.click('button[jsname="LgbsSe"]');
        await page.fill('input[type="password"]', LoginAutomationCredentials.PASSWORD);
        await page.click('button[jsname="LgbsSe"]');
        const otp = authenticator.generateToken(LoginAutomationCredentials.TOKEN);
        await page.fill('id=totpPin', otp);
        await page.click('button[jsname="LgbsSe"]');
    });

    it('Some description', async ({ page }) => {
        await page.goto('/foo');
        const dateFilter = await page.inputValue('input[placeholder="2011/03/02"]');
        expect(dateFilter).toBe('2021/12/07');
    });
});

I tried simply taking that code and and making it a function inside a separate .ts file and then importing it, but I figured the context is needed in order to do this. This is probably something every tester that uses playwright knows and uses regularly, however, I did not find anything on the subject.

How can I avoid copying the entire code of beforeEach and pasting it to all my spec files? How can I make it a function and call it whenever I want?



Solution 1:[1]

Use fixtures.

fixture.js:

const base = require('@playwright/test')
const newTest = base.test.extend({
    login: async({page}, use) => {
        await login();
        await use(page); //runs test here
        //logic after test
    }
})
exports.newTest = newTest
exports.expect = newTest.expect

Then in your tests:

const {newTest} = require('fixture.js')
newTest('mytest', async ({login}) => {
    //test logic
    login.goto(''); // using login here since I pass it as page in the fixture.
})

Solution 2:[2]

Fixtures is the right way, but there is a better option than having to remember to use login instead of page when you want to login. This is how I do it...

First I have this in playwright/src/index.ts where I setup all the fixtures for my project:

import { test as base_test, expect } from 'playwright_test';

type TestFixtures = {
  user: string;
};

export const test = base_test.extend<TestFixtures>( {
  user : '[email protected]',
  async context( { user, context, request }, use ) {
    // This makes a REST request to the backend to get a JWT token
    // and then stores that token in the browsers localStorage,
    // but you could replace this with whatever makes sense for auth
    // in your app
    if ( user ) {
      const content = await getAuthScript( user, request );
      await context.addInitScript( { content } );
    }
    await use( context );
  },
} );

/**
 * This makes a REST request to the backend to get a JWT token
 * and then stores that token in the browsers localStorage,
 * but you could replace this with whatever makes sense for auth
 * in your app.
 */
async function getAuthScript( user, request ) {
  const res = await request.post( '/api/test/auth', { data : { user } } );
  const { token } = await res.json();
  return `window.localStorage.setItem( 'jwt-token', "${token}" );`;
}

export { test, expect }

I also make sure that playwright/tsconfig.json includes this:

{
  "extends": "../path/to/base/tsconfig.json",
  "compilerOptions": {
    "noEmit": true,
    "paths": {
      "~": [ "./src" ],
      "~*": [ "./src/*" ]
    },
    "baseUrl": ".",
    "rootUrl": ".",
  },
  "include": [
    "src/**/*.ts",
    "test/**/*.ts"
  ],
}

Now every test will automatically login as [email protected], but if you need a test to login as a different user all you need to do in that test file is:

import { test, expect } from '~';

test.use( { user : '[email protected]' } );
test( 'can login as somebody-else', async ( { page } ) => {
  // Run your tests here...
} );

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Inkssz
Solution 2 Jason Kohles