diff --git a/apps/electron-backend-e2e/src/electron-unautomated-launch.ts b/apps/electron-backend-e2e/src/electron-unautomated-launch.ts index ee29ff715..7abb8ad19 100644 --- a/apps/electron-backend-e2e/src/electron-unautomated-launch.ts +++ b/apps/electron-backend-e2e/src/electron-unautomated-launch.ts @@ -55,9 +55,18 @@ async function openCdpSocket(url: string): Promise { ); }); socket.addEventListener('message', (event) => { - const message = JSON.parse(String(event.data)); - pending.get(message.id)?.(message); - pending.delete(message.id); + const message = JSON.parse(String(event.data)) as Record< + string, + unknown + >; + // Only replies to our own numbered requests settle a promise; events + // and unknown ids are ignored. + const id = message['id']; + if (typeof id !== 'number') return; + const settle = pending.get(id); + if (typeof settle !== 'function') return; + pending.delete(id); + settle(message); }); return { send: (method, params = {}) => diff --git a/apps/electron-backend-e2e/src/window-visibility.e2e.ts b/apps/electron-backend-e2e/src/window-visibility.e2e.ts index 5810607c1..ae323c541 100644 --- a/apps/electron-backend-e2e/src/window-visibility.e2e.ts +++ b/apps/electron-backend-e2e/src/window-visibility.e2e.ts @@ -1,3 +1,5 @@ +import { join } from 'node:path'; +import { pathToFileURL } from 'node:url'; import { expect, test } from './electron-test-fixtures'; import { launchUnautomatedElectronApp, @@ -9,6 +11,10 @@ import { const isWindowManagerlessCi = process.platform === 'linux' && !!process.env['CI']; +const videoFixtureUrl = pathToFileURL( + join(__dirname, '../../web-e2e/src/fixtures/playback/episode.webm') +).href; + const visibility = (app: UnautomatedElectronApp) => app.evaluateInPage('document.visibilityState'); @@ -92,4 +98,50 @@ test.describe('Main window visibility', () => { await app.close(); } }); + + test('@electron releases the playback display lock while the window is hidden', async ({ + dataDir, + }) => { + const app = await launchUnautomatedElectronApp(dataDir); + try { + await waitUntilShown(app); + // Record the display-sleep blockers the keep-awake service holds. + await app.evaluateInMain(` + const blocker = electron.powerSaveBlocker; + const active = (globalThis.__e2eDisplayBlockers = new Set()); + const start = blocker.start.bind(blocker); + const stop = blocker.stop.bind(blocker); + blocker.start = (type) => { + const id = start(type); + if (type === 'prevent-display-sleep') active.add(id); + return id; + }; + blocker.stop = (id) => { + active.delete(id); + return stop(id); + }; + `); + const heldBlockers = () => + app.evaluateInMain( + 'return globalThis.__e2eDisplayBlockers.size;' + ); + await app.evaluateInPage(`(() => { + const video = document.createElement('video'); + video.muted = true; + video.loop = true; + video.src = ${JSON.stringify(videoFixtureUrl)}; + document.body.append(video); + return video.play().then(() => true); + })()`); + await expect.poll(heldBlockers, { timeout: 10_000 }).toBe(1); + + await setWindowState(app, 'hide'); + await expect.poll(heldBlockers, { timeout: 10_000 }).toBe(0); + + await setWindowState(app, 'show'); + await expect.poll(heldBlockers, { timeout: 10_000 }).toBe(1); + } finally { + await app.close(); + } + }); });