Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
db20b56
E2E: Playwright harness, fixtures, and CI wiring (1/10).
yasserfaraazkhan Jun 19, 2026
f46ebbc
E2E: Main-process test hooks and shared launch helpers (2/10).
yasserfaraazkhan Jun 19, 2026
4c86fea
E2E: Mattermost shell helpers and bookmarks/custom groups specs (3/10).
yasserfaraazkhan Jun 19, 2026
a2f373b
Merge origin/master into e2e/03-mattermost-shell
yasserfaraazkhan Jun 30, 2026
17086f2
Address CodeRabbit review on mattermost shell E2E helpers
yasserfaraazkhan Jun 30, 2026
fd75868
E2E: Channel menu helper and Mattermost UI specs (#3858)
yasserfaraazkhan Jul 1, 2026
0922144
E2E: Downloads coverage (#3859)
yasserfaraazkhan Jul 1, 2026
5670666
Address CodeRabbit review on mattermost E2E helpers and specs.
yasserfaraazkhan Jul 1, 2026
e81d4dd
Fix cancelled E2E status checks and add E2E/Override label support.
yasserfaraazkhan Jul 1, 2026
6d81908
Address CodeRabbit review on downloads E2E helpers and specs.
yasserfaraazkhan Jul 1, 2026
19b915b
Scope E2E workflow cancellation to the triggering PR branch.
yasserfaraazkhan Jul 1, 2026
f7aa85b
Harden bad-server and drag-and-drop E2E specs against real load failu…
yasserfaraazkhan Jul 1, 2026
b64f379
fix coderabbit comments
yasserfaraazkhan Jul 1, 2026
7d492f2
Restore pull-requests:write for E2E label jobs.
yasserfaraazkhan Jul 1, 2026
48d392f
fix coderabbit comments
yasserfaraazkhan Jul 1, 2026
eab67d9
fix ci
yasserfaraazkhan Jul 1, 2026
e560f83
fix(e2e): tighten TLS error match, clean up created teams, drop dead …
yasserfaraazkhan Jul 1, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 54 additions & 0 deletions .github/actions/cancel-e2e-runs/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
# yaml-language-server: $schema=https://json.schemastore.org/github-action.json
name: Cancel E2E workflow runs
description: >-
Cancel active Electron Playwright Tests runs and mark E2E commit statuses as
cancelled so PR checks do not show a green "No tests ran" result.

inputs:
pr_number:
description: Pull request number whose head SHA receives updated commit statuses
required: true
reason:
description: Commit status description when cancelling
required: false
default: E2E cancelled — tests skipped
cancel_workflow_runs:
description: When false, only update commit statuses (runs already stopped)
required: false
default: 'true'

runs:
using: composite
steps:
- uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
env:
PR_NUMBER: ${{ inputs.pr_number }}
REASON: ${{ inputs.reason }}
CANCEL_WORKFLOW_RUNS: ${{ inputs.cancel_workflow_runs }}
with:
script: |
const {markE2EStatusesCancelled, cancelActiveE2ERuns} = require('./e2e/utils/github-actions.js');
const prNumber = parseInt(process.env.PR_NUMBER, 10);
if (!Number.isFinite(prNumber)) {
throw new Error(`Invalid pr_number: ${process.env.PR_NUMBER}`);
}

const reason = process.env.REASON || 'E2E cancelled — tests skipped';
const shouldCancelRuns = process.env.CANCEL_WORKFLOW_RUNS !== 'false';

if (shouldCancelRuns) {
await cancelActiveE2ERuns({github, context, prNumber});
}

const {data: pr} = await github.rest.pulls.get({
owner: context.repo.owner,
repo: context.repo.repo,
pull_number: prNumber,
});

await markE2EStatusesCancelled({
github,
context,
sha: pr.head.sha,
reason,
});
1 change: 1 addition & 0 deletions .github/workflows/e2e-functional-template.yml
Original file line number Diff line number Diff line change
Expand Up @@ -388,6 +388,7 @@ jobs:
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
env:
PER_OS_REPORT_URL: ${{ steps.upload-html-report-to-s3.outputs.report_url }}
JOB_STATUS: ${{ job.status }}
with:
script: |
process.chdir('./e2e');
Expand Down
24 changes: 20 additions & 4 deletions .github/workflows/e2e-functional.yml
Original file line number Diff line number Diff line change
Expand Up @@ -67,11 +67,13 @@ jobs:

- name: Update initial status for all platforms
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
env:
PLATFORMS: ${{ needs.prepare-matrix.outputs.platforms }}
with:
github-token: ${{ github.token }}
script: |
const { updateInitialStatus } = require('./e2e/utils/github-actions.js');
const platforms = ${{ needs.prepare-matrix.outputs.platforms }};
const platforms = JSON.parse(process.env.PLATFORMS);
await updateInitialStatus({ github, context, platforms });

e2e-tests:
Expand Down Expand Up @@ -113,13 +115,26 @@ jobs:

- name: Update final status for all platforms
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
env:
PR_NUMBER: ${{ inputs.pr_number }}
PLATFORMS: ${{ needs.prepare-matrix.outputs.platforms }}
OUTPUTS: ${{ toJSON(needs.e2e-tests.outputs) }}
E2E_TESTS_RESULT: ${{ needs.e2e-tests.result }}
with:
github-token: ${{ github.token }}
script: |
const { updateFinalStatus } = require('./e2e/utils/github-actions.js');
const platforms = ${{ needs.prepare-matrix.outputs.platforms }};
const outputs = ${{ toJSON(needs.e2e-tests.outputs) }};
await updateFinalStatus({ github, context, platforms, outputs });
const platforms = JSON.parse(process.env.PLATFORMS);
const outputs = JSON.parse(process.env.OUTPUTS);
const prNumber = parseInt(process.env.PR_NUMBER, 10) || null;
await updateFinalStatus({
github,
context,
platforms,
outputs,
e2eTestsResult: process.env.E2E_TESTS_RESULT,
prNumber,
});

remove-e2e-label:
name: Remove E2E label from PR
Expand Down Expand Up @@ -380,4 +395,5 @@ jobs:
with:
name: policy-test-results-${{ runner.os }}
path: e2e/playwright-report
if-no-files-found: ignore
retention-days: 7
211 changes: 162 additions & 49 deletions .github/workflows/e2e-pr-trigger.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,13 @@ name: E2E PR Trigger
# the Electron Playwright Tests (e2e-functional.yml) workflow.
# After tests complete, e2e-functional.yml / e2e-label-cleanup.yml remove the label.
#
# On synchronize: in-progress Electron Playwright Tests runs are cancelled before
# re-adding the label so stale runs for the old commit don't block the new one.
# Matterwick dispatches e2e-functional.yml via workflow_dispatch using the default
# branch as the ref (so all E2E runs show head_branch: master). This makes it
# impossible to distinguish runs by PR branch, so all in-progress E2E runs are
# cancelled — which is correct behaviour since only one labelled PR triggers tests
# at a time in this project.
# E2E/Override (same contract as mattermost-mobile): when present on a PR,
# opened/synchronize events do not add E2E/Run, and applying the override label
# strips E2E/Run and cancels in-flight E2E runs.
#
# On synchronize: in-progress Electron Playwright Tests runs for this PR are
# cancelled before re-adding the label so stale runs for the old commit do not
# block the new one. Runs on other PR branches are left running.
#
# The concurrency group ensures rapid pushes to the same PR don't queue multiple
# label operations: only the most recent push proceeds.
Expand All @@ -23,14 +23,11 @@ on:
- reopened
- ready_for_review
- synchronize
- labeled
- unlabeled
branches:
- master

permissions:
issues: write
pull-requests: write
actions: write

concurrency:
group: e2e-pr-trigger-${{ github.event.pull_request.number }}
cancel-in-progress: true
Expand All @@ -39,68 +36,94 @@ jobs:
add-e2e-label:
name: Add E2E/Run label
runs-on: ubuntu-22.04
if: ${{ !github.event.pull_request.draft }}
permissions:
issues: write
pull-requests: write
actions: write
statuses: write
if: >-
!github.event.pull_request.draft
&& contains(fromJSON('["opened", "reopened", "ready_for_review", "synchronize"]'), github.event.action)
steps:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
with:
ref: ${{ github.event.pull_request.base.ref }}

- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
if: github.event.pull_request.head.repo.full_name == github.repository
with:
ref: ${{ github.event.pull_request.head.sha }}
sparse-checkout: |
e2e/utils/github-actions.js
sparse-checkout-cone-mode: false

- name: Cancel running E2E tests and re-trigger
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
with:
github-token: ${{ github.token }}
script: |
const {cancelActiveE2ERuns, markE2EStatusesCancelled} = require('./e2e/utils/github-actions.js');
const { owner, repo } = context.repo;
const issue_number = context.issue.number;
const pr = context.payload.pull_request;

// --- 1. Cancel in-progress / queued Electron Playwright Tests runs ---
//
// Matterwick dispatches e2e-functional.yml via workflow_dispatch using the
// default branch as the dispatch ref, so every E2E run has head_branch=master
// regardless of which PR triggered it. We therefore cancel ALL non-terminal
// runs of that workflow rather than trying to filter by branch.
// This is safe because only one labelled PR drives E2E tests at a time.
const { data: { workflows } } = await github.rest.actions.listRepoWorkflows({
const { data: labels } = await github.rest.issues.listLabelsOnIssue({
owner,
repo,
issue_number,
});
const labelNames = labels.map((label) => label.name);

if (labelNames.includes('E2E/Override')) {
core.info(`PR #${issue_number} has E2E/Override — skipping E2E/Run refresh.`);

const e2eWorkflow = workflows.find((w) => w.name === 'Electron Playwright Tests');
if (e2eWorkflow) {
for (const status of ['in_progress', 'queued', 'waiting']) {
const { data: { workflow_runs } } = await github.rest.actions.listWorkflowRuns({
owner,
repo,
workflow_id: e2eWorkflow.id,
status,
per_page: 20,
});
for (const run of workflow_runs) {
try {
await github.rest.actions.cancelWorkflowRun({ owner, repo, run_id: run.id });
core.info(`Cancelled E2E run ${run.id} (status: ${status})`);
} catch (e) {
// A run may have finished between list and cancel — that's fine.
core.warning(`Could not cancel run ${run.id}: ${e.message}`);
if (labelNames.includes('E2E/Run')) {
try {
await github.rest.issues.removeLabel({
owner,
repo,
issue_number,
name: 'E2E/Run',
});
} catch (error) {
if (error.status !== 404) {
throw error;
}
}
}
} else {
core.warning('Electron Playwright Tests workflow not found — skipping cancellation');

await cancelActiveE2ERuns({
github,
context,
prNumber: issue_number,
headBranch: pr.head.ref,
});
await markE2EStatusesCancelled({
github,
context,
sha: pr.head.sha,
reason: 'E2E skipped (E2E/Override label active)',
});
return;
}

// --- 2. Remove + re-add E2E/Run label ---
//
// Remove first so re-adding always fires a pull_request:labeled webhook.
// Without this, if the label is already present (tests were running),
// addLabels is a no-op and Matterwick never sees a new event for the
// latest commit.
await cancelActiveE2ERuns({
github,
context,
prNumber: issue_number,
headBranch: pr.head.ref,
});

try {
await github.rest.issues.removeLabel({
owner,
repo,
issue_number,
name: 'E2E/Run',
});
} catch (e) {
if (e.status !== 404) {
throw e; // 404 means label was absent; anything else is unexpected
} catch (error) {
if (error.status !== 404) {
throw error;
}
}

Expand All @@ -110,3 +133,93 @@ jobs:
issue_number,
labels: ['E2E/Run'],
});

honor-e2e-override:
name: Honor E2E/Override
runs-on: ubuntu-22.04
permissions:
issues: write
pull-requests: write
actions: write
statuses: write
if: >-
github.event.action == 'labeled'
&& github.event.label.name == 'E2E/Override'
steps:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
with:
ref: ${{ github.event.pull_request.base.ref }}

- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
if: github.event.pull_request.head.repo.full_name == github.repository
with:
ref: ${{ github.event.pull_request.head.sha }}
sparse-checkout: |
e2e/utils/github-actions.js
sparse-checkout-cone-mode: false

- name: Strip E2E/Run and cancel in-flight E2E
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
with:
github-token: ${{ github.token }}
script: |
const {cancelActiveE2ERuns, markE2EStatusesCancelled} = require('./e2e/utils/github-actions.js');
const { owner, repo } = context.repo;
const issue_number = context.issue.number;
const pr = context.payload.pull_request;

try {
await github.rest.issues.removeLabel({
owner,
repo,
issue_number,
name: 'E2E/Run',
});
} catch (error) {
if (error.status !== 404) {
throw error;
}
}

await cancelActiveE2ERuns({
github,
context,
prNumber: issue_number,
headBranch: pr.head.ref,
});
await markE2EStatusesCancelled({
github,
context,
sha: pr.head.sha,
reason: 'E2E cancelled (E2E/Override label applied)',
});

cancel-on-manual-unlabel:
name: Cancel E2E on manual label removal
runs-on: ubuntu-22.04
permissions:
actions: write
statuses: write
pull-requests: read
if: >-
github.event.action == 'unlabeled'
&& github.event.label.name == 'E2E/Run'
&& github.event.sender.login != 'github-actions[bot]'
steps:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
with:
ref: ${{ github.event.pull_request.base.ref }}

- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
if: github.event.pull_request.head.repo.full_name == github.repository
with:
ref: ${{ github.event.pull_request.head.sha }}
sparse-checkout: |
e2e/utils/github-actions.js
sparse-checkout-cone-mode: false

- name: Cancel E2E runs and mark statuses skipped
uses: ./.github/actions/cancel-e2e-runs
with:
pr_number: ${{ github.event.pull_request.number }}
reason: E2E cancelled (E2E/Run label removed)
Loading
Loading