e2e-tests/playwright/README.md
There are two ways to run the local server:
Option 1: Run from source
# Typically run the local server with:
cd server && make run
# Or run webapp and server on separate terminals for better performance
# First terminal: Build and run the webapp
cd webapp && make run
# Second terminal: Run the server
cd server && make run-server
Option 2: Testcontainers (recommended for testing, and what CI uses)
No separate terminal or setup step needed — Playwright brings up Postgres, Inbucket, and the Mattermost server itself via Testcontainers, then tears them down after the run.
# Run with defaults (Postgres, Inbucket, Mattermost server, minio, openldap, keycloak, elasticsearch)
PW_USE_TESTCONTAINERS=true npm run test -- login
# Change which additional services start, comma-separated (or "" to start none)
PW_USE_TESTCONTAINERS=true PW_TESTCONTAINERS_SERVICES=minio,openldap npm run test
# Pin a specific server image (defaults to mattermostdevelopment/mattermost-enterprise-edition:master)
PW_USE_TESTCONTAINERS=true SERVER_IMAGE=mattermostdevelopment/mattermost-enterprise-edition:<tag> npm run test
# Pass arbitrary MM_* config overrides as comma-separated KEY=VALUE pairs
PW_USE_TESTCONTAINERS=true MM_ENV=MM_LICENSE=<your-license-key> npm run test
Containers are reused across invocations by default (PW_TESTCONTAINERS_REUSE=true) instead of being recreated every run — tear the stack down explicitly when you're done with npm run testcontainers:down. Set PW_TESTCONTAINERS_REUSE=false for a one-off run that tears itself down when it finishes. Use npm run testcontainers:up to just bring the stack up (or confirm an existing one's still reachable) without running any tests.
See lib/README.md for every available environment variable.
# Install npm packages
npm i
# Install browser binaries as prompted if Playwright is just installed or updated
# See https://playwright.dev/docs/browsers
npx playwright install
# Run a specific test of all projects -- Chrome, Firefox, iPhone and iPad.
# See https://playwright.dev/docs/test-cli.
npm run test -- login
# Run a specific test of a project
npm run test -- login --project=chrome
# Run all tests (including visual tests)
npm run test
# Run CI tests (excludes visual tests, runs only in Chrome)
# Note: visual tests run in a separate workflow
npm run test:ci
/results/output folder when something fails unexpectedly.Check out https://playwright.dev/docs/test-ui-mode for detailed guide on UI Mode to learn more about its features.
npm run playwright-ui
Note: If no tests appear in the UI, check your filter settings:
- Test name filters
- Project filters (setup, ipad, chrome, firefox)
- Tag filters (@tag)
- Execution status filters
The "setup" project runs the initial configuration tests in
specs/test_setup.ts(ensuring plugins are loaded and server deployment is correct). These setup tests are typically run only once before other tests and may be unchecked for subsequent runs, though they can remain checked if needed.
All visual tests must be placed in the specs/visual/ directory and tagged with @visual in the test tags array. This organization ensures proper test discovery and execution patterns.
Visual tests are used to verify the UI appearance is consistent across browsers and remains stable across code changes. There are two types of visual tests supported:
In CI environments, visual tests run in a separate dedicated pipeline:
npm run test:ci which excludes all tests with the @visual tagWhen creating visual tests:
Follow the test documentation format like other tests:
@objective tag// # for actions, // * for verifications)Place in the correct location:
specs/visual/ directory, organized by feature areaspecs/visual/channels/intro_channel.spec.tsAdd required tags:
@visual tag@login_page, @channel_page)Manage dynamic content:
pw.hideDynamicChannelsContent() to hide elements that could change between runsExample:
/**
* @objective Capture visual snapshot of the landing/login page
*/
test(
'displays landing page with login options',
{tag: ['@visual', '@landing_page']},
async ({pw, page, browserName, viewport}, testInfo) => {
// # Go to landing login page
await pw.landingLoginPage.goto();
await pw.landingLoginPage.toBeVisible();
// * Verify landing page appears as expected
await pw.matchSnapshot(testInfo, {page, browserName, viewport});
},
);
Change to the ./ project directory, then run the docker container. (See https://playwright.dev/docs/docker for reference.)
docker run -it --rm -v "$(pwd):/mattermost/" --ipc=host mcr.microsoft.com/playwright:v1.61.0-noble /bin/bash
export PW_BASE_URL=http://host.docker.internal:8065
export PW_HEADLESS=true
cd mattermost/e2e-tests/playwright
# Install npm packages. Use "npm ci" to match the automated environment
export PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD=1 npm ci
# Run specific test. See https://playwright.dev/docs/test-cli.
npm run test -- login --project=chrome
# Or run all tests
npm run test
# Run visual tests (must be run inside Docker for consistency)
npm run test -- specs/visual
# Update snapshots of visual tests (must be run inside Docker)
npm run test -- specs/visual --update-snapshots
# Run Percy visual tests (requires PERCY_TOKEN environment variable)
export PERCY_TOKEN=<your-percy-token>
npm run percy:docker
Accessibility tests ensure Mattermost meets WCAG 2.1 AA compliance standards. Tests are located in specs/accessibility/ and cover keyboard navigation, screen reader support, focus management, and automated accessibility scanning.
For comprehensive guidelines on writing accessibility tests, aria snapshots, and folder structure, see docs/accessibility/.
Playwright's accessibility locators should be the preferred approach for all tests, not just accessibility tests. These locators query elements based on how users and assistive technologies perceive them, making tests more resilient to implementation changes and ensuring better accessibility by design.
page.getByRole('button', {name: 'Save'}) is clearer than page.locator('[data-testid="save-btn"]')page.getByRole('button', {name: 'Save'}), page.getByRole('textbox', {name: 'Email'})page.getByLabel('Email address')page.getByText('Welcome'), page.getByPlaceholder('Enter email')page.locator('[data-testid="..."]') - Use only when accessibility locators aren't possiblepage.locator('.class') - Avoid unless absolutely necessaryUse data-testid only when:
For all test examples, see docs/accessibility/ for comprehensive patterns and best practices.
See https://playwright.dev/docs/test-pom.
Page and component abstractions are in shared library located at ./lib/src/ui. They should be established before writing a spec file so that any future changes in the DOM structure will be made in one place only. No static UI text or fixed locator should be written in the spec file.