avatarNeo Luo

Summary

This context outlines the process of implementing automated UI testing using Playwright in an Ant Design CMS project.

Abstract

The provided text is a comprehensive guide on how to integrate Playwright, a robust end-to-end testing tool, with an Ant Design-based CMS project. It begins by emphasizing the necessity of UI testing for modern web development, particularly when using React UI libraries like Ant Design. The article then proceeds with step-by-step instructions on creating a CMS project, setting up Playwright, writing test cases for common use cases such as login, and using mock data to ensure test stability. It also covers advanced testing features like UI mode debugging, snapshot testing for visual regression, and setup methods for efficient reuse of test states, ultimately ensuring that the CMS remains reliable and scalable as it grows.

Opinions

  • The author believes that manual testing is impractical for large-scale applications, advocating for automated UI testing with Playwright to maintain application quality.
  • The use of @ant-design/pro-cli for quickly scaffolding an Ant Design project is highly recommended for ease of development.
  • Storing user information in a JSON file after login is suggested to streamline the testing process, with the recommendation to ignore this file in version control systems like Git.
  • Mocking API responses is presented as a best practice to ensure stable and consistent test results, independent of backend implementations.
  • Snapshot testing is encouraged to quickly identify UI changes, with instructions on how to update snapshots when the UI is intentionally modified.
  • The use of Playwright's setup method for authentication is highlighted as a valuable technique to avoid redundancy in test cases and to maintain a clean and DRY (Don't Repeat Yourself) testing approach.

React:CMS Project Using Playwright

Introduction In modern web development, it is crucial to ensure the reliability and responsiveness of the user interface. Ant Design, a popular React UI library, provides a solid and beautiful foundation for building complex interfaces. However, as the application grows, manual testing becomes unrealistic. At this time, Playwright, a powerful end-to-end testing tool, becomes particularly important. By automating UI testing with Playwright, we can ensure that the quality and integrity of the application are maintained with every code change.

This article will guide you how to configure Playwright in your Ant Design project

Quickly create a CMS project

Use the popular CMS framework: antd pro, you can quickly create a front-end project through the scaffolding provided by the framework, install the antd pro scaffolding globally and create a project

  npm i @ant-design/pro-cli -g
  pro create antd-playwright

  🚀 Do you want the full amount or a simple scaffolding? (use arrow keys)
❯ simple
  complete

Create a successful installation project dependency

 cd antd-playwright
 npm install

Then start the project

 npm run start

Then open the browser and visit: http://localhost:8000, the login page will be displayed

Login account and password:

  • username : admin
  • password : ant.design

You will need your account and password to log in to playwright later.

Install Playwright

Install Playwright dependencies

npm install --save-dev playwright

If you are using TypeScript, you also need to install @playwright/test as the testing framework:

npm install --save-dev @playwright/test

Create a configuration file playwright.config.ts

// playwright.config.ts

import { defineConfig, devices } from "@playwright/test";

export default defineConfig({
  testDir: "./tests",
  use: {
    baseURL: "http://localhost:3000",
  },
  projects: [
    {
      name: "chromium",
      use: {
        ...devices["Desktop Chrome"],
      },
    },
  ],
});

Now write a use case to complete the login

// tests/login.spec.ts

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

test("login cms", async ({ page }) => {
  await page.goto("http://localhost:8000");
  await page.fill("#username", "admin");
  await page.fill("#password", "ant.design");
  const button = await page.locator("button.ant-btn");
  await button.click();
  await expect(page).toHaveURL("http://localhost:8000/welcome");
  await page.waitForSelector("text=Welcome"); // The home page loaded when text about "wait welcome" is appearance
});

Run playwright test

 npx playwright test

Run successfully:

During the first run, you will be prompted to install playwright:

  ╔═════════════════════════════════════════════════════════════════════════╗
    ║ Looks like Playwright Test or Playwright was just installed or updated. ║
    ║ Please run the following command to download new browsers:              ║
    ║                                                                         ║
    ║     npx playwright install                                              ║
    ║                                                                         ║
    ║ <3 Playwright Team                                                      ║
    ╚═════════════════════════════════════════════════════════════════════════╝

run

npx playwright install

npx will install the environment required for Playwright to run into the cache file, thus avoiding dependency version conflicts caused by different project Playwright versions

auth.setup

Since many tests for cms require login, you can use setup to perform the login operation in advance. Subsequent use cases can set the login state through dependencies setup to avoid repeated writing of login functions.

Login encapsulation into auth.setup

// tests/auth.setup.ts

import { test as setup, expect } from "@playwright/test";

const authFile = "playwright/.auth/user.json";

setup("authenticate", async ({ page }) => {
  await page.goto("http://localhost:8000/user/login");
  await page.fill("#username", "admin");
  await page.fill("#password", "ant.design");
  const button = await page.locator("button.ant-btn");
  await button.click();

  await expect(page).toHaveURL("http://localhost:8000/welcome");

  await page.waitForSelector("text=Welcome"); // 等待 welcome 页面的标志性元素
  await page.context().storageState({ path: authFile });
});

Here is the file: playwright/.auth/user.json, to store user information after successful login, such as: cookies, tokens, etc. after successful login, you need to manually create the folder playwright/.auth

mkdir -p playwright/.auth
echo $'\nplaywright/.auth' >> .gitignore

You need to set ignore in git to avoid using the same login state when generating or running test cases in the team

Modify playwright.config.ts configuration

// playwright.config.ts

import { defineConfig, devices } from "@playwright/test";

export default defineConfig({
  testDir: "./tests",
  use: {
    baseURL: "http://localhost:3000",
  },
  projects: [
+    {
+            name: 'setup',
+            testMatch: /auth\.setup\.ts/,
+     },
    {
      name: "chromium",
      use: {
        ...devices["Desktop Chrome"],
+         storageState: 'playwright/.auth/user.json'
      },
+      dependencies: ['setup'],
    },
  ],
});

Create a new test case to view the home page after successfully logging into the cms, and simulate clicking on the nav search Tableto display Enquiry Form

// tests/home.spec.ts

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

test("enquiry table", async ({ page }) => {
  await page.goto("http://localhost:8000/welcome");
  const enquiryNavItem = await page.locator(
    '.ant-pro-base-menu-inline-item-text:has-text("Search Table")'
  );
  await enquiryNavItem.click();

  const textLocator = page.locator(
    '.ant-pro-table-list-toolbar-title:has-text("Enquiry Form")'
  );
  await expect(textLocator).toBeVisible();
});

Run successfully:

Running 2 tests using 1 worker

  ✓  1 [setup] › auth.setup.ts:5:6 › authenticate (4.7s)
  ✓  2 [chromium] › home.spec.ts:5:5 › enquiry table (2.5s)

  2 passed (9.0s)

UI-mode

When some use cases need debugging, you can turn on UI mode, the main instructions

 npx playwright test --ui

The green triangle on the left is for startup and test cases, where you can choose to start setup or test cases

Click on the use case to see the specific execution

Mock interface return data

In order to ensure the stability of the front-end page test, it is very important to keep the interface with the same data output. You can use mock to keep the interface always returning the same data.

Use the page.route method to mock the interface: http://localhost:8000/api/currentUser?token=xxx , mainly modify the name in the response to “neo-luo”

// tests/home.spec.ts
const currentUserMock = {
    "success": true,
    "data": {
-      "name" : "Serati Ma"
+      "name" : "neo-luo"
        ...
    }
}

test('current user', async ({ page }) => {

    await page.route('*/**/api/currentUser*', async (route) => {
        await route.fulfill({
            status: 200,
            contentType: 'application/json',
            body: JSON.stringify(currentUserMock),
        });
    });

    await page.goto('http://localhost:8000/welcome');
    await expect(page.getByTitle('username')).toHaveText('neo-luo')

});

page.getByTitle is used here, and you need to add title to the corresponding code tag

// src/components/RightContent/AvatarDropdown.tsx

...
  return (
    <HeaderDropdown
      menu={{
        selectedKeys: [],
        onClick: onMenuClick,
        items: menuItems,
      }}
    >
-     {children}
+     <span title="username">{children}</span>
    </HeaderDropdown>
  );

View in UI modal mode, mock data is successful

In order to make the code look concise and facilitate other test cases to use mock data, you can store it in a json file

// tests/mocks/currentUser.json

{
  "success": true,
  "data": {
    "name": "neo-luo",
    "avatar": "https://gw.alipayobjects.com/zos/antfincdn/XAosXuNZyF/BiazfanxmamNRoxxVxka.png",
    "userid": "00000001",
    "email": "[email protected]",
    "signature": "海纳百川,有容乃大",
    "title": "交互专家",
    "group": "蚂蚁金服-某某某事业群-某某平台部-某某技术部-UED",
    "tags": [
      {
        "key": "0",
        "label": "很有想法的"
      },
      {
        "key": "1",
        "label": "专注设计"
      },
      {
        "key": "2",
        "label": "辣~"
      },
      {
        "key": "3",
        "label": "大长腿"
      },
      {
        "key": "4",
        "label": "川妹子"
      },
      {
        "key": "5",
        "label": "海纳百川"
      }
    ],
    "notifyCount": 12,
    "unreadCount": 11,
    "country": "China",
    "access": "admin",
    "geographic": {
      "province": {
        "label": "浙江省",
        "key": "330000"
      },
      "city": {
        "label": "杭州市",
        "key": "330100"
      }
    },
    "address": "西湖区工专路 77 号",
    "phone": "0752-268888888"
  }
}

Current user use case code modification

// tests/home.spec.ts

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

test('current user', async ({ page }) => {

+    const mockCurrentUserFilePath = path.resolve(__dirname, './mocks/currentUser.json');
+    const mockCurrentUserResponse = JSON.parse(fs.readFileSync(mockCurrentUserFilePath, 'utf-8'));

    await page.route('*/**/api/currentUser*', async (route) => {
        await route.fulfill({
            status: 200,
            contentType: 'application/json',
            body: JSON.stringify(mockCurrentUserResponse),
        });
    });

    await page.goto('http://localhost:8000/welcome');

    await expect(page.getByTitle('username')).toHaveText('neo-luo')

});

screenshot

Snapshots can quickly record the overall structure and style of a page. If there are changes in the style, it will not be accepted. Write a home page snapshot use case

// tests/home.spec.ts
+ test('home screenshot', async ({ page }) => {
+    await page.goto('http://localhost:8000/welcome');
+    await expect(page).toHaveScreenshot({
+        fullPage: true,
+    });
+ });

Here fullPage = true, which means capturing the entire screen. The default is fullPage = false, which captures the current visible range. Since there is no snapshot for comparison during the first run, an error will be reported. You can pass the second run.

Now modify Ant Design Pro in the home page

Change to Hello World

Run the test case again: home screenshot. The differences will be marked. For specific error information, see the xxxdiff.png image in /test-results/home-screenshot-chromium.

  • If you need to make the modified page a new snapshot, you can add -u to the test command.
 npx playwright test -u
  • If you update the specified file snapshot (path/to/test-file.spec.ts is the test case file name that needs to be updated)
 npx playwright test path/to/test-file.spec.ts  -u
  • Update the specific use case (snapshot test is the use case name)
 npx playwright test -g 'snapshot test'  -u

in conclusion

Using Playwright in a CMS project built with Ant Design Pro simplifies the process of automating UI tests and maintaining the reliability of your application. The ability to quickly create an Ant Design project using the provided CLI makes it easy to get started with development. Adding Playwright as a testing framework ensures that, as your project scales, you can maintain quality through robust testing strategies.

By configuring Playwright with reusable authentication states, mocking API responses, and verifying core interactions in the CMS interface, such as login and navigation, you ensure that essential features function correctly across various scenarios. Snapshot testing further enhances this by allowing you to capture the entire UI’s state, ensuring that any visual or structural changes are easily identified.

Mocking APIs using page.route() guarantees that tests are stable and not reliant on backend changes. If your UI evolves or new features are introduced, updating snapshots allows you to adapt the testing process efficiently. Overall, integrating Playwright ensures that even complex CMS systems remain stable and performant as you iterate and expand.

By using the setup method to handle authentication, combined with Playwright’s flexible API for interactions and assertions, this testing solution becomes invaluable in keeping your CMS project bug-free and scalable.

Reactjs
Playwrights
CMS
Ci Cd Pipeline
Testing
Recommended from ReadMedium