mattermost/.github/e2e-tests-workflows.md
sabril 9d318dc4cd
refactor: speed up E2E test workflows and eliminate npm cache-restore failures (#36599)
Workers no longer run `npm ci` — `node_modules` and framework binaries
are restored from actions/cache populated once by a new `prep-deps` job.
This closes the intermittent EEXIST/ENOENT failure inside npm's own
cacache writer that occasionally fails `npm ci` on a runner. Removing
`npm ci` from workers also cuts ~5 min of duplicated install work per worker.

dispatch-begin now runs as its own job after prep-deps so it fires once the
per-worker test-server setup is the only remaining work before dispatch-run.
2026-05-19 11:26:39 +08:00

22 KiB
Raw Permalink Blame History

E2E Test Pipelines

Three automated E2E test pipelines cover different stages of the development lifecycle.

Pipelines

Pipeline Trigger Editions Tested Image Source
PR (e2e-tests-ci.yml) Argo Events on Enterprise CI/docker-image status enterprise mattermostdevelopment/**
Merge to master/release (e2e-tests-on-merge.yml) Platform delivery after docker build (delivery-platform/.github/workflows/mattermost-platform-delivery.yaml) enterprise, fips mattermostdevelopment/**
Release cut (e2e-tests-on-release.yml) Platform release after docker build (delivery-platform/.github/workflows/release-mattermost-platform.yml) enterprise, fips, team (future) mattermost/**

All pipelines follow the smoke-then-full pattern: smoke tests run first, full tests only run if smoke passes.

Workflow Files

.github/workflows/
├── e2e-tests-ci.yml                       # PR orchestrator
├── e2e-tests-on-merge.yml                 # Merge orchestrator (master/release branches)
├── e2e-tests-on-release.yml               # Release cut orchestrator
├── e2e-tests-cypress.yml                  # Shared wrapper: routes to v1 or v2 template
├── e2e-tests-playwright.yml               # Shared wrapper: routes to v1 or v2 template
├── e2e-tests-cypress-template-v2.yml      # Active: cypress + test-system-io dispatch
├── e2e-tests-playwright-template-v2.yml   # Active: playwright + test-system-io dispatch
├── e2e-tests-cypress-template.yml         # Deprecated v1 (legacy in-job execution)
└── e2e-tests-playwright-template.yml      # Deprecated v1 (legacy in-job execution)

v1 templates are deprecated. They remain available behind a feature flag during cutover but receive no further changes. New work targets the v2 templates exclusively. The wrappers route by vars.E2E_USE_TEST_IO_DISPATCH'true' selects v2, anything else falls back to v1.

Call hierarchy

e2e-tests-ci.yml ─────────────────┐
e2e-tests-on-merge.yml ───────────┤──► e2e-tests-cypress.yml ─────┐
e2e-tests-on-release.yml ─────────┘    e2e-tests-playwright.yml ──┤
                                                                  │
                                       ┌──────────────────────────┘
                                       │  routes on E2E_USE_TEST_IO_DISPATCH
                                       ▼
                  v2 (active) ──► e2e-tests-{cypress,playwright}-template-v2.yml
                  v1 (legacy) ──► e2e-tests-{cypress,playwright}-template.yml

Workflow Architecture (v2)

v2 splits the template into five jobs — prepare-run, prep-deps, dispatch-begin, workers (matrix), and report — and pushes spec-level execution to Test System IO so workers stay thin and identical.

┌──────────────────────────────────────────────────────────────────────────┐
│  Template v2: e2e-tests-{cypress,playwright}-template-v2.yml              │
│                                                                            │
│   ┌───────────────────┐                ┌──────────────────────────────┐   │
│   │ prepare-run       │                │ prep-deps                    │   │
│   │ (1 runner)        │    parallel    │ (1 runner)                   │   │
│   │                   │ ◄────────────► │                              │   │
│   │ • build workers   │                │ Cypress:                     │   │
│   │   matrix [1..N]   │                │   • cypress/node_modules     │   │
│   │ • compute commit  │                │   • ~/.cache/Cypress (binary)│   │
│   │   status context  │                │                              │   │
│   │ • emit composite  │                │ Playwright:                  │   │
│   │   identity        │                │   • webapp/platform/{client, │   │
│   │                   │                │       types}/{lib,node_mod}  │   │
│   │                   │                │   • playwright/node_modules  │   │
│   │                   │                │   • playwright/lib/dist      │   │
│   │                   │                │   • ~/.cache/ms-playwright   │   │
│   │                   │                │     (chromium only)          │   │
│   │                   │                │                              │   │
│   │                   │                │   → saved to actions/cache   │   │
│   └─────────┬─────────┘                └───────────────┬──────────────┘   │
│             │                                          │                  │
│             │                                          ▼                  │
│             │                          ┌──────────────────────────────┐  │
│             │                          │ dispatch-begin               │  │
│             │                          │ • register run with          │  │
│             │                          │   Test System IO             │  │
│             │                          │ • runs immediately before    │  │
│             │                          │   workers to minimise the    │  │
│             │                          │   inactivity-timeout window  │  │
│             │                          └───────────────┬──────────────┘  │
│             │                                          │                  │
│             └────────────────────┬─────────────────────┘                  │
│                                  ▼                                        │
│   ┌─────────────────────────────────────────────────────────────────┐    │
│   │ workers  (matrix, fail-fast: false)                             │    │
│   │   Cypress full: N=40       |   Playwright full: N=10            │    │
│   │                                                                 │    │
│   │   each worker, in parallel:                                     │    │
│   │     1. sparse-checkout actions + full checkout-repo             │    │
│   │     2. setup-node                                                │    │
│   │     3. restore caches  ◄─── actions/cache (from prep-deps)      │    │
│   │        (fail-on-cache-miss: true)                               │    │
│   │     4. cloud-init + start-server  (docker compose stack)        │    │
│   │     5. prepare-cypress | prepare-playwright (run setup project) │    │
│   │     6. dispatch-run  ──────────────────────────────────┐        │    │
│   │        (pulls specs from Test System IO, runs locally,  │       │    │
│   │         posts result, loops until queue is empty)       │       │    │
│   │     7. cloud-teardown                                   │       │    │
│   └────────────────────┬────────────────────────────────────┼───────┘    │
│                        │                                    │            │
│                        ▼                                    │            │
│   ┌─────────────────────────────────────────────────┐       │            │
│   │ report                                           │       │            │
│   │ • pull aggregated results from Test System IO    │  ◄────┘            │
│   │ • post commit status                             │                    │
│   │ • send webhook notification                      │                    │
│   └─────────────────────────────────────────────────┘                    │
└────────────────────────────────────────────────────────────────────────┬─┘
                                                                          │
                                              ┌───────────────────────────▼───┐
                                              │ Test System IO (external)     │
                                              │ • spec-level dispatch         │
                                              │ • result aggregation          │
                                              │ • retry orchestration         │
                                              └───────────────────────────────┘

Key properties

  • Spec-level vs. job-level parallelism. The matrix sizes the runner pool; Test System IO does the spec assignment. Slow specs don't block a worker — fast workers keep pulling the next spec from the queue.
  • Cache-only workers. prep-deps installs once per workflow run and saves to actions/cache. Every worker restores with fail-on-cache-miss: true and runs zero npm ci. Eliminates the 40-way EEXIST/ENOENT race in npm's shared cacache writer.
  • dispatch-begin runs late. It depends on prep-deps so the gap between Test System IO run registration and the first worker calling dispatch-run is just per-worker setup (~35 min). Registering earlier risks the run timing out before any worker checks in, bulk-failing every spec.
  • Playwright slim slice. Playwright only consumes @mattermost/client and @mattermost/types from webapp, so prep-deps caches just those two packages' built lib/ and node_modules (~1030 MB) instead of the full webapp/node_modules tree (~12 GB).
  • Browser/binary caches. Cypress caches ~/.cache/Cypress (cypress binary lives outside node_modules); playwright caches ~/.cache/ms-playwright (chromium only). Both keyed on the framework's lockfile so they invalidate on version bumps.
  • No retry plumbing in the template. Test System IO handles per-spec retries; the workflow only sees aggregated results.

Pipeline 1: PR (e2e-tests-ci.yml)

Runs E2E tests for every PR commit after the enterprise docker image is built. Fails if the commit is not associated with an open PR.

Trigger chain:

PR commit ─► Enterprise CI builds docker image
           ─► Argo Events detects "Enterprise CI/docker-image" status
           ─► dispatches e2e-tests-ci.yml

For PRs from forks, body.branches may be empty so the workflow falls back to master for workflow files (trusted code), while commit_sha still points to the fork's commit.

Jobs: 2 (cypress + playwright), each does smoke -> full

Commit statuses (4 total):

Context Description (pending) Description (result)
e2e-test/cypress-smoke|enterprise tests running, image_tag:abc1234 100% passed (1313), 440 specs, image_tag:abc1234
e2e-test/cypress-full|enterprise tests running, image_tag:abc1234 100% passed (1313), 440 specs, image_tag:abc1234
e2e-test/playwright-smoke|enterprise tests running, image_tag:abc1234 100% passed (200), 50 specs, image_tag:abc1234
e2e-test/playwright-full|enterprise tests running, image_tag:abc1234 99.5% passed (199/200), 1 failed, 50 specs, image_tag:abc1234

Manual trigger (CLI):

gh workflow run e2e-tests-ci.yml \
  --repo mattermost/mattermost \
  --field pr_number="35171"

Manual trigger (GitHub UI):

  1. Go to Actions > E2E Tests (smoke-then-full)
  2. Click Run workflow
  3. Fill in pr_number (e.g., 35171)
  4. Click Run workflow

On-demand testing

For on-demand E2E testing, the existing triggers still work:

  • Comment triggers: /e2e-test, /e2e-test fips, or with MM_ENV parameters
  • Label trigger: E2E/Run

These are separate from the automated workflow and can be used for custom test configurations or re-runs.


Pipeline 2: Merge (e2e-tests-on-merge.yml)

Runs E2E tests after every push/merge to master or release-* branches.

Trigger chain:

Push to master/release-*
  ─► Argo Events (mattermost-platform-package sensor)
  ─► delivery-platform/.github/workflows/mattermost-platform-delivery.yaml
  ─► builds docker images (enterprise + fips)
  ─► trigger-e2e-tests job dispatches e2e-tests-on-merge.yml

Jobs: 4 (cypress + playwright) x (enterprise + fips), smoke skipped, full tests only

Commit statuses (4 total):

Context Description example
e2e-test/cypress-full|enterprise 100% passed (1313), 440 specs, image_tag:abc1234_def5678
e2e-test/cypress-full|fips 100% passed (1313), 440 specs, image_tag:abc1234_def5678
e2e-test/playwright-full|enterprise 100% passed (200), 50 specs, image_tag:abc1234_def5678
e2e-test/playwright-full|fips 100% passed (200), 50 specs, image_tag:abc1234_def5678

Manual trigger (CLI):

# For master
gh workflow run e2e-tests-on-merge.yml \
  --repo mattermost/mattermost \
  --field branch="master" \
  --field commit_sha="<full_commit_sha>" \
  --field server_image_tag="<image_tag>"

# For release branch
gh workflow run e2e-tests-on-merge.yml \
  --repo mattermost/mattermost \
  --field branch="release-11.4" \
  --field commit_sha="<full_commit_sha>" \
  --field server_image_tag="<image_tag>"

Manual trigger (GitHub UI):

  1. Go to Actions > E2E Tests (master/release - merge)
  2. Click Run workflow
  3. Fill in:
    • branch: master or release-11.4
    • commit_sha: full 40-char SHA
    • server_image_tag: e.g., abc1234_def5678
  4. Click Run workflow

Pipeline 3: Release Cut (e2e-tests-on-release.yml)

Runs E2E tests after a release cut against the published release images.

Trigger chain:

Manual release cut
  ─► delivery-platform/.github/workflows/release-mattermost-platform.yml
  ─► builds and publishes release docker images
  ─► trigger-e2e-tests job dispatches e2e-tests-on-release.yml

Jobs: 4 (cypress + playwright) x (enterprise + fips), smoke skipped, full tests only. Team edition planned for future.

Commit statuses (4 total, 6 when team is enabled):

Descriptions include alias tags showing which rolling docker tags point to the same image.

RC example (11.4.0-rc3):

Context Description example
e2e-test/cypress-full|enterprise 100% passed (1313), 440 specs, image_tag:11.4.0-rc3 (release-11.4, release-11)
e2e-test/cypress-full|fips 100% passed (1313), 440 specs, image_tag:11.4.0-rc3 (release-11.4, release-11)
e2e-test/cypress-full|team (future) 100% passed (1313), 440 specs, image_tag:11.4.0-rc3 (release-11.4, release-11)

Stable example (11.4.0) — includes MAJOR.MINOR alias:

Context Description example
e2e-test/cypress-full|enterprise 100% passed (1313), 440 specs, image_tag:11.4.0 (release-11.4, release-11, 11.4)
e2e-test/cypress-full|fips 100% passed (1313), 440 specs, image_tag:11.4.0 (release-11.4, release-11, 11.4)
e2e-test/cypress-full|team (future) 100% passed (1313), 440 specs, image_tag:11.4.0 (release-11.4, release-11, 11.4)

Manual trigger (CLI):

gh workflow run e2e-tests-on-release.yml \
  --repo mattermost/mattermost \
  --field branch="release-11.4" \
  --field commit_sha="<full_commit_sha>" \
  --field server_image_tag="11.4.0" \
  --field server_image_aliases="release-11.4, release-11, 11.4"

Manual trigger (GitHub UI):

  1. Go to Actions > E2E Tests (release cut)
  2. Click Run workflow
  3. Fill in:
    • branch: release-11.4
    • commit_sha: full 40-char SHA
    • server_image_tag: e.g., 11.4.0 or 11.4.0-rc3
    • server_image_aliases: e.g., release-11.4, release-11, 11.4 (optional)
  4. Click Run workflow

Commit Status Format

Context name: e2e-test/<phase>|<edition>

Where <phase> is cypress-smoke, cypress-full, playwright-smoke, or playwright-full.

Description format:

  • All passed: 100% passed (<count>), <specs> specs, image_tag:<tag>[ (<aliases>)]

  • With failures: <rate>% passed (<passed>/<total>), <failed> failed, <specs> specs, image_tag:<tag>[ (<aliases>)]

  • Pending: tests running, image_tag:<tag>[ (<aliases>)]

  • Pass rate: 100% if all pass, otherwise one decimal (e.g., 99.5%)

  • Aliases only present for release cuts

Failure behavior

  1. Smoke test fails: Full tests are skipped, only smoke commit status shows failure
  2. Full test fails: Full commit status shows failure with pass rate
  3. Both pass: Both smoke and full commit statuses show success
  4. No PR found (PR pipeline only): Workflow fails immediately

Smoke-then-Full Pattern

Each wrapper (Cypress/Playwright) follows this flow:

generate-build-variables (branch, build_id, server_image)
  ─► smoke tests (1 worker, minimal docker services)
    ─► if smoke passes ─► full tests (20 workers cypress / 1 worker playwright, all docker services)
      ─► report (aggregate results, update commit status)

Test filtering

Framework Smoke Full
Cypress --stage=@prod --group=@smoke --stage="@prod" --excludeGroup="@te_only,@cloud_only,@high_availability" --sortFirst=... --sortLast=...
Playwright --grep @smoke --grep-invert "@smoke|@visual"

Worker configuration

Framework Smoke Workers Full Workers
Cypress 1 20
Playwright 1 1 (uses internal parallelism via PW_WORKERS)

Docker services

Test Phase Docker Services
Smoke postgres inbucket
Full postgres inbucket minio openldap elasticsearch keycloak

Tagging Smoke Tests

Cypress

Add @smoke to the Group comment at the top of spec files:

// Stage: @prod
// Group: @channels @messaging @smoke

Playwright

Add @smoke to the test tag option:

test('critical login flow', {tag: ['@smoke', '@login']}, async ({pw}) => {
    // ...
});

Shared Wrapper Inputs

The wrappers (e2e-tests-cypress.yml, e2e-tests-playwright.yml) accept these inputs:

Input Default Description
server_edition enterprise Edition: enterprise, fips, or team
server_image_repo mattermostdevelopment Docker namespace: mattermostdevelopment or mattermost
server_image_tag derived from commit_sha Docker image tag
server_image_aliases (empty) Alias tags shown in commit status description
ref_branch (empty) Source branch name for webhook messages (e.g., master or release-11.4)

The automation dashboard branch name is derived from context:

  • PR: pr-<pr_number> (e.g., pr-35205)
  • Master merge: master
  • Release merge: release-<version> (e.g., release-11.7)
  • Fallback: commit-<short_sha> (e.g., commit-abc1234)

Master and release runs use the real branch verbatim so the /reports/{repo}/{branch} view aggregates every build on that branch into one history. PR runs use a pr-<n> token because there's no real branch to use; the commit-only fallback uses a commit-<short_sha> token (defensive — no current caller hits this path).

The test type suffix (-smoke or -full) is appended by the template.

The server image is derived as:

{server_image_repo}/{edition_image_name}:{server_image_tag}

Where edition_image_name maps to:

  • enterprise -> mattermost-enterprise-edition
  • fips -> mattermost-enterprise-fips-edition
  • team -> mattermost-team-edition

Webhook Message Format

After full tests complete, a webhook notification is sent to the configured REPORT_WEBHOOK_URL. The results line uses the same commit_status_message as the GitHub commit status. The source line varies by pipeline using report_type and ref_branch.

Report types: PR, MASTER, RELEASE, RELEASE_CUT

PR

:open-pull-request: mattermost-pr-35205
:docker: mattermostdevelopment/mattermost-enterprise-edition:abc1234
100% passed (1313), 440 specs | full report

Merge to master

:git_merge: abc1234 on master
:docker: mattermostdevelopment/mattermost-enterprise-edition:abc1234_def5678
100% passed (1313), 440 specs | full report

Merge to release branch

:git_merge: abc1234 on release-11.4
:docker: mattermostdevelopment/mattermost-enterprise-edition:abc1234_def5678
100% passed (1313), 440 specs | full report

Release cut

:github_round: abc1234 on release-11.4
:docker: mattermost/mattermost-enterprise-edition:11.4.0-rc3
100% passed (1313), 440 specs | full report

The commit short SHA links to the commit on GitHub. The PR number links to the pull request.


  • e2e-tests/cypress/ - Cypress test suite
  • e2e-tests/playwright/ - Playwright test suite
  • e2e-tests/.ci/ - CI configuration and environment files
  • e2e-tests/Makefile - Makefile with targets for running tests, generating cycles, and reporting