I am 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?
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.
})
There two aproaches:
project dependencyForm Playwright 1.31 with project dependency you can set test what will be executed before any other tests and pass i.e. browser storage between tests.
See working and tested example (entering google search page and accepting policies, and then entering this page again with cookies):
playwright.config.ts:
import { PlaywrightTestConfig } from "@playwright/test";
import path from "path";
//here you save session
export const STORAGE_STATE = path.join(__dirname, 'login.json')
const config: PlaywrightTestConfig = {
timeout: 10 * 1000,
expect: {
timeout: 3 * 1000,
},
testDir: './tests',
use:{
baseURL: 'https://google.com',
trace: 'retain-on-failure',
video: 'retain-on-failure',
},
// here we set main project and dependent one
projects: [
{
name: 'login',
grep: /@login/
},
{
name: 'depend e2e',
grep: /@e2e/,
dependencies: ['login'],
use: {
storageState: STORAGE_STATE
}
}
]
};
export default config;
tests/example.spec.ts:
import { test} from '@playwright/test';
import { STORAGE_STATE } from '../playwright.config';
test('login to service @login', async({page}) => {
await page.goto('/');
// below example is to reject cookies from google
await page.waitForLoadState('domcontentloaded');
await page.keyboard.press('Tab');
await page.keyboard.press('Enter');
await page.getByRole('menuitem', { name: "English (United States)" }).press('Enter');
await page.getByRole('button', { name: 'Reject all' }).click();
// Save storage:
await page.context().storageState({path: STORAGE_STATE})
})
test('logged in test @e2e', async ({page}) => {
await page.goto('/');
await page.waitForLoadState('domcontentloaded');
// BOOM - you are in!
// Screenshot shows that settings regarding cookies were saved
await page.screenshot({ path: 'screenshot.png' })
})
Video with example: https://www.youtube.com/watch?v=PI50YAPTAs4&t=286s
In above example I'm using tags to identify tests: https://playwright.dev/docs/test-annotations#tag-tests
fixturesLet test entering google search page and accepting policies
In file fixtures.ts:
import { test as base } from "@playwright/test";
export const test = base.extend({
page: async ({ baseURL, page }, use) => {
// We have a few cases where we need our app to know it's running in Playwright.
// This is inspired by Cypress that auto-injects window.Cypress.
await page.addInitScript(() => {
(window as any).Playwright = true;
});
await page.goto("/");
// below example is to reject cookies from google
await page.waitForLoadState("domcontentloaded");
await page.keyboard.press("Tab");
await page.keyboard.press("Enter");
await page
.getByRole("menuitem", { name: "English (United States)" })
.press("Enter");
await page.getByRole("button", { name: "Reject all" }).click();
use(page);
},
});
export { expect } from "@playwright/test";
And then in test use new test object instead importing it form "@playwright/test"
import { test, expect } from "../fixture";
test("logged in test @e2e", async ({ page }) => {
await page.goto("/");
await page.waitForLoadState("domcontentloaded");
// BOOM - you are in!
// Screenshot shows that it works
await page.screenshot({ path: "screenshot.png" });
});
Inspired by: https://github.com/microsoft/playwright/issues/9468#issuecomment-1403214587
If you need example with global request intercepting see here: https://stackoverflow.com/a/76234592/1266040
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With