Skip to content

Latest commit

 

History

History
205 lines (170 loc) · 5.86 KB

File metadata and controls

205 lines (170 loc) · 5.86 KB

Playwright

Docs:

Why:

  • Support for all browsers
  • Fast and reliable execution
  • Powerful automation capabilities
  • Integrates with your workflow

Comparison to Cypress

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.

Examples

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);
};