Testing with Playwright

Purpose and Scope

Dub’s web end-to-end testing entry point is the Playwright setup under the web workspace. This page is for contributors and self-hosters who need to run browser-level checks against the local Next.js application, especially flows that require authentication or email verification. The repository separates these checks from unit-style tests: root-level test orchestration goes through Turborepo, while the web package exposes specific Playwright commands for headless, headed, and interactive runs. That split lets contributors run fast local validation when they are working inside apps/web, while still fitting into the monorepo’s broader test script contract.

Sources: apps/web/playwright/README.md, package.json, apps/web/package.json

Playwright tests assume a running local application rather than starting the product in isolation. The documented flow says to start the development server first with pnpm dev, then invoke the web package’s e2e scripts. In this repository, the root pnpm dev script delegates to turbo dev, and the web package’s own dev script generates Prisma client artifacts before launching next dev --turbopack --port 8888. That means e2e test preparation is not only about installing a browser; it also depends on a working local database, environment file, and web app process that matches the normal development workflow.

Sources: package.json, apps/web/package.json, apps/web/playwright/README.md

Relevant Source Files

  • apps/web/playwright/README.md — Defines the contributor-facing Playwright setup: Chromium installation, MailHog startup, required environment variables, seeded login user requirements, and the three supported test modes.
  • package.json — Provides the monorepo-level scripts that wrap common workflows with Turborepo, including dev and test, plus the repository package manager and licensing posture.
  • apps/web/package.json — Defines the web workspace scripts that actually run Playwright: test:e2e, test:e2e:ui, and test:e2e:headed, alongside the local dev server and Vitest script.

Prerequisites and Local Services

Before running browser tests, install the Chromium browser that Playwright will drive. The README scopes the install command to the web workspace with pnpm --filter web exec playwright install chromium, which keeps the command aligned with the package that owns the tests. This is a one-time local setup step rather than a per-test command. If Playwright reports that the browser executable is missing, rerun this installation command from the repository root so pnpm resolves the web package context correctly.

Sources: apps/web/playwright/README.md

pnpm --filter web exec playwright install chromium

Email verification is part of the e2e setup. The Playwright README requires MailHog, started from the web app’s Docker Compose file, because signup tests read one-time password emails from MailHog instead of from an external email provider. The environment must point SMTP traffic at localhost:1025, and RESEND_API_KEY must not be set; otherwise emails can leave the local MailHog path and be delivered through Resend. This detail is important because tests that appear to fail at authentication may actually be failing because the OTP never reaches the local mailbox.

Sources: apps/web/playwright/README.md

docker-compose -f apps/web/docker-compose.yml up -d mailhog

The login tests also require a seeded partner user. The README names E2E_PARTNER_EMAIL=partner1@dub-internal-test.com and E2E_PARTNER_PASSWORD=password, then states that the seeded test user must exist in the local database. The creation command is tsx apps/web/playwright/seed.ts. Treat this as part of test fixture setup: the tests are written around a known account for login coverage, while signup coverage creates a fresh user each run through MailHog email verification.

Sources: apps/web/playwright/README.md

# apps/web/.env
E2E_PARTNER_EMAIL=partner1@dub-internal-test.com
E2E_PARTNER_PASSWORD=password
SMTP_HOST=localhost
SMTP_PORT=1025
# RESEND_API_KEY must not be set for MailHog-backed signup tests
tsx apps/web/playwright/seed.ts

Execution Flow

A typical local e2e session starts with dependency and fixture setup, then the web server, then a Playwright command. From the repository root, use the monorepo development script when you want the normal Turborepo-managed development environment: pnpm dev. At the package level, the web dev script performs pnpm prisma:generate and then runs next dev --turbopack --port 8888 through concurrently. This is useful context when debugging: stale Prisma artifacts, an unavailable database, or a stopped Next.js server can all block a Playwright run before the browser interactions are reached.

Sources: package.json, apps/web/package.json, apps/web/playwright/README.md

Once the server is available, run the e2e script that matches your debugging need. pnpm --filter web test:e2e executes playwright test in the web workspace and is the default headless path. pnpm --filter web test:e2e:headed maps to playwright test --headed, which is better when you need to see browser behavior. pnpm --filter web test:e2e:ui maps to playwright test --ui, which opens Playwright’s interactive UI mode for selecting tests and inspecting runs. These are thin scripts, so most Playwright CLI flags can be reasoned about from the underlying command.

Sources: apps/web/package.json, apps/web/playwright/README.md

# Start the local app first
pnpm dev
 
# Headless e2e run
pnpm --filter web test:e2e
 
# Visible browser run
pnpm --filter web test:e2e:headed
 
# Interactive Playwright UI
pnpm --filter web test:e2e:ui

Script Reference

CommandDefined inUnderlying behaviorUse when
pnpm devpackage.jsonRuns turbo devYou want the normal monorepo development process before testing.
pnpm --filter web test:e2eapps/web/package.jsonRuns playwright testYou want the standard headless browser suite.
pnpm --filter web test:e2e:headedapps/web/package.jsonRuns playwright test --headedYou need to watch browser behavior during a failing flow.
pnpm --filter web test:e2e:uiapps/web/package.jsonRuns playwright test --uiYou want Playwright’s interactive test runner.
pnpm testpackage.jsonRuns turbo run testYou want the monorepo’s test pipeline rather than only web e2e tests.
pnpm --filter web testapps/web/package.jsonGenerates Prisma client, then runs Vitest with no file parallelism and bail-on-first-failure behaviorYou are validating the web package’s non-e2e test command.

The root test script and the web test:e2e scripts serve different purposes. The root script delegates to Turborepo with turbo run test, and the Turborepo pipeline depends test tasks on upstream package builds. The Playwright commands are direct web-package scripts and are documented in the Playwright README as requiring the dev server to already be running. In practice, use the direct Playwright scripts while iterating on browser behavior, then use the broader test pipeline when you need repository-level confidence before opening or updating a pull request.

Sources: package.json, apps/web/package.json, apps/web/playwright/README.md

Troubleshooting Signals

If a signup e2e test cannot find or accept an OTP, first verify MailHog and SMTP settings. The documented configuration requires SMTP_HOST=localhost and SMTP_PORT=1025, with no RESEND_API_KEY in the environment. A hidden Resend key changes the delivery path and can make the local mailbox appear empty even though the app sent an email. If a login test fails immediately, verify that the seeded user exists and that E2E_PARTNER_EMAIL and E2E_PARTNER_PASSWORD match the values in apps/web/.env. These checks map directly to the prerequisites in the Playwright README.

Sources: apps/web/playwright/README.md

If Playwright cannot connect to the app, confirm that the development server was started first and that the web app finished its Prisma generation step. The package script makes Prisma generation part of dev, build, and the web unit test command, so failures in generated client state can surface before any browser assertions. For visual debugging, rerun the same scenario with test:e2e:headed or test:e2e:ui rather than changing the test itself. Those commands exercise the same Playwright runner but expose enough UI to distinguish application state problems from browser automation problems.

Sources: apps/web/package.json, apps/web/playwright/README.md

Next Steps

For day-to-day contribution, keep the setup sequence repeatable: install Chromium once, start MailHog when testing signup, seed the partner user when testing login, run pnpm dev, and then choose the Playwright mode that matches your feedback loop. Use the root pnpm test command when you need the Turborepo test pipeline, but do not confuse it with the documented e2e workflow that expects a live development server. If your change touches authentication, signup, email delivery, or user onboarding, run the relevant Playwright flow before relying only on package-level unit tests.

Sources: apps/web/playwright/README.md, package.json, apps/web/package.json