Back to blog overview

December 16, 2022

How to Playwright II

Eddie Gomez



Software Engineer
San Diego, CA

### Recap

In [part I,]( we learned how to setup and write our first test using playwright.
We'll continue on and setup a global config to save a user's session for writing more complex tests. For example; Our [Redwood Template App]( has user's with different roles. We would like to test the page functionally depending on what privileges a user has.

### Advance Configuration

In `playwright.config.ts` - `PlaywrightTestConfig` we are given the option to add a `globalSetup` pointing to the global setup file path. The file will run before all tests.

For example

const config: PlaywrightTestConfig = {
globalSetup: require.resolve('./playwright.setup.ts')


In our case we decided to call the file `playwright.setup.ts`


import { chromium } from '@playwright/test'

async function globalSetup() {
const URL = '<http://localhost:8910>'
const browser = await chromium.launch()
const adminLogin = await browser.newPage()
await adminLogin.goto(`${URL}/login`)

const usernameInput = adminLogin.getByLabel('username')
await usernameInput.fill('')

const passwordInput = adminLogin.getByLabel('password')
await passwordInput.fill('password')

await adminLogin.getByRole('button', { name: 'Login' }).click()
await adminLogin.waitForURL(URL, { waitUntil: 'domcontentloaded' })

await adminLogin
.storageState({ path: 'web/tests/storage/adminUser-pw.json' })

await browser.close()

export default globalSetup


The `globalSetup` function opens a new chromium browser with `chromium.lanch()`
`browser.newPage()` open’s a new page; creates a new page in a new browser context.
Afterwards, we navigate to our App's login page with `.goTo()`.

Then, we can use playwright's selectors to login as an admin user.

The part of the code we want to focus on is

await adminLogin
.storageState({ path: 'web/tests/storage/adminUser-pw.json' })


`.context()` - Grabs the browsers contexts the page belongs to.
`.storageState()` - Returns storage state containing browser context, cookie, and local storage snapshot. We can specify the `path`  we want the file to be stored at.

For instance,

"cookies": [
   "name": "session",
   "value": "U2FsdGVkX1+HySRVzUW0F7tQKDMxgBHhLetU+iVR+9TjR4YbXbJqeFxNVnTPD+VQvlGvKeTRmOjOuRXK1ubrMKxhShViotBQqtOEO2rIwhAvQ4JCPeqohq3Iuo/hBqjrSRZ5lXP3cvuTElXPIbDGsA==",
"domain": "localhost",
"path": "/",
"expires": 1705431528.960474,
"httpOnly": true,
"secure": false,
"sameSite": "Strict"
"origins": []


What this does for us, is keep a user’s session active when running test.

### Let’s write a test!

Now that we have a session being stored for an Admin user; we can access the stored state and use in our test.

For example,

test.use({ storageState: 'web/tests/storage/adminUser-pw.json' })


In our test, we can also use `.beforeEach()` hook to execute some logic before each test runs.

For instance,

test.beforeEach(async ({ page }) => {
await page.goto('/')

const admin = page.getByText('Admin').first()

await page.waitForURL('/admin/users')


In the `.beforeEach()` hook we are confirming the User is logged in and is an admin.
Once, confirmed we can get into the meat and potatoes of the test.

We first setup a `MOCK_USER` to populate the input fields in our test.

const MOCK_USER = {
 email: '',
 name: 'SnapCracklePop',
 nickname: 'waffleCrisp',
 pronouns: 'cheerios',

Thereafter, we can test for Admin CRUD (Create, Read, Update, Delete) user functionality.

For instance, admins can create new users.


test.describe('admin crud user', async () => {
test('admin creates a new user', async ({ page }) => {
const newUser = page.getByText('New User').first()

await page.waitForURL('/admin/users/new')

const emailInput = page.locator('input[name="email"]')
await emailInput.fill(

const nameInput = page.locator('input[name="name"]')
await nameInput.fill(

const nicknameInput = page.locator('input[name="nickname"]')
await nicknameInput.fill(MOCK_USER.nickname)

const pronounsInput = page.locator('input[name="pronouns"]')
await pronounsInput.fill(MOCK_USER.pronouns)

await page.getByLabel('Active').check()

const saveButton = page.getByRole('button', { name: 'Save' })

await page.waitForURL('/admin/users')

const newUserToast = page.getByText('User created')

const newUserList = page.getByText(


`.locator()` - method can be used to find elements on the page.
`.click()` - used to simulate a click on the page.
`.fill()` - focuses on an input elements and fills it in.
`.waitForURL()` - waits for page to navigation.

### Challenge

Now that you got the hang of writing tests, can you finish the rest of the test for Read, Update, Delete?

### Resources

Missed how to setup Playwright?

- [How to Playwright](

Want to checkout the repo?

- [Redwood Template App](

Learn RedwoodJS?

- [Learn with Redwood](
- [RedwoodJS](

Interested in Fullstack?

- [Exploring Javascript Fullstack](

Photo by Joshua Aragon on Unsplash

Let's Chat

Are you ready to build something brilliant? We're ready to help.

Thank you! Your submission has been received!
Oops! Something went wrong while submitting the form.
RedwoodJS Logo

for builders

Grants Pass, Oregon • September 26 - 29, 2023
View All