From 1e301ab1dfd8e0c4970ca68cd9d62a00596d2f16 Mon Sep 17 00:00:00 2001 From: Neil <4138956+nwparker@users.noreply.github.com> Date: Sun, 6 Sep 2026 19:02:54 -0700 Subject: [PATCH] test: cover native Wayland Hangul in isolated CI (#19174) * test: exercise native Wayland Hangul in isolated CI session * test: wait for nested compositor socket before selecting IBus * test: align Wayland IBus discovery with GNOME environment filtering * test: assert Wayland launch and register native Hangul evidence --- .github/workflows/terminal-ime-e2e.yml | 37 ++++ config/reliability-gates.jsonc | 91 ++++++++ .../scripts/focus-nested-wayland-terminal.sh | 11 + config/scripts/pr-e2e-source-routing.mjs | 2 +- .../scripts/run-terminal-ibus-hangul-e2e.mjs | 199 ++++++++++++++---- .../terminal-ime-e2e-workflow.test.mjs | 15 ++ ...al-hangul-terminating-digit-native.spec.ts | 2 + 7 files changed, 312 insertions(+), 45 deletions(-) create mode 100755 config/scripts/focus-nested-wayland-terminal.sh diff --git a/.github/workflows/terminal-ime-e2e.yml b/.github/workflows/terminal-ime-e2e.yml index 1ab905d8783..bd6be26bd27 100644 --- a/.github/workflows/terminal-ime-e2e.yml +++ b/.github/workflows/terminal-ime-e2e.yml @@ -70,3 +70,40 @@ jobs: path: test-results/ retention-days: 7 if-no-files-found: ignore + + linux-wayland: + name: Linux Wayland Hangul terminating digit + runs-on: ubuntu-22.04 + timeout-minutes: 25 + steps: + - uses: actions/checkout@v6 + with: + persist-credentials: false + - name: Install native build, nested compositor and IME tools + run: >- + sudo apt-get update && sudo apt-get install -y + build-essential python3 fonts-noto-cjk dbus-x11 dconf-gsettings-backend + ibus ibus-hangul gnome-shell gnome-settings-daemon libglib2.0-bin + xdotool xvfb x11-utils imagemagick + - uses: ./.github/actions/install-node-dependencies + with: + native-runtime: electron + - name: Build Electron app for E2E + env: + VITE_EXPOSE_STORE: 'true' + run: | + pnpm run build:relay + pnpm exec electron-vite build --mode e2e + pnpm run build:web-from-renderer + - name: Run native Wayland Hangul terminating digit + env: + SKIP_BUILD: '1' + run: node config/scripts/run-terminal-ibus-hangul-e2e.mjs --nested-wayland + - name: Upload Wayland terminal IME evidence + if: always() + uses: actions/upload-artifact@v7 + with: + name: terminal-wayland-ime-evidence + path: test-results/ + retention-days: 7 + if-no-files-found: error diff --git a/config/reliability-gates.jsonc b/config/reliability-gates.jsonc index ede7c46c751..2bcba7cb737 100644 --- a/config/reliability-gates.jsonc +++ b/config/reliability-gates.jsonc @@ -18573,6 +18573,97 @@ "Other released version pairs remain untested; not a required PR check." ], "demotionRule": "Keep experimental if any direction skips or fails; do not extend timeouts or retry to green." + }, + { + "id": "terminal-input.native-wayland-hangul-digit", + "title": "Native Wayland Hangul terminating digits reach the PTY exactly once", + "maturity": "experimental", + "protection": "partial", + "owner": "terminal-input", + "layer": "electron-native-ime-e2e", + "surfaces": [ + "native Hangul composition", + "Wayland terminal input" + ], + "platforms": [ + "linux" + ], + "providers": [ + "local" + ], + "coveredPlatforms": [ + "linux" + ], + "coveredProviders": [ + "local" + ], + "coverageNotes": "Ubuntu 22.04 nested GNOME and IBus Hangul drive three complete native executions in GitHub Actions. GNOME owns IBus; daemon and CLI share its default config discovery path.", + "motivatingLinks": [ + "https://github.com/stablyai/orca/pull/19174" + ], + "invariant": "Typing d k 1 Return through native IBus Hangul delivers exactly 아1 followed by newline without missing, duplicate, or reordered characters.", + "oracle": "Three executions each assert three exact UTF-8 PTY lines. Verify the exact Playwright title, zero skips/retries, each individual native composition receipt, and the nested launch Wayland flag.", + "commands": [ + "gh workflow run terminal-ime-e2e.yml", + "gh run view 34074017928 --log", + "pnpm exec playwright test --config tests/playwright.config.ts tests/e2e/terminal-hangul-terminating-digit-native.spec.ts --project=electron-headful --workers=1 --repeat-each=3 --retries=0 --reporter=list,json", + "ORCA_BACKGROUND_LAUNCH=1 node_modules/.bin/vitest run --config config/vitest.config.ts config/scripts/terminal-ime-e2e-workflow.test.mjs" + ], + "testFiles": [ + "tests/e2e/terminal-hangul-terminating-digit-native.spec.ts", + "config/scripts/terminal-ime-e2e-workflow.test.mjs" + ], + "assertionRefs": [ + { + "file": "tests/e2e/terminal-hangul-terminating-digit-native.spec.ts", + "assertions": [ + "a digit typed right after a Hangul syllable reaches the pty" + ] + }, + { + "file": "config/scripts/terminal-ime-e2e-workflow.test.mjs", + "assertions": [ + "runs native Wayland independently with CJK fonts and retained evidence" + ] + } + ], + "evidenceRuns": [ + { + "date": "2026-09-07", + "runner": "ci", + "platform": "linux", + "command": "gh run view 34074017928 --log", + "result": "passed", + "summary": "Permanent runner passed three native executions, nine exact lines and zero skips/retries. Downloaded participation report and all three engagement receipts verified; compositor cleanup reported no remaining group members. Independent X11 job passed.", + "durationSeconds": 76.61 + } + ], + "runtimeBudget": { + "p95Seconds": 1500, + "scope": "CI job timeout including installation/build; measured p95 not established" + }, + "flakeHistory": { + "status": "soaking", + "evidence": "Earlier diagnostic repetition had one unexplained missing Hangul commit. GNOME-owned diagnostic and corrected permanent runner each passed 3/3. Long-term soak is missing." + }, + "redGreenEvidence": { + "status": "partial", + "evidence": "Original exact-byte assertions retained. Permanent startup failed until the GNOME config-discovery mismatch was corrected. No intentional production regression was introduced." + }, + "performanceBudget": { + "required": false, + "evidence": "CI-only harness; no application runtime changes." + }, + "promotionCriteria": [ + "Collect 100 soak runs across 14 days with no unexplained flakes.", + "Exercise native Wayland desktops beyond nested GNOME before broadening the claim." + ], + "knownGaps": [ + "Only Hangul terminating digits; no native candidate-selection or other input-method coverage claim.", + "No macOS, Windows, SSH terminal, packaged build, or mixed-version claim.", + "Default config paths are shared with GNOME on a disposable hosted CI runner; nested mode refuses non-GitHub-Actions execution." + ], + "demotionRule": "Keep experimental on unexplained failures; retain exact bytes and participation checks without retries, skips, or longer deadlines." } ] } diff --git a/config/scripts/focus-nested-wayland-terminal.sh b/config/scripts/focus-nested-wayland-terminal.sh new file mode 100755 index 00000000000..6d75699c54c --- /dev/null +++ b/config/scripts/focus-nested-wayland-terminal.sh @@ -0,0 +1,11 @@ +#!/usr/bin/env bash +set -euo pipefail +[[ "${GITHUB_ACTIONS:-}" == true ]] +# The isolated X server owns exactly one nested compositor window. +mapfile -t windows < <(xwininfo -root -tree | awk '$2 == "\"gnome-shell\":" {print $1}') +[[ ${#windows[@]} -eq 1 ]] +xdotool windowmap --sync "${windows[0]}" +xdotool windowfocus --sync "${windows[0]}" +read -r width height < <(xwininfo -id "${windows[0]}" | awk '$1 == "Width:" {w=$2} $1 == "Height:" {print w,$2}') +# The native spec opens a single terminal; a seat click activates its Wayland client. +xdotool mousemove --window "${windows[0]}" "$((width / 2))" "$((height / 2))" click 1 diff --git a/config/scripts/pr-e2e-source-routing.mjs b/config/scripts/pr-e2e-source-routing.mjs index 308f1dfdaa3..3b8f2e90afb 100644 --- a/config/scripts/pr-e2e-source-routing.mjs +++ b/config/scripts/pr-e2e-source-routing.mjs @@ -10,7 +10,7 @@ const NATIVE_IME_PRODUCT_SOURCE = /** The harness itself: the session runner, the boundary probes, and the native specs. */ const NATIVE_IME_HARNESS = - /^(?:config\/scripts\/(?:run-terminal-ibus-hangul-e2e|terminal-ime-engagement-receipt)\.mjs$|tests\/e2e\/terminal-ime-(?:boundary-probe|byte-reader|engagement-receipt)\.ts$|tests\/e2e\/terminal-(?:ibus-hangul|hangul-terminating-digit|macos-2set-korean)-native\.spec\.ts$)/ + /^(?:config\/scripts\/focus-nested-wayland-terminal\.sh$|config\/scripts\/(?:run-terminal-ibus-hangul-e2e|terminal-ime-engagement-receipt)\.mjs$|tests\/e2e\/terminal-ime-(?:boundary-probe|byte-reader|engagement-receipt)\.ts$|tests\/e2e\/terminal-(?:ibus-hangul|hangul-terminating-digit|macos-2set-korean)-native\.spec\.ts$)/ export const PR_E2E_SOURCE_ROUTES = [ { diff --git a/config/scripts/run-terminal-ibus-hangul-e2e.mjs b/config/scripts/run-terminal-ibus-hangul-e2e.mjs index 669f7744b39..572f1988629 100644 --- a/config/scripts/run-terminal-ibus-hangul-e2e.mjs +++ b/config/scripts/run-terminal-ibus-hangul-e2e.mjs @@ -9,6 +9,7 @@ import { readFileSync, writeFileSync } from 'node:fs' +import { verifyPlaywrightParticipation } from './verify-playwright-participation.mjs' import os from 'node:os' import path from 'node:path' import { @@ -20,6 +21,9 @@ import { const projectDir = path.resolve(import.meta.dirname, '../..') const scriptPath = import.meta.filename const insideSessionFlag = '--inside-session' +const nestedWaylandFlag = '--nested-wayland' +const nestedWayland = process.argv.includes(nestedWaylandFlag) +const waylandTitle = 'a digit typed right after a Hangul syllable reaches the pty' const processStopTimeoutMs = 5_000 const processKillTimeoutMs = 1_000 @@ -111,26 +115,38 @@ function configureHangulEngine() { } } -async function waitForHangulEngine(ibusProcess) { +async function waitForHangulEngine(sessionProcess) { + let lastError = '' const deadline = Date.now() + 15_000 while (Date.now() < deadline) { - if (ibusProcess.exitCode !== null) { - throw new Error(`ibus-daemon exited early with code ${ibusProcess.exitCode}`) + if (sessionProcess.exitCode !== null) { + throw new Error(`IME session process exited early with code ${sessionProcess.exitCode}`) } - const result = spawnSync('ibus', ['engine', 'hangul'], { stdio: 'pipe' }) + if ( + nestedWayland && + !existsSync(path.join(process.env.XDG_RUNTIME_DIR, process.env.WAYLAND_DISPLAY)) + ) { + await delay(100) + continue + } + const result = spawnSync('ibus', ['engine', 'hangul'], { encoding: 'utf8' }) + lastError = result.stderr?.trim() || String(result.error ?? result.status) if (result.status === 0) { return } await delay(100) } - throw new Error('Timed out while selecting the IBus Hangul engine') + throw new Error(`Timed out while selecting the IBus Hangul engine: ${lastError}`) } async function runInsideSession(evidenceDir) { const receiptPath = path.join(evidenceDir, 'ime-engagement-receipt.jsonl') const ibusLogPath = path.join(evidenceDir, 'ibus-daemon.log') const ibusLogFd = openSync(ibusLogPath, 'w') - const windowManagerLogPath = path.join(evidenceDir, 'xfwm4.log') + const windowManagerLogPath = path.join( + evidenceDir, + nestedWayland ? 'gnome-shell.log' : 'xfwm4.log' + ) const windowManagerLogFd = openSync(windowManagerLogPath, 'w') const evidence = { display: process.env.DISPLAY ?? null, @@ -147,32 +163,58 @@ async function runInsideSession(evidenceDir) { try { configureHangulEngine() - windowManagerProcess = spawn('xfwm4', ['--compositor=off'], { - detached: true, - env: process.env, - stdio: ['ignore', windowManagerLogFd, windowManagerLogFd] - }) - if (!windowManagerProcess.pid) { - throw new Error('xfwm4 did not return a PID') - } - evidence.windowManagerPid = windowManagerProcess.pid - console.error(`[terminal-ime] started xfwm4 PID ${windowManagerProcess.pid}`) - - ibusProcess = spawn( - 'ibus-daemon', - ['--xim', '--verbose', '--panel=disable', '--emoji-extension=disable'], - { + if (nestedWayland) { + for (const [schema, key, value] of [ + ['org.gnome.desktop.interface', 'enable-animations', 'false'], + ['org.gnome.desktop.input-sources', 'sources', "[('ibus', 'hangul')]"] + ]) { + const result = spawnSync('gsettings', ['set', schema, key, value], { encoding: 'utf8' }) + if (result.status !== 0) { + throw new Error(`Failed to configure GNOME: ${result.stderr}`) + } + } + windowManagerProcess = spawn( + 'gnome-shell', + ['--nested', '--wayland', `--wayland-display=${process.env.WAYLAND_DISPLAY}`], + { + detached: true, + env: process.env, + stdio: ['ignore', windowManagerLogFd, windowManagerLogFd] + } + ) + } else { + windowManagerProcess = spawn('xfwm4', ['--compositor=off'], { detached: true, env: process.env, - stdio: ['ignore', ibusLogFd, ibusLogFd] + stdio: ['ignore', windowManagerLogFd, windowManagerLogFd] + }) + } + if (!windowManagerProcess.pid) { + throw new Error('Window manager did not return a PID') + } + evidence.windowManagerPid = windowManagerProcess.pid + console.error(`[terminal-ime] started window manager PID ${windowManagerProcess.pid}`) + + if (nestedWayland) { + // GNOME starts IBus in the private session; a second daemon can compete for ownership. + await waitForHangulEngine(windowManagerProcess) + } else { + ibusProcess = spawn( + 'ibus-daemon', + ['--xim', '--verbose', '--panel=disable', '--emoji-extension=disable'], + { + detached: true, + env: process.env, + stdio: ['ignore', ibusLogFd, ibusLogFd] + } + ) + if (!ibusProcess.pid) { + throw new Error('ibus-daemon did not return a PID') } - ) - if (!ibusProcess.pid) { - throw new Error('ibus-daemon did not return a PID') + evidence.ibusDaemonPid = ibusProcess.pid + console.error(`[terminal-ime] started ibus-daemon PID ${ibusProcess.pid}`) + await waitForHangulEngine(ibusProcess) } - evidence.ibusDaemonPid = ibusProcess.pid - console.error(`[terminal-ime] started ibus-daemon PID ${ibusProcess.pid}`) - await waitForHangulEngine(ibusProcess) console.error(`[terminal-ime] IBus version: ${commandOutput('ibus', ['version'])}`) console.error(`[terminal-ime] IBus engine: ${commandOutput('ibus', ['engine'])}`) console.error( @@ -189,23 +231,49 @@ async function runInsideSession(evidenceDir) { 'hangul-keyboard' ])}` ) - evidence.ibusGroupBeforeCleanup = processGroupMembers(ibusProcess.pid) + evidence.ibusGroupBeforeCleanup = ibusProcess?.pid ? processGroupMembers(ibusProcess.pid) : [] console.error(`[terminal-ime] owned IBus group: ${evidence.ibusGroupBeforeCleanup.join('; ')}`) const testProcess = spawn( process.platform === 'win32' ? 'pnpm.cmd' : 'pnpm', - [ - 'run', - 'test:e2e:headful', - '--workers=1', - '--', - 'tests/e2e/terminal-ibus-hangul-native.spec.ts', - 'tests/e2e/terminal-hangul-terminating-digit-native.spec.ts' - ], + nestedWayland + ? [ + 'exec', + 'playwright', + 'test', + '--config', + 'tests/playwright.config.ts', + 'tests/e2e/terminal-hangul-terminating-digit-native.spec.ts', + '--project=electron-headful', + '--workers=1', + '--repeat-each=3', + '--retries=0', + '--reporter=list,json' + ] + : [ + 'run', + 'test:e2e:headful', + '--workers=1', + '--', + 'tests/e2e/terminal-ibus-hangul-native.spec.ts', + 'tests/e2e/terminal-hangul-terminating-digit-native.spec.ts' + ], { cwd: projectDir, env: { ...process.env, + ...(nestedWayland + ? { + ORCA_E2E_IME_INJECTOR: 'nested', + ORCA_E2E_NESTED_FOCUS_CMD: path.join( + projectDir, + 'config/scripts/focus-nested-wayland-terminal.sh' + ), + ORCA_E2E_EXTRA_APP_ARGS: + '--ozone-platform=wayland --enable-wayland-ime --wayland-text-input-version=3 --password-store=basic --use-mock-keychain --disable-gpu-sandbox', + PLAYWRIGHT_JSON_OUTPUT_FILE: path.join(evidenceDir, 'playwright.json') + } + : {}), ORCA_E2E_FORWARD_APP_LOGS: '1', ORCA_E2E_NATIVE_IBUS_HANGUL: '1', [IME_ENGAGEMENT_RECEIPT_ENV]: receiptPath, @@ -232,6 +300,13 @@ async function runInsideSession(evidenceDir) { windowManagerProcess.pid ) } + if (nestedWayland && existsSync(path.join(evidenceDir, 'playwright.json'))) { + mkdirSync(path.join(projectDir, 'test-results'), { recursive: true }) + copyFileSync( + path.join(evidenceDir, 'playwright.json'), + path.join(projectDir, 'test-results', 'terminal-wayland-playwright.json') + ) + } closeSync(ibusLogFd) closeSync(windowManagerLogFd) mkdirSync(path.join(projectDir, 'test-results'), { recursive: true }) @@ -241,7 +316,11 @@ async function runInsideSession(evidenceDir) { ) copyFileSync( windowManagerLogPath, - path.join(projectDir, 'test-results', 'terminal-ibus-hangul-native-xfwm4.log') + path.join( + projectDir, + 'test-results', + nestedWayland ? 'terminal-wayland-gnome-shell.log' : 'terminal-ibus-hangul-native-xfwm4.log' + ) ) writeFileSync( path.join(projectDir, 'test-results', 'terminal-ibus-hangul-native-processes.json'), @@ -269,6 +348,23 @@ async function runInsideSession(evidenceDir) { // Why unconditionally, and not only when Playwright failed: a skipped test reports as a pass, // so exit code 0 is exactly the state this check exists to distrust. const receiptText = existsSync(receiptPath) ? readFileSync(receiptPath, 'utf8') : '' + if (nestedWayland) { + verifyPlaywrightParticipation( + JSON.parse(readFileSync(path.join(evidenceDir, 'playwright.json'), 'utf8')), + { titles: [waylandTitle], label: 'Native Wayland Hangul', repetitions: 3 } + ) + const receipts = receiptText.trim().split('\n') + if (receipts.length !== 3) { + throw new Error('Expected three native Wayland engagement receipts') + } + for (const receipt of receipts) { + const problems = verifyImeEngagementReceipts(receipt, [waylandTitle]) + if (problems.length) { + throw new Error(problems.join('\n')) + } + } + return testExitCode + } const engagementProblems = verifyImeEngagementReceipts(receiptText, EXPECTED_NATIVE_IME_TESTS) if (engagementProblems.length > 0) { for (const problem of engagementProblems) { @@ -288,7 +384,7 @@ async function runInsideSession(evidenceDir) { async function runOuter() { if (process.platform !== 'linux') { - throw new Error('The native IBus Hangul E2E runner requires Linux/X11') + throw new Error('The native IBus Hangul E2E runner requires Linux') } const evidenceDir = mkdtempSync(path.join(os.tmpdir(), 'orca-terminal-ime-e2e-')) @@ -302,24 +398,36 @@ async function runOuter() { 'xvfb-run', [ '--auto-servernum', + ...(nestedWayland ? ['--server-args=-screen 0 1280x800x24'] : []), 'dbus-run-session', '--', process.execPath, scriptPath, insideSessionFlag, - evidenceDir + evidenceDir, + ...(nestedWayland ? [nestedWaylandFlag] : []) ], { cwd: projectDir, detached: true, env: { ...process.env, + ...(nestedWayland + ? { + WAYLAND_DISPLAY: 'wayland-orca-ime', + XDG_SESSION_TYPE: 'wayland', + XDG_CURRENT_DESKTOP: 'GNOME', + LIBGL_ALWAYS_SOFTWARE: '1', + NO_AT_BRIDGE: '1' + } + : {}), GTK_IM_MODULE: 'ibus', IBUS_ENABLE_SYNC_MODE: '1', LANG: process.env.LANG || 'C.UTF-8', QT_IM_MODULE: 'ibus', - XDG_CACHE_HOME: path.join(evidenceDir, 'cache'), - XDG_CONFIG_HOME: path.join(evidenceDir, 'config'), + // GNOME 42 drops XDG_CONFIG_HOME when spawning IBus; both must use its default path. + XDG_CACHE_HOME: nestedWayland ? undefined : path.join(evidenceDir, 'cache'), + XDG_CONFIG_HOME: nestedWayland ? undefined : path.join(evidenceDir, 'config'), XDG_RUNTIME_DIR: runtimeDir, XMODIFIERS: '@im=ibus' }, @@ -329,17 +437,20 @@ async function runOuter() { if (!sessionProcess.pid) { throw new Error('xvfb-run did not return a PID') } - console.error(`[terminal-ime] started isolated X11 session PID ${sessionProcess.pid}`) + console.error(`[terminal-ime] started isolated display session PID ${sessionProcess.pid}`) const exitCode = await waitForExit(sessionProcess) const remaining = await stopOwnedProcessGroup(sessionProcess.pid) if (remaining.length > 0) { - throw new Error(`Owned X11 session processes survived cleanup: ${remaining.join('; ')}`) + throw new Error(`Owned display session processes survived cleanup: ${remaining.join('; ')}`) } return exitCode } const insideSession = process.argv[2] === insideSessionFlag try { + if (nestedWayland && process.env.GITHUB_ACTIONS !== 'true') { + throw new Error('Nested Wayland native input validation runs only in GitHub Actions') + } if (insideSession && !process.argv[3]) { throw new Error(`${insideSessionFlag} requires an evidence directory argument`) } diff --git a/config/scripts/terminal-ime-e2e-workflow.test.mjs b/config/scripts/terminal-ime-e2e-workflow.test.mjs index 96062ebe706..65277b5e898 100644 --- a/config/scripts/terminal-ime-e2e-workflow.test.mjs +++ b/config/scripts/terminal-ime-e2e-workflow.test.mjs @@ -68,6 +68,21 @@ describe('terminal IME e2e workflow', () => { expect(runner).not.toContain('pkill') }) + it('runs native Wayland independently with CJK fonts and retained evidence', () => { + const job = workflow.jobs['linux-wayland'] + expect(job.needs).toBeUndefined() + const install = job.steps.find((step) => step.run?.includes('apt-get install')).run + for (const tool of ['gnome-shell', 'ibus-hangul', 'fonts-noto-cjk', 'xwininfo']) { + expect(install).toContain(tool === 'xwininfo' ? 'x11-utils' : tool) + } + expect(job.steps.find((step) => step.run?.includes('--nested-wayland')).run).toBe( + 'node config/scripts/run-terminal-ibus-hangul-e2e.mjs --nested-wayland' + ) + const upload = job.steps.find((step) => step.uses?.startsWith('actions/upload-artifact')) + expect(upload.if).toBe('always()') + expect(upload.with.name).toBe('terminal-wayland-ime-evidence') + }) + it('bounds blocking native input commands', () => { const nativeSpec = readFileSync( join(projectDir, 'tests/e2e/terminal-ibus-hangul-native.spec.ts'), diff --git a/tests/e2e/terminal-hangul-terminating-digit-native.spec.ts b/tests/e2e/terminal-hangul-terminating-digit-native.spec.ts index 4344f94adaf..9d0127d4df4 100644 --- a/tests/e2e/terminal-hangul-terminating-digit-native.spec.ts +++ b/tests/e2e/terminal-hangul-terminating-digit-native.spec.ts @@ -190,6 +190,8 @@ test.describe('Hangul terminating digit @headful', () => { })) console.log(`[digit-diag] ${JSON.stringify(launchDiagnostics)}`) if (INJECTOR === 'nested') { + expect(launchDiagnostics.ozonePlatform).toBe('wayland') + expect(launchDiagnostics.waylandDisplay).toBeTruthy() // Under Wayland the app's ready-to-show never fires here, so the window // stays hidden and the compositor has nothing to give keyboard focus to. await electronApp.evaluate(({ BrowserWindow }) => {