73 lines
3.1 KiB
JavaScript
73 lines
3.1 KiB
JavaScript
/**
|
|
* Smoke test for the background-audio-resume watchdog (Task #50).
|
|
*
|
|
* iOS Safari — most visibly in PWA/standalone mode on iPhone — can silently
|
|
* pause a background <audio> element (screen lock, app switch) without the
|
|
* app reacting, while a muted foreground <video> element keeps rolling. This
|
|
* doesn't try to simulate real OS audio suspension (Playwright/Chromium
|
|
* can't), but it does verify:
|
|
* 1. The new visibilitychange/pageshow/focus listeners don't throw and are
|
|
* wired up without breaking page load.
|
|
* 2. Player.resumeIfNeeded() actually resumes a sounding element that is
|
|
* paused while intent (_wantsPlaying) is true, and leaves it alone when
|
|
* intent is false (a real user pause).
|
|
*/
|
|
const { test, expect } = require('@playwright/test');
|
|
|
|
test.describe('Background audio resume watchdog', () => {
|
|
test.beforeEach(async ({ page }) => {
|
|
await page.goto('/');
|
|
await page.waitForSelector('.app', { state: 'attached' });
|
|
});
|
|
|
|
test('page loads with no JS errors and the new listeners are wired', async ({ page }) => {
|
|
const errors = [];
|
|
page.on('pageerror', (e) => errors.push(e.message));
|
|
|
|
await page.evaluate(() => {
|
|
document.dispatchEvent(new Event('visibilitychange'));
|
|
window.dispatchEvent(new Event('pageshow'));
|
|
window.dispatchEvent(new Event('focus'));
|
|
});
|
|
|
|
expect(errors).toEqual([]);
|
|
});
|
|
|
|
test('resumeIfNeeded() resumes a silently-paused sounding element while intent is true', async ({ page }) => {
|
|
const result = await page.evaluate(async () => {
|
|
const audio = document.getElementById('audio');
|
|
// Simulate a loaded audio-only track without going through the native
|
|
// bridge (not available in this headless smoke context).
|
|
audio.src = 'data:audio/mpeg;base64,//uQxAAAAAAAAAAAAAAAAAAAAAAASW5mbwAAAA8AAAACAAAJmwAeHh4eHh4eHh4eLi4uLi4uLi4uLi5AQEBAQEBAQEBAQFJSUlJSUlJSUlJSZGRkZGRkZGRkZGR2dnZ2dnZ2dnZ2dg==';
|
|
Player.mode = 'audio';
|
|
Player.master = audio;
|
|
Player.secondary = null;
|
|
Player._wantsPlaying = true;
|
|
audio.pause(); // simulate iOS silently pausing the sounding element
|
|
const pausedBefore = audio.paused;
|
|
Player.resumeIfNeeded();
|
|
// play() is async; give the browser a tick to apply it.
|
|
await new Promise((r) => setTimeout(r, 150));
|
|
return { pausedBefore, pausedAfter: audio.paused };
|
|
});
|
|
expect(result.pausedBefore).toBe(true);
|
|
expect(result.pausedAfter).toBe(false);
|
|
});
|
|
|
|
test('resumeIfNeeded() leaves a real user pause alone', async ({ page }) => {
|
|
const result = await page.evaluate(async () => {
|
|
const audio = document.getElementById('audio');
|
|
audio.src = 'data:audio/mpeg;base64,//uQxAAAAAAAAAAAAAAAAAAAAAAASW5mbwAAAA8AAAACAAAJmwAeHh4eHh4eHh4eLi4uLi4uLi4uLi5AQEBAQEBAQEBAQFJSUlJSUlJSUlJSZGRkZGRkZGRkZGR2dnZ2dnZ2dnZ2dg==';
|
|
Player.mode = 'audio';
|
|
Player.master = audio;
|
|
Player.secondary = null;
|
|
Player._wantsPlaying = false; // user explicitly paused
|
|
audio.pause();
|
|
Player.resumeIfNeeded();
|
|
await new Promise((r) => setTimeout(r, 150));
|
|
return { paused: audio.paused };
|
|
});
|
|
expect(result.paused).toBe(true);
|
|
});
|
|
});
|