Docs:
Why:
- Support for all browsers
- Fast and reliable execution
- Powerful automation capabilities
- Integrates with your workflow
Playwright is simialar to Cypress in it's API and if you know Cypress, you can easily learn Playwright.
Some benefirts of Playwright over Cypress:
- Speed, this issue cypress-io/cypress#22868 killed our test suites - now it has been fixed (I think) but I haven't tested it (because we already ported our test suite to Playwright)
- TypeScript support has always been better in Playwright, I believe it still is
- Playwrights explicit async is better than Cypress implicit async which has caused pain here and there
- Playwrights Locator makes it possible to do user centric testing without using cypress-testing-library (https://playwright.dev/docs/locators)
- Playwrights dev tools are ok and do the job, I liked Cypress test runner more though
- Configuring and extending Playwright has been easier than Cypress
- Playwrights multi browser support has always been better, although Cypress has improved it lately
- Video / trace recording is easy and fast
- CI/CD integration is easy
- VSCode plugin is available and tests can be run from the editor
- Playwrights second mover advantage, it has always done the same things as Cypress but better
- Cypress also has evolved and e.g. there's new methods to clear cookies now
- There are big differences in Cypress and Playwrights API, how to do "click this button" and "take the third button and click it". Cypress suggests to use data-attributes to find elements, Playwright suggests to use locators and "how user finds elements" with labels and placeholders.
https://playwright.dev/docs/api-testing https://playwright.dev/docs/auth
Login via API:
const apiContext = await playwright.request.newContext({
baseURL: URL,
});
const response = await apiContext.post(`/api/auth/login`, {
data: {
email: loginMail,
password: loginPassword,
recaptcha: "something-something",
},
});
expect(response.ok()).toBeTruthy();
const data = await response.json();
console.log("data:", data);localStorage setup before test:
auth.context.js
import fs from "fs";
import { test as base } from "@playwright/test";
import { STORAGE_STATE } from "../playwright.config";
export const test = base.extend({
context: async ({ context }, use) => {
const localStorage = JSON.parse(fs.readFileSync(STORAGE_STATE, "utf-8"));
await context.addInitScript((storage) => {
storage.origins.forEach((origin) => {
origin.localStorage.forEach((item) => {
window.localStorage.setItem(item.name, item.value);
});
});
}, localStorage);
use(context);
},
});and then
const { expect } = require('@playwright/test')
import { test } from './auth.context'
...auth.setup.js
import { test as setup } from "@playwright/test";
require("dotenv").config({ path: ".env" });
import { STORAGE_STATE } from "../playwright.config";
const { MAIL, PASSWORD, URL, API_URL } = process.env;
import {
storeLocalStorageState,
getLocalStorageFromState,
} from "./storageUtils";
setup("authenticate", async ({ page }) => {
await page.goto(`${URL}/login`);
const response = await page.request.post(`${API_URL}/auth/login`, {
data: {
email: MAIL,
password: PASSWORD,
recaptcha: "something-something",
},
});
await page.context().storageState({
path: STORAGE_STATE,
});
const data = await response.json();
await storeLocalStorageState(page, data, page.context());
const localStorage = await getLocalStorageFromState();
await page.context().addInitScript((storage) => {
storage.origins.forEach((origin) => {
if (origin === "http://localhost:8080") {
origin.localStorage.forEach((item) => {
window.localStorage.setItem(item.name, item.value);
});
}
});
}, localStorage);
});playwright.config.js
export const STORAGE_STATE = path.join(__dirname, "playwright/.auth/user.json");
{
...
projects: [
// Setup project
{ name: "setup", testMatch: /.*\.setup\.js/ },
{
name: "chromium",
use: {
...devices["Desktop Chrome"],
...(process.env.PWDEBUG && {
launchOptions: {
devtools: true,
},
}),
storageState: STORAGE_STATE,
},
dependencies: ["setup"],
},
}Storage utils:
import fs from "fs";
import { STORAGE_STATE, SESSION_STORAGE_STATE } from "../playwright.config";
export const storeLocalStorageState = async (page, data, browserContext) => {
await page.evaluate((data) => {
localStorage.setItem("token", data.response.token.token);
localStorage.setItem("refresh_token", data.response.token.refresh_token);
localStorage.setItem("secret", data.response.token.user.user_hash);
}, data);
await browserContext.storageState({
path: STORAGE_STATE,
});
};
export const getLocalStorageFromState = async () => {
return JSON.parse(fs.readFileSync(STORAGE_STATE, "utf-8"));
};
export const storeSessionStorageState = async () => {
// Get session storage and store as env variable
const sessionStorage = await page.evaluate(() =>
JSON.stringify(sessionStorage)
);
fs.writeFileSync(
SESSION_STORAGE_STATE,
JSON.stringify(sessionStorage),
"utf-8"
);
};
export const getSessionStorageState = async () => {
// Set session storage in a new context
const sessionStorage = JSON.parse(
fs.readFileSync(SESSION_STORAGE_STATE, "utf-8")
);
await context.addInitScript((storage) => {
if (window.location.hostname === "dev.local") {
for (const [key, value] of object.entries(storage))
window.sessionStorage.setItem(key, value);
}
}, sessionStorage);
};