diff --git a/system/public/mime/index.html b/system/public/mime/index.html
index b0afe42aec..b451a23ad9 100644
--- a/system/public/mime/index.html
+++ b/system/public/mime/index.html
@@ -28,6 +28,8 @@
+
+
` });
+ if (u.pathname.startsWith('/fixture/')) return route.fulfill({ contentType: 'image/svg+xml', body: '' });
+ return route.fulfill({ status: 200, body: '' });
+ });
+ const page = await context.newPage();
+ const errors = []; page.on('pageerror', e => errors.push(e.message));
+ try {
+ await page.goto('https://mime.ac/?ac-automation=1');
+ await page.locator('#wide img').waitFor();
+ await page.waitForFunction(() => document.querySelector('#wide img')?.naturalWidth === 800);
+ assert.ok(!requests.includes('/fixture/deferred'), 'offscreen text does not load eagerly');
+ const imageBox = await page.locator('#wide .feed-media').boundingBox();
+ assert.ok(Math.abs(imageBox.width / (imageBox.height - 2) - 800 / 300) < .05, 'image uses natural proportions');
+ for (const id of ['program', 'document', 'longtext']) {
+ const frame = page.locator(`#${id} .feed-media`);
+ await frame.evaluate(el => el.scrollIntoView({ block: 'center' }));
+ await page.waitForTimeout(450);
+ const box = await frame.boundingBox();
+ const y = Math.min(800, box.y + box.height / 2);
+ await page.mouse.move(box.x + box.width / 2, y);
+ const before = await page.evaluate(() => scrollY);
+ await page.mouse.wheel(0, 180);
+ await page.waitForFunction(previous => scrollY > previous + 60, before);
+ }
+ const program = page.locator('#program .feed-media');
+ await program.evaluate(el => el.scrollIntoView({ block: 'center' }));
+ await page.waitForTimeout(500);
+ await page.locator('#program .media-toggle').click();
+ assert.equal(await program.getAttribute('data-interacting'), 'true');
+ await page.locator('#program iframe').waitFor();
+ await page.frameLocator('#program iframe').getByRole('button', { name: 'Program button', exact: true }).click();
+ await page.frameLocator('#program iframe').getByRole('button', { name: 'clicked', exact: true }).waitFor();
+ await page.locator('#program .media-toggle').click();
+ assert.equal(await program.getAttribute('data-interacting'), null);
+ assert.equal(await page.locator('#program .piece-preview').evaluate(el => el.inert), true);
+ await page.locator('#document .media-toggle').click();
+ await page.locator('#longtext').evaluate(el => el.scrollIntoView({ block: 'start' }));
+ await page.waitForTimeout(1100);
+ assert.equal(await page.locator('#document .feed-media').getAttribute('data-interacting'), null, 'scrolling away re-locks media');
+ await page.locator('#deferred').scrollIntoViewIfNeeded();
+ await page.waitForFunction(() => document.querySelector('#deferred pre')?.textContent.startsWith('A long'));
+ await page.setViewportSize({ width: 390, height: 844 });
+ await program.evaluate(el => el.scrollIntoView({ block: 'center' }));
+ await page.waitForTimeout(500);
+ const box = await program.boundingBox(); const x = box.x + box.width / 2, y = box.y + box.height / 2;
+ const before = await page.evaluate(() => scrollY);
+ const cdp = await context.newCDPSession(page);
+ await cdp.send('Input.dispatchTouchEvent', { type: 'touchStart', touchPoints: [{ x, y }] });
+ for (let i = 1; i <= 6; i++) {
+ await cdp.send('Input.dispatchTouchEvent', { type: 'touchMove', touchPoints: [{ x, y: y - i * 25 }] });
+ await page.waitForTimeout(30);
+ }
+ await cdp.send('Input.dispatchTouchEvent', { type: 'touchEnd', touchPoints: [] });
+ await page.waitForFunction(previous => scrollY > previous + 50, before);
+ await page.evaluate(() => scrollTo(0, 0));
+ if (process.env.MIME_SCREENSHOT_DIR) {
+ await mkdir(process.env.MIME_SCREENSHOT_DIR, { recursive: true });
+ await page.screenshot({ path: `${process.env.MIME_SCREENSHOT_DIR}/mime-mobile.png` });
+ await page.setViewportSize({ width: 900, height: 900 });
+ await page.screenshot({ path: `${process.env.MIME_SCREENSHOT_DIR}/mime-desktop.png` });
+ }
+ assert.deepEqual(errors, []);
+ } finally { await context.close(); await browser.close(); }
+});