68 lines
2.9 KiB
JavaScript
68 lines
2.9 KiB
JavaScript
/**
|
|
* Smoke test for the layout-viewport anchor guard.
|
|
*
|
|
* Bug: on an iPhone running the installed PWA, playing a video sometimes left
|
|
* the bottom-nav buttons unresponsive. iOS WebKit can scroll the document's
|
|
* layout viewport behind the app's back (exiting native video fullscreen,
|
|
* keyboard dismissal, scrollIntoView walking up into <html>) even though the
|
|
* body is overflow:hidden. Fixed elements are then still drawn in place but
|
|
* their hit-testing regions are offset by the stray scroll amount, so taps on
|
|
* the nav do nothing — and no user gesture can scroll the document back.
|
|
*
|
|
* The guard (setupViewportAnchorGuard in app.js) snaps the document back to 0
|
|
* whenever it ends up scrolled, except while an input is focused (so it never
|
|
* fights the on-screen keyboard); it re-anchors on blur instead.
|
|
*
|
|
* The static test page doesn't overflow, so each test injects a tall spacer
|
|
* and relaxes the overflow clamp — simulating the scrollable-document state
|
|
* iOS leaves behind.
|
|
*/
|
|
const { test, expect } = require('@playwright/test');
|
|
|
|
test.describe('Viewport anchor guard — stray document scroll', () => {
|
|
test.beforeEach(async ({ page }) => {
|
|
await page.goto('/');
|
|
await page.waitForSelector('.app', { state: 'attached' });
|
|
await page.evaluate(() => {
|
|
const spacer = document.createElement('div');
|
|
spacer.style.height = '3000px';
|
|
document.body.appendChild(spacer);
|
|
document.documentElement.style.overflow = 'visible';
|
|
document.body.style.overflow = 'visible';
|
|
});
|
|
});
|
|
|
|
test('snaps the document back to 0 after a stray window scroll', async ({ page }) => {
|
|
await page.evaluate(() => {
|
|
// boot() focuses the search input; release it so the guard is active.
|
|
if (document.activeElement) document.activeElement.blur();
|
|
window.scrollTo(0, 400);
|
|
});
|
|
await page.waitForFunction(() => window.scrollY === 0);
|
|
expect(await page.evaluate(() => window.scrollY)).toBe(0);
|
|
});
|
|
|
|
test('leaves the scroll alone while an input is focused (keyboard), re-anchors on blur', async ({ page }) => {
|
|
await page.evaluate(() => {
|
|
document.getElementById('searchInput').focus();
|
|
window.scrollTo(0, 300);
|
|
});
|
|
// Guard must not fight the keyboard-driven scroll while editing.
|
|
await page.waitForTimeout(200);
|
|
expect(await page.evaluate(() => window.scrollY)).toBeGreaterThan(0);
|
|
|
|
await page.evaluate(() => document.getElementById('searchInput').blur());
|
|
await page.waitForFunction(() => window.scrollY === 0);
|
|
});
|
|
|
|
test('re-anchors when native video fullscreen exits (webkitendfullscreen)', async ({ page }) => {
|
|
await page.evaluate(() => {
|
|
if (document.activeElement) document.activeElement.blur();
|
|
window.scrollTo(0, 250);
|
|
document.getElementById('video').dispatchEvent(new Event('webkitendfullscreen'));
|
|
});
|
|
await page.waitForFunction(() => window.scrollY === 0);
|
|
expect(await page.evaluate(() => window.scrollY)).toBe(0);
|
|
});
|
|
});
|