e2e-tests/playwright/lib/README.md
A comprehensive end-to-end testing library for Mattermost web, desktop and plugin applications using Playwright.
This library provides:
npm install @mattermost/playwright-lib
This library requires @playwright/test version 1.55.0 or higher:
npm install @playwright/test@^1.55.0
Basic example of logging in and posting a message:
import {test, expect} from '@mattermost/playwright-lib';
test('user can post message', async ({pw}) => {
// # Create and login a new user
const {user} = await pw.initSetup();
const {channelsPage} = await pw.testBrowser.login(user);
// # Navigate and post a message
await channelsPage.goto();
const message = 'Hello World!';
await channelsPage.postMessage(message);
// * Verify message appears
const lastPost = await channelsPage.getLastPost();
await expect(lastPost).toHaveText(message);
});
Ready-to-use page objects for common Mattermost pages:
Reusable component objects for UI elements:
Helper functions for common testing needs:
The library can be configured via optional environment variables:
All environment variables are optional with sensible defaults.
| Variable | Description | Default |
|---|---|---|
PW_BASE_URL | Server URL | http://localhost:8065 |
PW_ADMIN_USERNAME | Admin username | sysadmin |
PW_ADMIN_PASSWORD | Admin password | Sys@dmin-sample1 |
PW_ADMIN_EMAIL | Admin email | [email protected] |
PW_ENSURE_PLUGINS_INSTALLED | Comma-separated list of plugins to install | [] |
PW_RESET_BEFORE_TEST | Reset server before test | false |
PW_SMTP_URL | Inbucket HTTP API URL | http://localhost:9001 |
| Variable | Description | Default |
|---|---|---|
PW_HA_CLUSTER_ENABLED | Enable HA cluster | false |
PW_HA_CLUSTER_NODE_COUNT | Number of cluster nodes | 2 |
PW_HA_CLUSTER_NAME | Cluster name | mm_dev_cluster |
| Variable | Description | Default |
|---|---|---|
PW_PUSH_NOTIFICATION_SERVER | Push notification server URL | https://push-test.mattermost.com |
| Variable | Description | Default |
|---|---|---|
PW_HEADLESS | Run tests headless | true |
PW_SLOWMO | Add delay between actions in ms | 0 |
PW_WORKERS | Number of parallel workers | 1 |
| Variable | Description | Default |
|---|---|---|
PW_SNAPSHOT_ENABLE | Enable snapshot testing | false |
PW_PERCY_ENABLE | Enable Percy visual testing | false |
| Variable | Description | Default |
|---|---|---|
CI | Set automatically in CI environments | N/A |
Selects testcontainers mode — Playwright brings up the server + dependencies itself via Testcontainers — instead of the external mode default, which expects a server already running at PW_BASE_URL.
| Variable | Description | Default |
|---|---|---|
PW_USE_TESTCONTAINERS | Selects testcontainers mode | false |
PW_TESTCONTAINERS_SERVICES | Comma-separated additional services to start (openldap, keycloak, elasticsearch, opensearch, minio, azurite); set to an empty string to start none | minio,openldap,keycloak,elasticsearch |
PW_TESTCONTAINERS_REUSE | Reuse containers across repeated local runs instead of recreating them; tear down with npm run testcontainers:down (requires testcontainers.reuse.enable=true in ~/.testcontainers.properties) | true |
PW_TESTCONTAINERS_CONTAINER_RUNNER | Set when the Playwright process itself runs inside a Docker container (e.g. CI), to join it to the Testcontainers network | false |
SERVER_IMAGE | Prebuilt Mattermost server image testcontainers mode starts | mattermostdevelopment/mattermost-enterprise-edition:master |
MM_ENV | Comma-separated KEY=VALUE server config overrides, merged over the test baseline | none |
PW_LDAP_HOST / PW_LDAP_PORT | OpenLDAP host/port (only used when openldap is started) | localhost / 389 |
PW_KEYCLOAK_URL | Keycloak URL (only used when keycloak is started) | http://localhost:8484 |
PW_ELASTICSEARCH_URL | Elasticsearch URL (only used when elasticsearch is started) | http://localhost:9200 |
PW_OPENSEARCH_URL | OpenSearch URL (only used when opensearch is started) | http://localhost:9201 |
PW_MINIO_URL | Minio URL (only used when minio is started) | http://localhost:9000 |
PW_AZURITE_URL | Azurite URL (only used when azurite is started) | http://localhost:10000 |
In testcontainers mode, these host/port defaults are never actually used — Testcontainers always assigns its own dynamic port or network alias per run, so a testcontainers-mode run can never collide with a same-machine external-mode session using the fixed defaults above. Tear a reused stack down explicitly with npm run testcontainers:down (from the playwright/ package).
The library includes built-in accessibility testing using axe-core:
import {test, expect} from '@mattermost/playwright-lib';
test('verify login page accessibility', async ({page, axe}) => {
// # Navigate to login page
await page.goto('/login');
// # Run accessibility scan
const results = await axe.builder(page).analyze();
// * Verify no accessibility violations
expect(results.violations).toHaveLength(0);
});
The axe-core integration:
The library supports visual testing through Playwright's built-in visual comparisons and Percy integration:
import {test, expect} from '@mattermost/playwright-lib';
test('verify channel header appearance', async ({pw, browserName, viewport}, testInfo) => {
// # Setup and login
const {user} = await pw.initSetup();
const {page, channelsPage} = await pw.testBrowser.login(user);
// # Navigate and prepare page
await channelsPage.goto();
await expect(channelsPage.appBar.playbooksIcon).toBeVisible();
await pw.hideDynamicChannelsContent(page);
// * Take and verify snapshot
await pw.matchSnapshot(testInfo, {page, browserName, viewport});
});
Mock and verify browser notifications:
import {test, expect} from '@mattermost/playwright-lib';
test('verify notification on mention', async ({pw}) => {
// # Setup users and team
const {team, adminUser, user} = await pw.initSetup();
// # Setup admin browser with notifications
const {page: adminPage, channelsPage: adminChannelsPage} = await pw.testBrowser.login(adminUser);
await adminChannelsPage.goto(team.name, 'town-square');
await pw.stubNotification(adminPage, 'granted');
// # Setup user browser and post mention
const {channelsPage: userChannelsPage} = await pw.testBrowser.login(user);
await userChannelsPage.goto(team.name, 'off-topic');
await userChannelsPage.postMessage(`@ALL good morning, ${team.name}!`);
// * Verify notification received
const notifications = await pw.waitForNotification(adminPage);
expect(notifications.length).toBe(1);
});
See CONTRIBUTING.md for development setup and guidelines.
See LICENSE.txt for license information.