Introduction
Repeating full page.goto('http://localhost:4100/login') and asserting that the URL merely “contains /” invites false greens. The Cypress cleanup article walked through hooks and baseUrl; here is the same discipline with Playwright Test.
Previous post: Playwright refactoring - app actions vs POM.
Hooks - test.beforeEach
Playwright reuses Mocha-style hooks. A common pattern: open /login before each test:
import { test } from "@playwright/test"
test.describe("login", () => {
test.beforeEach(async ({ page }) => {
await page.goto("/login")
})
test("successful login", async ({ page, request }) => {
// ...
})
})In UI mode (npx playwright test --ui) the beforeEach steps are clearly grouped, similar to Cypress’s BEFORE EACH block.

baseURL
In playwright.config.ts:
import { defineConfig } from "@playwright/test"
export default defineConfig({
use: {
baseURL: process.env.BASE_URL ?? "http://localhost:4100",
},
})Now page.goto("/login") resolves against the frontend origin - the same idea as baseUrl in cypress.json.
URL assertions - substring traps
Checking only / still passes if the app navigates to /you_should_not_be_here. Prefer equality (or a tight regex):
import { expect } from "@playwright/test"
const base = process.env.BASE_URL ?? "http://localhost:4100"
await expect(page).toHaveURL(`${base}/`)
await expect(page).toHaveURL(`${base}/login`)
API base URL
Keep the ASP.NET host (http://localhost:5000) in process.env.API_URL and reuse it inside HTTP helpers. Optionally mirror values into defineConfig({ env: { ... } }) if you prefer reading from test.info(), but shared modules reading process.env are usually enough.
Remember: issue DELETE before POST when seeding users - the same logical bug described in login tests and the URL refactor post if create skips cleanup.
On CI, define BASE_URL and API_URL as pipeline variables (Cypress on Azure shows the idea). Playwright needs no Cypress plugin: Node already sees process.env before tests start.
Summary
| Cypress | Playwright Test |
|---|---|
cypress.json |
playwright.config.ts |
cy.visit('/x') |
page.goto('/x') + baseURL |
Cypress.config() |
process.env / defineConfig |
Reference branch from the Cypress series:
https://github.com/12masta/react-redux-realworld-example-app/tree/4-cypress
https://github.com/12masta/react-redux-realworld-example-app/pull/4/files