const { test, expect } = require('@playwright/test'); const { openClassic, showPlayingFixture } = require('./helpers/classic-fixture'); for (const layout of ['classic', 'glass-stage']) { for (const width of [390, 1440]) { for (const route of ['mini player', 'close', 'player jump', 'browser back', 'track reveal'].filter(route => width === 390 || route !== 'player jump')) { test(`${layout} ${width}: ${route} restores the playing list`, async ({ page }) => { await page.setViewportSize({ width, height: 844 }); await openClassic(page, { populated: true, settings: { autoPreload: false } }); await showPlayingFixture(page); await page.evaluate(layout => { document.documentElement.dataset.layout = layout; for (const type of ['search', 'history', 'saved', 'downloads', 'channel', 'settings', 'search']) { view = { type }; if (type === 'search') setHomeOverPlayer(true); render(); } showMiniBar(); }, layout); // Chromium is not an installed phone PWA. Expose its mini-player // chrome while exercising the actual click listeners (no forced click). await page.addStyleTag({ content: '#miniBar { display:flex !important; }' }); if (route === 'close') await page.locator('#miniCloseBtn').click(); else if (route === 'track reveal') { // User-picked tracks/deep links share this reveal hook after loading; // avoid testing network/media decoding with synthetic fixture ids. await page.evaluate(() => scrollPlayerIntoViewPortrait()); } else { await page.locator('#miniTitle').click(); if (route === 'player jump') { await page.evaluate(() => { view = { type: 'history' }; render(); }); await page.locator('#sectionRail [data-sec="player"]').click(); } else if (route === 'browser back') { await page.evaluate(() => { view = { type: 'history' }; render(); }); await page.goBack(); await expect(page.locator('#listTitle')).toHaveText('Studio sessions'); await page.goBack(); await expect(page.locator('#playerPane')).toHaveClass(/home-over/); await page.goForward(); } } await expect(page.locator('#upnext')).toBeVisible(); await expect(page.locator('#upnextList .upnext-item')).toHaveCount(11); await expect(page.locator('#listPane')).toBeVisible(); await expect(page.locator('#listTitle')).toHaveText('Studio sessions'); await expect(page.locator('#cards .card')).toHaveCount(12); expect(await page.evaluate(() => ({ source: queueSource, index: queueIndex, size: queue.length, id: current.meta.id }))) .toEqual({ source: 'playlist:studio', index: 0, size: 12, id: 'preview0000' }); }); } } test(`${layout}: missing source playlist falls back to the live queue`, async ({ page }) => { await page.setViewportSize({ width: 390, height: 844 }); await openClassic(page, { populated: true, settings: { autoPreload: false } }); await showPlayingFixture(page); await page.evaluate(layout => { document.documentElement.dataset.layout = layout; data.playlists = []; data.queue = []; view = { type: 'downloads' }; render(); scrollPlayerIntoViewPortrait(); }, layout); await expect(page.locator('#listTitle')).toHaveText('Now playing'); await expect(page.locator('#cards .card')).toHaveCount(12); await expect(page.locator('#upnextList .upnext-item')).toHaveCount(11); const historyLength = await page.evaluate(() => history.length); // Re-entry must repair stale DOM even when home-over is already false. await page.evaluate(() => { document.getElementById('upnextList').innerHTML = ''; scrollPlayerIntoViewPortrait(); }); await expect(page.locator('#upnextList .upnext-item')).toHaveCount(11); expect(await page.evaluate(() => history.length)).toBe(historyLength); await page.evaluate(() => { queueIndex = queue.length - 1; scrollPlayerIntoViewPortrait(); }); await expect(page.locator('#upnext')).toBeHidden(); await expect(page.locator('#cards .card')).toHaveCount(12); }); }