Writing Playwright UI Test
Tags: starchart, senecaosd700, playwright
Published: 2023-04-17 23:32:11

As Starchart is getting close to the launch, we have more UI changes than before. So I needed to learn how to write Playwright UI test for my recent PRs. 

![playwright test report](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3xl3q2ib5rdz0in6a1hw.png)

Playwright is a versatile UI testing frameworks which works on any browser and any platform. I used Selenium and Cypress before. In my opinion, Playwright's multi-browser support is easy to set up and the the way it creates a report that is easy to understand and debug. I really like that Playwright retries failed tests and sets a flaky flag even if it passes in another try. And you can run tests in parallel that reduces the deployment time on CI pipeline. 

![failed test debugging](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mn2vjyft9uiuiml1minl.png)

### How to set up Playwright
You just need to run this command to install Playwright in your project. 
```sh
npm init playwright@latest
```
Then you will have `playwright.config.ts` file and `tests` folder in your project root folder. 

To run the test, 
```sh
npx playwright test
```

To see the test reports,
```sh
npx playwright show-report
```

### Writing Playwright test


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

test.describe('Certificate Page', () => {
  test.beforeEach(async ({ page }) => {
    await page.goto('/certificate');  //Pre-condition for each test
  });

  test('Request a Certificate', async ({ page }) => {
    const titleHeader = page.getByRole('heading', { name: 'Certificate' });
    const domainName = page.getByText('user1.starchart.com');

    await expect(domainName).toContainText('user1.starchart.com');
    await expect(titleHeader).toContainText('Certificate');
  });
});
```

You can set up a suite of tests by declaring `test.describe('...')`. Everything inside this function is a subset of this test suite. 

And often you have to set up pre-condition or clear what certain test has changed. In this case, you can use `test.beforeAll`, `test.beforeEach`, `test.afterAll`, `test.beforeEach`. 

Then the actual test goes with the tile of the test. 
```js
test('Request a Certificate', async ({ page }) => {
```

To test UI, you need to select UI element for action or assertion. There are multiple ways of selecting a certain UI element. When it is looking for fixed content in the UI component, [Frame Locator](https://playwright.dev/docs/api/class-framelocator) is easy to write and read tests. And you can use [filter locators](https://playwright.dev/docs/locators#filtering-locators) when you have multiple elements from Frame Locator. 
When the content in a UI element is dynamic, you can also use [CSS locator](https://playwright.dev/docs/other-locators). 


### Codegen Test Generator
You can simply let Playwright generate tests by using `Codegen`. Run this command in your terminal. You don't have to add an URL here. 

```sh
npx playwright codegen [URL]
```

It will open up a browser. Every action you make to the browser will generate test code on the other window. 

![codegen screen](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6gdx0vksw0bsrr93zt8u.png)

You can use Codegen in VS Code. [Generating Tests](https://playwright.dev/docs/getting-started-vscode#generating-tests)

### Playwright Configuration
In your `playwright.config.ts` file, import `PlaywrightTestConfig` type and `devices`. Add config setting in the object. 

```js
import type { PlaywrightTestConfig } from '@playwright/test';
import { devices } from '@playwright/test';

const config: PlaywrightTestConfig = {
  testDir: './test/e2e',  // Specify Playwright test folder
  timeout: 30 * 1000,  // Timeout for single test
  expect: {
    timeout: 5000,  // Timeout for expect()
  },
  fullyParallel: true,  // Run tests in parallel
  retries: 2,  // Retry set
  workers: 1,  // Number of workers running test
  reporter: 'html',  // Test report format
  use: {
    actionTimeout: 0,  // Time limit for action (0 is no limit)
    baseURL: 'http://localhost:8080',  //Base URL setup
    trace: 'on-first-retry',  // Trace condition set
    video: 'on-first-retry',  // Recording condition set
  },
  // Browser setting
  projects: [  
    {
      name: 'Google Chrome',
      use: {
        channel: 'chrome',
      },
      dependencies: ['setup'],
      testIgnore: /.*\.mobile\.spec\.ts/,
    },
    {
      name: 'Mobile Safari',
      use: {
        userAgent: devices['iPhone 12'].userAgent,
        viewport: devices['iPhone 12'].viewport,
        deviceScaleFactor: devices['iPhone 12'].deviceScaleFactor,
        isMobile: false,
        hasTouch: devices['iPhone 12'].hasTouch,
        defaultBrowserType: devices['iPhone 12'].defaultBrowserType,
      },
      dependencies: ['setup'],
      testIgnore: /.*\.desktop\.spec\.ts/,
    },
  ]
```

### Conclusion
There are many other features to introduce. You can emulate browser setting such as locale, timezone, viewport, etc., as well as mock API, set a fixture. Official documentation is always the best place to start with and you can learn more details about features and ask questions to solve your problems. And there are always multiple framework choices for a certain purpose. I think it's good to try a different framework in your project to compare features and learn more technology from it. I think Playwright is a good choice for your UI testing.