Starchart: Shared sessions in Playwright
Published: 2023-03-13 10:14:12

Last week, I worked on [setting up shared authentication for end-to-end Playwright tests in starchart](https://github.com/DevelopingSpace/starchart/issues/296). This was necessary to avoid having to login before every test. The [Playwright docs](https://playwright.dev/docs/auth) suggest a few different ways of doing this, such as a shared account for all tests, or setting up multiple roles. 

I went with a similar setup to the setup you would have for multiple roles with a shared account across all tests that require login. Currently, all the pages do not differentiate between roles, i.e., they do not show different things based on the role. However, a design for an admin dashboard was being worked on, and will be implemented in the upcoming weeks. So, I decided to use a setup that would be easy to expand for different roles such as an `admin` user.

I started by adding a setup file under my e2e tests folder which would go through the login process, and save the authenticated state in a file:
```ts
import { test as setup } from '@playwright/test';
const userFile = 'test/e2e/.auth/user.json';

setup('authenticate as user', async ({ page }) => {
  ...
  // Login steps

  await page.context().storageState({ path: userFile });
});
```
Then I modified playwright config and added a new project that will be a dependency of all other projects. A project has a name and defines configuration for running the tests on a specific device in this case:

```ts
projects: [
  {
    name: 'setup',
    testMatch: /.*\.setup\.ts/,
  },
  {
    name: ...,
    use: {
      ...devices['Desktop Chrome'],
    },
    dependencies: ['setup'],
  },
  ... other projects
```

By setting setup as a dependency, it will be run before the tests in the project are run. `testMatch` defines a regex for the setup file(s), and it matches paths relative to the test directory defined in the config.

Since the setup process is run before the tests are run, we actually don't need to commit our file with the saved session to git. Thus, I added it to the `.gitignore` file.

I also [added tests for the `New DNS Record form`](https://github.com/DevelopingSpace/starchart/pull/313). This form is used by a user to create a new `DNS Record`. Since this required a user to be logged in, we can use the saved session state generated in the `setup` project:

```ts
test.describe('authenticated as user', () => {
  loggedInAsUser();
```
Here, `loggedInAsUser()` is a util function that was added based on PR reviews:
```ts
export function loggedInAsUser() {
  return test.use({ storageState: 'test/e2e/.auth/user.json' });
}
```
`test.use` can be specified in a `describe` block or in a test file. It is not necessary to use this in a `beforeEach` or `beforeAll`, and doing so would result in an error.

I ran the tests locally, and they all passed. However, I encountered a strange error in CI (which uses GitHub Actions) for the `Mobile Safari` project:
```
Test timeout of 30000ms exceeded.

page.goto: Navigation failed because page was closed!
``` 
Playwright generates a report containing a video and a trace on the first retry, depending on the configuration. I looked at the video and saw that most of it was a blank black screen. After trying a bunch of things such as upgrading playwright, and commenting out all other projects, I found [this open issue on the playwright project](https://github.com/microsoft/playwright/issues/11812). There was a [super helpful comment](https://www.github.com/microsoft/playwright/issues/11812#issuecomment-1462829766) left on the issue with a temporary workaround. Basically, setting `isMobile` to false for the `iPhone` configuration used by the `Mobile Safari` project results in the CI passing on GitHub actions.