const { test, expect } = require('@playwright/test'); const { openClassic, showPlayingFixture } = require('./helpers/classic-fixture'); for (const layout of ['classic', 'glass-stage']) test(`${layout}: portrait forced landscape controls and restore`, async ({ page }, testInfo) => { await page.setViewportSize({ width: 390, height: 844 }); await openClassic(page, { populated: true }); await showPlayingFixture(page); await page.evaluate(layout => { data.settings.layout = layout; data.settings.fullscreenOrientation = 'landscape'; applyAppearance(); els.video.parentElement.requestFullscreen = undefined; }, layout); await page.locator('#fsBtn').click(); const stage = page.locator('.orientation-overlay'); await expect(stage).toHaveAttribute('data-fullscreen-rotation', '90'); await expect.poll(async () => Math.round((await stage.boundingBox()).height)).toBe(844); const rect = await stage.boundingBox(); expect(Math.abs(rect.width - 390)).toBeLessThan(2); expect(Math.abs(rect.height - 844)).toBeLessThan(2); for (const button of await stage.locator('.fullscreen-hud button').all()) { const r = await button.boundingBox(); expect(r.x).toBeGreaterThanOrEqual(0); expect(r.y).toBeGreaterThanOrEqual(0); expect(r.x + r.width).toBeLessThanOrEqual(391); expect(r.y + r.height).toBeLessThanOrEqual(845); } await page.screenshot({ path: testInfo.outputPath(`${layout}-landscape.png`) }); await stage.getByRole('button', { name: 'Change fullscreen orientation' }).click(); await expect(stage).toHaveAttribute('data-fullscreen-rotation', '0'); await stage.getByRole('button', { name: 'Exit fullscreen' }).click(); await expect(page.locator('.orientation-overlay')).toHaveCount(0); await expect(page.locator('.fullscreen-hud')).toBeHidden(); }); test('standard fullscreen requests the selected orientation and releases it', async ({ page }) => { await page.setViewportSize({ width: 390, height: 844 }); await openClassic(page, { populated: true }); await showPlayingFixture(page); await page.evaluate(() => { data.settings.fullscreenOrientation = 'landscape'; window.orientationCalls = []; const stage = els.video.parentElement; stage.requestFullscreen = async () => { Object.defineProperty(document, 'fullscreenElement', { configurable: true, value: stage }); }; document.exitFullscreen = async () => { Object.defineProperty(document, 'fullscreenElement', { configurable: true, value: null }); }; Object.defineProperty(screen, 'orientation', { configurable: true, value: { lock: async value => orientationCalls.push(value), unlock: () => orientationCalls.push('unlock') } }); }); await page.locator('#fsBtn').click(); await expect.poll(() => page.evaluate(() => orientationCalls)).toEqual(['landscape']); await page.locator('.fullscreen-hud').getByRole('button', { name: 'Exit fullscreen' }).click(); expect(await page.evaluate(() => orientationCalls)).toEqual(['landscape', 'unlock']); }); test('browser fullscreen exit restores a CSS fallback after orientation lock rejects', async ({ page }) => { await page.setViewportSize({ width: 390, height: 844 }); await openClassic(page, { populated: true }); await showPlayingFixture(page); await page.evaluate(() => { data.settings.fullscreenOrientation = 'landscape'; const stage = els.video.parentElement; stage.requestFullscreen = async () => { Object.defineProperty(document, 'fullscreenElement', { configurable: true, value: stage }); }; Object.defineProperty(screen, 'orientation', { configurable: true, value: { lock: async () => { throw new Error('Unsupported'); } } }); }); await page.locator('#fsBtn').click(); await expect(page.locator('.orientation-overlay')).toHaveAttribute('data-fullscreen-rotation', '90'); await page.evaluate(() => { Object.defineProperty(document, 'fullscreenElement', { configurable: true, value: null }); document.dispatchEvent(new Event('fullscreenchange')); }); await expect(page.locator('.orientation-overlay')).toHaveCount(0); await expect(page.locator('.fullscreen-hud')).toBeHidden(); });