The following test cases are written based on Ant Design Pro. Source: www.yuque.com/qhan/dufpgr…

Initial login

The simplest and most convenient login authentication is to newly initialize the login state for each test. Before running each test, run some browser automation code that performs user login and other packet operations.

This method is slow to execute. The reason for this is that if you have n(n>=0) tests, then you need to perform n(n>=0) logins. The benefit is that each test has a new login session, for example, if we create a new user and then log in for each test of that user, we can keep the tests completely isolated from each other.

independent

e2e/basic.e2e.spec.ts

import { test, expect } from '@playwright/test';

const BASE_URL = `http://localhost:${process.env.PORT || 8000}`;

test.describe('The sub page below the admin is rendered'.() = > {
  test.beforeEach(async ({ page }) => {
    await page.goto(`${BASE_URL}/user/login`);

    // Login authorization
    await page.waitForSelector('button[class="ant-btn ant-btn-primary ant-btn-lg"]');
    await page.fill('input[id="username"]'.'admin');
    await page.fill('input[id="password"]'.'ant.design');
    await page.click('button[class="ant-btn ant-btn-primary ant-btn-lg"]');
    await page.waitForNavigation(); // Redirect after login
  });

  test('Sub page render'.async ({ page }) => {
    await page.goto(`${BASE_URL}/admin/sub-page`);
    const title = page.locator('.ant-pro-page-container-content');
    await expect(title).toHaveText('This page can only be viewed with admin permission');
  });
});
Copy the code

These steps can be performed for each browser context. However, re-logging in for each test can slow down test execution. To prevent this, we will reuse the existing authentication state in the new browser context, as detailed in Reuse Login. It is also possible to wrap and abstract logins, but the code effort is reduced when writing test cases, but the test time is not optimized.

abstract

If we have multiple test files that rely on this login code, repeating the code in each file becomes a little cumbersome beforeEach(). We can solve the problem ourselves with the Page Object Model. The page object model allows us to create abstractions on Web application pages to reduce code duplication across multiple tests.

First, we create a user registration page object model:

tests/config.ts

export const BASE_URL = `http://localhost:${process.env.PORT || 8000}`;
Copy the code

tests/sign-page-model.ts

import type { Page } from '@playwright/test';

import { BASE_URL } from './config';

export class SignPage {
  readonly page: Page;

  constructor(page: Page) {
    this.page = page;
  }
  async login() {
    const { page } = this;
    await page.goto(`${BASE_URL}/user/login`);

    // Login authorization
    await page.waitForSelector('button[class="ant-btn ant-btn-primary ant-btn-lg"]');

    await page.fill('input[id="username"]'.'admin');
    await page.fill('input[id="password"]'.'ant.design');
    await page.click('button[class="ant-btn ant-btn-primary ant-btn-lg"]');
    awaitpage.waitForNavigation(); }}Copy the code

Simplified test files

import { test, expect } from '@playwright/test';
import { SignPage } from '.. /.. /tests/sign-page-model';
import { BASE_URL } from '.. /.. /tests/config';

test.describe('The sub page below the admin is rendered'.() = > {
  test('Sub page render'.async ({ page }) => {
    const signIn = new SignPage(page);
    await signIn.login();
    await page.goto(`${BASE_URL}/admin/sub-page`);
    const title = page.locator('.ant-pro-page-container-content');
    await expect(title).toHaveText('This page can only be viewed with admin permission');
  });
});
Copy the code

Of course, this refactoring only starts to pay off when we have multiple test files.

Reuse the login

Log on globally once and reuse the state in tests.

Global setting authentication or application status must be defined in the ourselves configuration file with a global setting script that runs first before any tests. In the script, automatically perform the browser into the desired state (such as a login), and any interaction, and then get all the cookies and storage (local) and session storage data, and through the browserContext. StorageState () method to retrieve from the authenticated context storage state, Then create a new context with a pre-populated state.

tests/global-setup.ts

import { chromium } from '@playwright/test';

import { SignPage } from './sign-page-model';

async function globalSetup() {
  const browser = await chromium.launch();
  const context = await browser.newContext();
  const page = await context.newPage();

  const sign = new SignPage(page);
  await sign.login();

  await context.storageState({ path: './tests/.tmp/state.json' });
  await browser.close();
}
export default globalSetup;
Copy the code

playwright.config.ts

// playwright.config.ts
...
const config: PlaywrightTestConfig = {
+ globalSetup: require.resolve('./tests/global-setup'),forbidOnly: !! process.env.CI, retries: process.env.CI ? 2 : 0, use: { trace: 'on-first-retry',+ storageState: './tests/.tmp/state.json',},... }; export default config;Copy the code

Test cases:

import { test, expect } from '@playwright/test';
import { BASE_URL } from '.. /.. /tests/config';

test.describe('The sub page below the admin is rendered'.() = > {
  test('Sub page render'.async ({ page }) => {
    // Already logged in, can do whatever you want to do
    // We implement our test goals and assertions here as the logged-in user
  });
});
Copy the code

Each test will now use the same user login session.

Others: When there is different state data in different contexts, you can use the test.use() method to set the storageState configuration options in the test column, as shown in the following example:

import { test, expect } from '@playwright/test';
import { BASE_URL } from '.. /.. /tests/config';

test.describe('The sub page below the admin is rendered'.() = > {
  test.use({ storageState: './tests/.tmp/state.json' });
  test('Sub page render'.async ({ page }) => {
    // Already logged in, can do whatever you want to do
    // We implement our test goals and assertions here as the logged-in user
  });
});
Copy the code

The session

Thinking???

internationalization

In ourselves, the offender may start in English by default, and we may need to make some language adjustments when it comes to international projects.

playwright.config.ts

// playwright.config.ts
...
const config: PlaywrightTestConfig = {
  use: {
  	 ...
+ locale: 'zh-cn ', // internationalization processing, default English},... }; export default config;Copy the code

Reference:

playwright.dev/docs/auth