Skip to main content

Playwright

The @archibald/playwright package provides the Playwright integration for the archibald e2e command. See the Overview for the parts shared with Cypress.

Installation

From the root of your Archibald project run:

archibald add playwright

This installs @archibald/playwright and @playwright/test, scaffolds the config and support files, adds a CI environment, and sets cli.e2e.framework to "playwright" in archibald.json.

Layout

LocationContents
playwright.config.tsProject config (wraps Archibald defaults)
playwright/support/fixtures.tsThe extended test/expect and the testUser fixture
playwright/support/helpers/methods.tsgetTestUser(), selectors, features, helpers
src/{platform}/e2e-playwright/specsTest specifications (*.spec.ts)
src/{platform}/e2e-playwright/pagesPage objects

The spec/page folder is e2e-playwright when Cypress is the configured runner (the default). When Playwright is the configured runner (cli.e2e.framework: "playwright") it owns the base e2e folder instead — see Where tests live for the full convention.

Environment/selector/feature configuration is shared with Cypress and read from cypress/config (see the Overview).

Configuration

playwright.config.ts uses defineConfig from @archibald/playwright, which deep-merges your config over Archibald's defaults (the chrome/firefox/webkit projects, a CI-friendly reporter, tracing, etc.):

import { defineConfig } from '@archibald/playwright';

export default defineConfig({
fullyParallel: true,
use: {
baseURL: 'http://localhost:3100'
}
});

defineConfig also applies the merged environment on top of your config, so baseURL, viewport and userAgent follow the selected environment/tenant (e.g. a tenant baseUrl of http://localhost:3100/de). You do not need a webServer block — the dev server is started by archibald e2e (see the Overview).

Spec discovery and shadowing

defineConfig resolves testDir/testMatch from archibald.json rather than scanning src blindly — see Spec discovery and shadowing in the Overview for the rules, which are shared with Cypress.

Setting testDir or testMatch yourself in playwright.config.ts overrides the resolved set and opts out of shadowing.

Test user and fixtures

Specs import the extended test/expect from support/fixtures, which adds the testUser fixture on top of the package fixtures:

import { test, expect } from 'support/fixtures';
import LoginPage from 'pages/LoginPage';

test.describe('Cart', () => {
test.beforeEach(async ({ page, login, testUser }) => {
await testUser.reset(); // seed a clean account
await new LoginPage(page).visit();
await login(testUser.email, testUser.password);
});

test('...', async ({ page }) => {
/* ... */
});
});

Available fixtures:

FixtureFromDescription
testUsersupport/fixtures (template)Isolated account: email, password, reset, addCart, deleteCart, addOrder, deleteOrder
login@archibald/playwright/supportLogs in via the auth API and sets the session cookies
fillForm@archibald/playwright/supportFills a form (input/select/textarea, incl. checkbox/radio)
waitUntilSettled@archibald/playwright/supportWaits until the DOM stops mutating
waitForHydration@archibald/playwright/supportWaits for the hydration island containing a selector to finish hydrating

getTestUser() (from support/helpers/methods) returns only the identity ({ email, password }) and is what LoginPage.login() defaults to; the mutating operations live on the testUser fixture because they need the request context.

The package root (@archibald/playwright) re-exports everything from @playwright/test, plus defineConfig and getArchibaldConfig. The standalone waitUntilSettled(page) and waitForHydration(page, selector) helpers are exported from the @archibald/playwright/support subpath — the same subpath the Cypress integration uses (@archibald/cypress/support), so the shared hydration helper lives at a matching import path on both runners.

Waiting for hydration

Archibald hydrates CMS components (and the app root) as lazy islands marked with data-arc-hydrated, flipped to "true" once hydrated. Interacting before that — especially typing, which the event-replay feature does not replay — is silently lost. waitForHydration(page, selector) (Cypress: cy.waitForHydration(selector)) scrolls the target into view and waits for its closest island to hydrate, then returns the element. Use it before typing into a lazily-hydrated form (e.g. the review form) or after a full-page navigation such as the OIDC login round-trip (waitForHydration(page, '#app')).

Parallel isolation

Playwright runs specs in parallel. To keep the shared backend account from clashing across workers, getTestUser() derives a per-worker account by plus-tagging the email with the worker's parallel index (erika.musterfrau+0@…, +1@…, using withParallelTag from @archibald/e2e). Each worker reset()s and mutates only its own account, so fullyParallel: true is safe.

The password stays the shared base-account password (the mock clones each +N account from the canonical one), while the email is unique per worker.

Running

archibald e2e # interactive
archibald e2e -e local -b chrome -h # headless chrome, local env

The browser is selected with -b and mapped to the matching project (chrome, firefox, webkit). The Cypress-only electron value is mapped to chrome with a warning.

Without -h (headless), Playwright opens in --ui mode.

Gitlab CI

archibald add playwright scaffolds a CI environment. In CI the app is served externally (a Docker service reached via the ci environment's baseUrl), so archibald e2e does not start a local server. Enable the docker build in archibald.json and add the project's container registry, as for Cypress.

Further documentation

See the Playwright documentation.