feat(e2e): Migrate Desktop E2E to Playwright and add merged cross-OS reporting#3728
feat(e2e): Migrate Desktop E2E to Playwright and add merged cross-OS reporting#3728yasserfaraazkhan wants to merge 85 commits intomasterfrom
Conversation
Badge E2E tests (e2e/specs/notification_trigger/): - Add 20 Windows + 5 Linux test scenarios covering badge type priority, unread setting toggle, badge clearing, state transitions, and edge cases - Add test trigger hooks to badge.ts (__testTriggerBadge, __testBadgeState, __testTriggerSetUnreadBadgeSetting) Notification unit tests (src/main/notifications/): - index.test.ts: add 23 tests covering missing view/server guards, String(false) sound semantics, failed events, 10s timeout, click routing (NOTIFICATION_CLICKED), Linux DND, Windows Priority-Only, download DND/show/close/failed, and displayUpgrade/displayRestartToUpgrade flows - Mention.test.ts: 9 tests covering sound selection, icon stripping, Win7 fallback, uId uniqueness, channelId/teamId storage - Download.test.ts: 8 tests covering platform title/body, icon stripping, uId uniqueness - Upgrade.test.ts: 6 tests covering icon stripping and localization keys E2E test maintenance: - Remove stale known_flaky_tests.json allowlist (all entries were dead) - Fix deeplink.test.js and full_screen.test.js for deterministic assertions Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Covers 45 new test scenarios across index.test.ts additions and three new class-level test files (Mention, Download, Upgrade). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- NM-11: add isPriority to windows-focus-assist mock requirement - DD-01: specify process.platform='darwin' is required for DND path - NM-05/06: replace contradictory 'fire after await' approach with correct nested describe + prototype.show override pattern Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
prototype.show override doesn't work because show is an own instance property; replace with blockShow flag pattern consistent with NM-07. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
7 unit tests (PM-U01–U07) filling gaps in Calls widget routing, external fullscreen dialog, no-mainWindow crash guard, Windows IPC URLs, and Linux/macOS media access guards. 3 E2E IPC registration tests (E2E-P01–P03) proving ipcMain handler wiring. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Plan covers PM-U01–U07 unit tests and E2E-P01–P03 IPC registration tests for src/main/security/permissionsManager.ts. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…anup Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughAdds and expands end-to-end and unit tests for badge, notification, and permissions behavior across platforms; exposes test-only badge hooks; replaces fullscreen sleeps with deterministic waits; increases several test timeouts; and removes the flaky-test analyzer and its known list. Changes
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Suggested reviewers
🚥 Pre-merge checks | ✅ 1 | ❌ 2❌ Failed checks (2 warnings)
✅ Passed checks (1 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
📝 Coding Plan
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
- Add eslint-disable-next-line no-new comments for constructor side-effect tests in Download, Mention, and Upgrade notification test files - Apply auto-fix linter changes (brace style, dot notation, blank line) - Remove superpowers design/plan docs that are no longer needed Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
Actionable comments posted: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/main/security/permissionsManager.test.js (1)
74-112:⚠️ Potential issue | 🟠 Major
setForServertests are order-dependent without mock resets.
systemPreferencesmock call history persists across tests in this describe, so PM-U06/PM-U07 can fail due to previous tests rather than current behavior.🧪 Suggested isolation fix
describe('main/PermissionsManager', () => { describe('setForServer', () => { + beforeEach(() => { + systemPreferences.getMediaAccessStatus.mockReset(); + systemPreferences.askForMediaAccess.mockReset(); + }); + if (process.platform !== 'linux') { it('should ask for media permission when is not granted but the user explicitly granted it', () => {🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/main/security/permissionsManager.test.js` around lines 74 - 112, The tests under describe('setForServer') share persisted mock call history on systemPreferences which makes PM-U06/PM-U07 order-dependent; add a beforeEach inside that describe to clear/reset the mocks (e.g., jest.clearAllMocks() or specifically systemPreferences.getMediaAccessStatus.mockClear() and systemPreferences.askForMediaAccess.mockClear()) so each test invoking PermissionsManager.setForServer runs with a fresh mock state and does not rely on previous test calls.
🧹 Nitpick comments (2)
e2e/utils/analyze-flaky-test.js (1)
12-21: LGTM – simplification by removing stale flaky-test allowlist.The change correctly removes the per-OS filtering against a known flaky tests list, which aligns with the PR objective of removing the stale allowlist after fixing the flaky tests.
Minor pre-existing issue in adjacent code: The error path at line 20 returns
{}, but the consuming workflow (.github/workflows/e2e-functional-template.yml) destructuresnewFailedTestsand accesses.length. If an error occurs (e.g., report file missing), this will throw aTypeError. Consider returning a consistent shape:♻️ Suggested fix for error handling consistency
} catch (error) { console.error('Error analyzing failures:', error); - return {}; + return {newFailedTests: [], os: process.platform}; }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@e2e/utils/analyze-flaky-test.js` around lines 12 - 21, analyzeFlakyTests currently returns {} on errors which breaks callers that destructure newFailedTests and call .length; update the error path in analyzeFlakyTests to return the same shape as the success path (e.g., { newFailedTests: [], os }) so callers always receive newFailedTests as an array; locate and modify the catch block in the analyzeFlakyTests function (references: readJsonFromFile, generateShortSummary, failedFullTitles, MOCHAWESOME_REPORT_DIR) to log the error and return the consistent object.docs/superpowers/specs/2026-03-16-notification-unit-tests-design.md (1)
17-24: Add a language specifier to the fenced code block.The static analysis tool flagged this code block for missing a language. Adding a language specifier (e.g.,
textorplaintext) improves rendering consistency and accessibility.-``` +```text src/main/notifications/ index.test.ts ← existing file — add NM-01…NM-11, DD-01…DD-05, UV-07…UV-13 Mention.test.ts ← NEW (construction logic only) Download.test.ts ← NEW (construction logic only) Upgrade.test.ts ← NEW (NewVersionNotification + UpgradeNotification construction only) dnd-windows.test.js ← existing, untouched<details> <summary>🤖 Prompt for AI Agents</summary>Verify each finding against the current code and only fix it if needed.
In
@docs/superpowers/specs/2026-03-16-notification-unit-tests-design.mdaround
lines 17 - 24, The fenced code block listing notification test files is missing
a language specifier; update the opening fence fromto a language such astext or ```plaintext so the block containing "index.test.ts",
"Mention.test.ts", "Download.test.ts", "Upgrade.test.ts", and
"dnd-windows.test.js" is rendered with the specified language for consistent
formatting and accessibility.</details> </blockquote></details> </blockquote></details> <details> <summary>🤖 Prompt for all review comments with AI agents</summary>Verify each finding against the current code and only fix it if needed.
Inline comments:
In@docs/superpowers/plans/2026-03-16-permissions-manager-tests.md:
- Around line 55-57: Replace the machine-specific cd command and absolute path
with a repo-relative instruction: remove "cd
/Users/yasserkhan/Documents/mattermost/desktop" and instruct readers to run the
test from the repository root using the existing npm script (e.g., "npm run
test:unit -- --testPathPattern=permissionsManager"); alternatively suggest
adding a package.json script like "test:permissions" that runs "npm run
test:unit -- --testPathPattern=permissionsManager" so contributors can run a
single, portable command.In
@src/app/system/badge.ts:
- Around line 130-132: The test-only global __testBadgeState currently stores
the raw inputs (sessionExpired, mentionCount, showUnreadBadge) which can hide
bugs in the actual badge resolution; modify the test branch so it records the
resolved badge outcome (e.g., resolvedType or resolvedBadge) from the real badge
resolution logic instead of just inputs—locate where the badge resolution is
computed (the variable/symbol that holds the final badge decision such as
resolvedType/resolvedBadge in badge.ts) and include that resolved value in
__testBadgeState along with any minimal metadata needed for E2E checks, keeping
this change limited to the process.env.NODE_ENV === 'test' branch.
Outside diff comments:
In@src/main/security/permissionsManager.test.js:
- Around line 74-112: The tests under describe('setForServer') share persisted
mock call history on systemPreferences which makes PM-U06/PM-U07
order-dependent; add a beforeEach inside that describe to clear/reset the mocks
(e.g., jest.clearAllMocks() or specifically
systemPreferences.getMediaAccessStatus.mockClear() and
systemPreferences.askForMediaAccess.mockClear()) so each test invoking
PermissionsManager.setForServer runs with a fresh mock state and does not rely
on previous test calls.
Nitpick comments:
In@docs/superpowers/specs/2026-03-16-notification-unit-tests-design.md:
- Around line 17-24: The fenced code block listing notification test files is
missing a language specifier; update the opening fence fromto a language such astext or ```plaintext so the block containing "index.test.ts",
"Mention.test.ts", "Download.test.ts", "Upgrade.test.ts", and
"dnd-windows.test.js" is rendered with the specified language for consistent
formatting and accessibility.In
@e2e/utils/analyze-flaky-test.js:
- Around line 12-21: analyzeFlakyTests currently returns {} on errors which
breaks callers that destructure newFailedTests and call .length; update the
error path in analyzeFlakyTests to return the same shape as the success path
(e.g., { newFailedTests: [], os }) so callers always receive newFailedTests as
an array; locate and modify the catch block in the analyzeFlakyTests function
(references: readJsonFromFile, generateShortSummary, failedFullTitles,
MOCHAWESOME_REPORT_DIR) to log the error and return the consistent object.</details> --- <details> <summary>ℹ️ Review info</summary> <details> <summary>⚙️ Run configuration</summary> **Configuration used**: Repository UI **Review profile**: CHILL **Plan**: Pro **Run ID**: `ef381e77-dfda-4922-902f-c3b08d65ee85` </details> <details> <summary>📥 Commits</summary> Reviewing files that changed from the base of the PR and between 3357df5215b25d9448549a9fb4dd4289051a60f4 and 4b6c60fd9d5c26f5871df234a93cf05585984f5b. </details> <details> <summary>📒 Files selected for processing (15)</summary> * `docs/superpowers/plans/2026-03-16-permissions-manager-tests.md` * `docs/superpowers/specs/2026-03-16-notification-unit-tests-design.md` * `docs/superpowers/specs/2026-03-16-permissions-manager-tests-design.md` * `e2e/specs/deep_linking/deeplink.test.js` * `e2e/specs/menu_bar/full_screen.test.js` * `e2e/specs/notification_trigger/notification_badge_windows_linux.test.js` * `e2e/specs/permissions/permissions_ipc.test.js` * `e2e/utils/analyze-flaky-test.js` * `e2e/utils/known_flaky_tests.json` * `src/app/system/badge.ts` * `src/main/notifications/Download.test.ts` * `src/main/notifications/Mention.test.ts` * `src/main/notifications/Upgrade.test.ts` * `src/main/notifications/index.test.ts` * `src/main/security/permissionsManager.test.js` </details> <details> <summary>💤 Files with no reviewable changes (1)</summary> * e2e/utils/known_flaky_tests.json </details> </details> <!-- This is an auto-generated comment by CodeRabbit for review status -->
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add beforeEach in setForServer describe to clear systemPreferences mock call history, preventing PM-U06's not.toHaveBeenCalled() from testing against cumulative calls from prior tests - Add resolvedType field to __testBadgeState computed from real branch logic, so priority/toggle E2E tests verify actual resolution not just raw inputs - Assert resolvedType in W-06/07/08/09/10 and L-04 badge tests Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
Actionable comments posted: 4
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/main/notifications/index.test.ts`:
- Line 673: The tests currently assert on the mock-only static
Notification.didConstruct which doesn't exist on Electron's Notification type
and causes TypeScript errors; replace these with a standalone Jest mock function
(e.g., notificationConstructMock) that you call from your Notification test
double/constructor replacement and assert on that mock instead of
Notification.didConstruct; update all occurrences referenced (around the
Notification constructor checks at the previous didConstruct assertions) to
call/expect notificationConstructMock (ensure the mock is created at top of the
test file and invoked inside the mocked Notification implementation used by
tests).
- Around line 489-490: Tests are returning null from mocks but the Map.get()
usages are typed to return undefined; update the mock return values to use
undefined instead of null for
WebContentsManager.getViewByWebContentsId.mockReturnValue and
WebContentsManager.getServerByWebContentsId.mockReturnValue so they match the
expected types (MattermostWebContentsView | undefined and MattermostServer |
undefined) referenced when calling NotificationManager.displayMention; locate
those mocks and replace null with undefined to satisfy TypeScript.
- Around line 573-581: The test uses the unavailable jest.runAllTimersAsync API;
replace it with the synchronous timers API (e.g., jest.runAllTimers() or
jest.advanceTimersByTime(10000)) so the pending 10s timeout in the
NotificationManager.displayMention test is executed in Jest 29.4.1; update the
test that invokes NotificationManager.displayMention (the "NM-07" it block) to
call the synchronous jest timer helper after creating the promise and before
awaiting it.
In `@src/main/notifications/Upgrade.test.ts`:
- Around line 6-7: The test currently imports the i18nManager default as
notMockedLocalizeMessage which is incorrect for TypeScript: change the import to
use the named export localizeMessage instead of the default export (the default
is the i18nManager instance without a localizeMessage property); update the
import line to import { localizeMessage } (or rename as needed) and update any
references to notMockedLocalizeMessage to use the named localizeMessage to
restore proper typing and runtime behavior for the localizeMessage function.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: 69b48067-7ab0-4f40-b0c5-3ed23a064fe9
📒 Files selected for processing (7)
e2e/specs/permissions/permissions_ipc.test.jse2e/utils/analyze-flaky-test.jssrc/main/notifications/Download.test.tssrc/main/notifications/Mention.test.tssrc/main/notifications/Upgrade.test.tssrc/main/notifications/index.test.tssrc/main/security/permissionsManager.test.js
💤 Files with no reviewable changes (1)
- e2e/utils/analyze-flaky-test.js
🚧 Files skipped from review as they are similar to previous changes (2)
- src/main/notifications/Mention.test.ts
- e2e/specs/permissions/permissions_ipc.test.js
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
src/main/security/permissionsManager.test.js (1)
119-126: Prefer testing Windows settings navigation through a public flow, not private methods.At Lines 121 and 124, the test directly invokes internal helpers, which makes it brittle to internal refactors. Consider asserting the same behavior via the public permission-handling path that triggers these helpers.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/main/security/permissionsManager.test.js` around lines 119 - 126, The test calls internal helpers openWindowsCameraPreferences and openWindowsMicrophonePreferences directly; instead exercise the public permission-handling flow on PermissionsManager that triggers those helpers (e.g., call the public method that requests or handles a 'camera' and 'microphone' permission), keep shell.openExternal mocked/spied, and assert that shell.openExternal was called with 'ms-settings:privacy-webcam' and 'ms-settings:privacy-microphone'; replace direct calls to openWindowsCameraPreferences/openWindowsMicrophonePreferences with calls to the public permission request/handler method so the spec remains stable against internal refactors.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/app/system/badge.ts`:
- Around line 130-136: The nested ternary expressions used to set resolvedType
inside the process.env.NODE_ENV === 'test' block (within the process.platform
=== 'linux' and else branches) violate the lint rule; replace those ternaries
with equivalent if/else logic to preserve behavior: for the linux branch set
resolvedType to 'mention' if mentionCount > 0, else to 'expired' if
sessionExpired, else 'none'; for the non-linux branch set resolvedType to
'mention' if mentionCount > 0, else to 'unread' if both showUnreadBadge and
showUnreadBadgeSetting are true, else to 'expired' if sessionExpired, else
'none'. Ensure you update the same resolvedType variable (declared as 'let
resolvedType: ...') and keep the surrounding process.env.NODE_ENV and
process.platform checks intact.
---
Nitpick comments:
In `@src/main/security/permissionsManager.test.js`:
- Around line 119-126: The test calls internal helpers
openWindowsCameraPreferences and openWindowsMicrophonePreferences directly;
instead exercise the public permission-handling flow on PermissionsManager that
triggers those helpers (e.g., call the public method that requests or handles a
'camera' and 'microphone' permission), keep shell.openExternal mocked/spied, and
assert that shell.openExternal was called with 'ms-settings:privacy-webcam' and
'ms-settings:privacy-microphone'; replace direct calls to
openWindowsCameraPreferences/openWindowsMicrophonePreferences with calls to the
public permission request/handler method so the spec remains stable against
internal refactors.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: c11c1186-4559-4440-a044-b0b430e59d4a
📒 Files selected for processing (3)
e2e/specs/notification_trigger/notification_badge_windows_linux.test.jssrc/app/system/badge.tssrc/main/security/permissionsManager.test.js
- Replace static Notification.didConstruct with module-level mockNotificationConstruct so assertions don't rely on a property that doesn't exist on Electron's Notification type - Change mockReturnValue(null) to undefined for getViewByWebContentsId and getServer mocks (Map.get return type is T | undefined, not null) - Replace non-existent jest.runAllTimersAsync() with jest.advanceTimersByTime(10000); configure fake timers to not fake setImmediate so await new Promise(setImmediate) can flush displayMention's async chain before advancing the clock - Fix Upgrade.test.ts to import localizeMessage as named export instead of accessing it via the default import (mock has no default export) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Change 6 nested describe callbacks from function() to arrow functions in notification_badge_windows_linux.test.js so this.app is accessible from the outer Mocha suite context in all test callbacks - Fix Download.test.ts to use named import for localizeMessage instead of default import, matching the actual export shape of i18nManager Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Design spec for migrating the Mattermost Desktop E2E test suite from electron-mocha to @playwright/test, covering fixture architecture, app readiness signalling, platform parallelism constraints, and three-phase migration plan with P0/P1 new test coverage. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Detailed step-by-step plan for migrating from electron-mocha to @playwright/test across 3 phases: foundation infrastructure, 8 new P0/P1 tests, and complete migration of all 36 legacy test files. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- webpack: fix double-dot SVG filenames ([name].[ext] → [name][ext])
- deeplink: replace waitForEvent('window') with polling for WebContentsView navigation
- focus: use cancelModal() IPC instead of page.close() to restore focus
- linux_dark_mode: use direct menu invocation instead of keyboard nav
- copy_link: increase sidebar selector timeout from 5s to 30s
- edit_menu: unify clickEditMenuItem to use direct wc API on all platforms
- file_menu: use correct menu ID (app on macOS, file elsewhere)
- full_screen: use direct menu invocation instead of three-dot + keyboard
- menu: use direct menu invocation instead of Alt + keyboard nav
- view_menu: use clickApplicationMenuItem for Find (sends Ctrl+Shift+F)
- window_menu: fix createExtraTabs() to wait for DOM and WebContentsManager
- notification_badge: reset showUnreadBadgeSetting after WIN_02 test
- drag_and_drop: poll getCurrentActiveTabView until view is registered
- tab_management: poll buildServerMap until 2 entries exist after tab create
- startup/app: add Linux platform guard for MM-T4985
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ns Linux guard, search focus, server view race
- linux_dark_mode: .topBar never gets darkMode class; fix selector to body.darkMode
since setupDarkMode() adds/removes the class on document.body, not on .topBar
- permissions E2E-P01: systemPreferences.getMediaAccessStatus is not a function on
Linux; skip the test on Linux where the API doesn't exist
- view_menu MM-T813: revert darwin to keyboard.press('Meta+f') which was passing;
clickApplicationMenuItem → openFind() → Cmd+Shift+F does not focus search-bar on macOS CI
- focus.test.ts: buildServerMap returns when any server is found, but beforeAll needs
both 'example' and 'community' registered; poll until both are present (up to 30s)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- deeplink.test.ts: extract inline try/catch arrow fn to named helper to satisfy brace-style and max-statements-per-line rules - full_screen.test.ts: remove unused mainWindow destructure from test params Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
… fallback The remove-e2e-label job only ran when pr_number was explicitly passed as input. The e2e-label-cleanup.yml fallback uses workflow_run which only fires from workflows on the default branch, so it never triggered on this branch. Fix: remove the pr_number guard (if: always() without the != '' check) and add an inline branch-name lookup when pr_number is not provided, matching the same logic used in removeE2ELabel(). No file checkout needed — pure GitHub API calls via github-script. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Use cross-env in run:policy script so RUN_POLICY_E2E=true works on Windows cmd.exe (inline KEY=val syntax is bash-only) - Switch CI reporter from dot to line for readable per-test output in GitHub Actions logs instead of cryptic symbols Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The remove-e2e-label job was getting HTTP 403 despite declaring issues:write. The GitHub API response showed pull_requests=write is also accepted for label operations on PRs. The Mattermost org appears to restrict GITHUB_TOKEN issues scope for workflow_dispatch on non-default branches, but pull-requests scope works. Add pull-requests:write to both e2e-functional.yml and e2e-label-cleanup.yml so the token has the necessary access. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
PowerShell Remove-Item -ErrorAction SilentlyContinue still exits with
non-zero on Windows 2025 GitHub runners, causing execFileSync to throw.
Replace with if (Test-Path ...) { Remove-Item ... } which always exits 0.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- deeplink: guard afterAll against undefined userDataDir when beforeAll skips on non-Windows - focus: add focusMainWindow() helper and call it in beforeEach to ensure OS-level window focus in CI - copy_link: increase timeout to 15s and handle both 'Copy Link' / 'Copy link' capitalizations - view_menu: wait for search-bar focus via waitForFunction after Cmd+F to avoid timing race - window_menu: fall back to getAllWindows() when getFocusedWindow() returns null in headless CI - notification_badge_in_dock: gracefully skip when CustomizeYourExperienceTour element is absent - popout_windows: fall back to getAllWindows() when getFocusedWindow() returns null in headless CI - bad_servers: add waitForRendererThenReload() to fix race where SSL failure fires before onLoadFailed listener registers - drag_and_drop: replace getCurrentActiveTabView() with buildServerMap polling to fix IPC ordering race Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…e state isolation - focus.test.ts: increase waitForFunction timeout from 5s to 15s for Windows focus delivery - view_menu.test.ts: increase waitForFunction timeout from 5s to 15s for async search bar - drag_and_drop.test.ts: use toContainText with timeout instead of reading innerText immediately - window_menu.test.ts: use toContainText for active tab assertion; add 15s timeout to all expect.poll(getActiveTabTitle) calls to accommodate Windows IPC timing - notification_badge_windows_linux.test.ts: add top-level beforeEach to reset showUnreadBadgeSetting to false and clear __testBadgeState before each test, preventing state bleed when MM-T_BADGE_WIN_08 fails before its inline reset runs Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…, policy, tab_management, deeplink - file_menu MM-T805/MM-T804: replace three-dot-menu keyboard navigation with direct electronApp.evaluate() menu invocation — keyboard events miss popup menus in headless CI - full_screen MM-T816: use __e2eTestRefs.MainWindow.get() instead of getAllWindows()[0] when polling for fullscreen state, so we track the correct BrowserWindow - policy MM-T_GPO_1: wait for .ServerDropdownButton to render before reading its text - tab_management MM-TXXXX_1: add 15s timeout to waitForSelector calls for #sidebarItem_off-topic and .serverTabItem__close, which render slowly on Windows CI - deeplink MM-T1304/MM-T1306: replace 2s fixed delay + single retry with expect.poll (15s timeout) to reliably wait for the github server view to register in buildServerMap Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…nd artifact warning - focus.test.ts: rewrite focusMainWindow to use __e2eTestRefs.MainWindow.get() and call show() before focus() so isFocused() returns true on headless Linux - window_menu.test.ts: prefer __e2eTestRefs.MainWindow.get() over getAllWindows().find() as menu click target to avoid targeting wrong window - popout_windows.test.ts: same fix for clickFileMenuItem's target window - e2e-functional.yml: remove if-no-files-found from actions/download-artifact step (not a valid input for that action, only valid for upload-artifact) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds a new workflow (e2e-pr-trigger.yml) that fires on pull_request events (opened, reopened, ready_for_review, synchronize) against master and automatically adds the E2E/Run label when the PR is not in draft status. The label is removed-then-re-added on every event so the labeled webhook always fires — even when tests are already running for a previous commit. This ensures Matterwick provisions servers and triggers e2e-functional.yml for every qualifying commit without manual intervention. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Cancel all in_progress/queued/waiting Electron Playwright Tests runs
before re-adding the E2E/Run label on synchronize events.
Matterwick dispatches e2e-functional.yml via workflow_dispatch with
the default branch as ref (head_branch=master for all runs), making
PR-specific filtering unreliable — so all non-terminal runs are
cancelled, which is correct since only one labelled PR triggers tests
at a time.
- Add concurrency group (e2e-pr-trigger-{PR}) with cancel-in-progress so
rapid successive pushes collapse into a single label operation.
- Add actions: write permission for the cancel API call.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- focus.test.ts: skip Focus textbox tests on Linux and in CI environments
where BrowserWindow.isFocused() is unreliable (headless Xvfb on Linux,
GitHub Actions macOS runners); MattermostWebContentsView.focus() silently
no-ops when isFocused() returns false so these tests require a real
interactive desktop session
- window_menu.test.ts: retry clickWindowMenuItem on 'not found' errors so
the 15 s deadline absorbs the async menu rebuild MenuManager does after
TAB_ADDED fires (CmdOrCtrl+2 accelerator may not exist yet immediately
after createExtraTabs)
- popout_windows.test.ts: drop URL predicate from waitForEvent('window');
Playwright fires the event at BrowserWindow creation before PopoutManager
calls loadURL, so the URL is still blank when the predicate runs; now
catch any new window then waitForURL('**/popout.html')
- drag_and_drop.test.ts: increase sidebar selector timeouts 15 s → 30 s
and toContainText timeouts 10 s → 15 s for newly created tab views that
take longer to render the channel list on slow CI runners
- tab_management.test.ts: same sidebar selector timeout increase 15 s → 30 s
- helpers/menu.ts: add __e2eTestRefs.MainWindow.get() fallback in
clickApplicationMenuItem; BrowserWindow.getFocusedWindow() returns null
on headless CI so getAllWindows()[0] previously returned the wrong window
(dropdown/modal), causing DevTools to open on the wrong target (MM-T820)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This PR completes the Mattermost Desktop E2E migration away from the legacy Electron test stack and standardizes on Playwright.
It does three main things:
Highlights
Why this is better