Can I loop the same test with different data?
Playwright before each for all spec files
node.js - Run grouped tests sequentially using Playwright - Stack Overflow
[Feature] Allow to set test.use options per single test
Videos
I want to create a checker inside our staging page for any inconsistent data displayed that we didn't spot before uploading the data.
Example:
List to check = an array
Loop:
Test:
Check this ( LIst to check [n] )
Return pass
I tried several of this approach before and keep getting timeouts.
Thank you in advance :)
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:
Use project dependency
Form 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
Use fixtures
Let 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
» npm install @playwright/test
Unfortunately, browsers/OSes have global state such as clipboard contents, so you'll get race conditions if testing such features in parallel.
If all your tests are running in parallel, you've probably enabled parallelism in playwright config:
const config: PlaywrightTestConfig = {
fullyParallel: true,
...
This is good - you should leave fullyParallel: true to execute your tests faster, and then opt-out of running the tests in a single file (or single describe block) serially by adding test.describe.configure({ mode: 'serial' }); like this:
import { test, expect } from '@playwright/test';
test.describe('Add a simple invoice test', () => {
test.describe.configure({ mode: 'serial' });
test('01.Login & add an invoice', async ({ page }) => {
await page.goto("https://someUrl.com");
await page.fill('input[id="email"]', "someEmailAddress");
await page.fill('input[ng-model="ctrl.user.password"]', "somePassword");
await page.click('button[id="login-btn"]');
});
test('02.Add an invoice', async ({ page }) => {
await page.click('[name="invoice"]');
await page.click('button[id="addInvoiceButton"]');
await page.click('a[ng-click="ctrl.goToAddInvoice()"]');
await page.fill('#invoiceTitle', Math.random().toString(36).substring(7));
await page.fill('#exampleInputAmount', "120");
await page.click("#invoiceCategory")
await page.fill("#invoiceCategory > input", "Car")
await page.keyboard.press("Enter");
await page.click('button[id="submitInvoiceButton"]');
});
});
Read more in the playwright docs here: https://playwright.dev/docs/next/test-parallel#parallelize-tests-in-a-single-file
The solution is very simple. It is executing as an independent test since you are passing {page} in each test, so if you want to use the same context for both the test you need to modify the test like below.
import { test, expect } from '@playwright/test';
test.describe('Add a simple invoice test', () => {
let page: Page;
test.beforeAll(async ({ browser }) => {
page = await browser.newPage();
});
test('01.Login & add an invoice', async () => { // do not pass page
await page.goto("https://someUrl.com");
await page.fill('input[id="email"]', "someEmailAddress");
await page.fill('input[ng-model="ctrl.user.password"]', "somePassword");
await page.click('button[id="login-btn"]');
});
test('02.Add an invoice', async () => { //do not pass page
await page.click('[name="invoice"]');
await page.click('button[id="addInvoiceButton"]');
await page.click('a[ng-click="ctrl.goToAddInvoice()"]');
await page.fill('#invoiceTitle', Math.random().toString(36).substring(7));
await page.fill('#exampleInputAmount', "120");
await page.click("#invoiceCategory")
await page.fill("#invoiceCategory > input", "Car")
await page.keyboard.press("Enter");
await page.click('button[id="submitInvoiceButton"]');
});
});
This should work as you expected You can also refer to the Dzone Article regarding the same.
Note: Playwright doesn't recommend this approach but this answer should fulfill your need.