test(magazine): cover driving seek controls
This commit is contained in:
parent
9904459913
commit
c81907ccfe
@ -52,7 +52,7 @@ Two self-hosted Docker instances, both port 7890:
|
||||
| Studio (phase wizard) | — | `studio.js` | mature |
|
||||
| TTS engines / routing | `routes/tts.py`, `core/routing.py` | `engines.js` | mature |
|
||||
| STT | `routes/stt.py` | — | mature |
|
||||
| **Magazines** | `core/magazine.py`, `routes/magazine.py` | `magazine.js`, `s-magazine.html` | **P0 + P1** |
|
||||
| **Magazines** | `core/magazine.py`, `routes/magazine.py` | `magazine.js`, `s-magazine.html` | **P0 + P1**; generated-MP3 playlist and driving-mode seek/±15 s transport |
|
||||
|
||||
## Data model
|
||||
|
||||
|
||||
@ -258,9 +258,12 @@ works regardless.
|
||||
|
||||
`#mag-driving` is a full-screen overlay, not a separate playback path: it
|
||||
reuses whatever is already playing or queued, so entering it never
|
||||
interrupts an article in progress. Two oversized controls (skip, play/pause)
|
||||
and no other interactive surface, matching the "no interaction required"
|
||||
behaviour the manual originally specified as a P2 goal.
|
||||
interrupts an article in progress. Its touch-sized transport exposes the
|
||||
previous/next article, back/forward 15 seconds, play/pause, and direct seeking
|
||||
on the shared playlist `<audio>` element. Seeking is bounded to the current
|
||||
track. Playback speed and the optional synchronized text/image companion are
|
||||
browser-local preferences; none of these controls changes server-side article
|
||||
data or creates a second playback state.
|
||||
|
||||
### Automatic sidebar/advert exclusion
|
||||
|
||||
|
||||
@ -217,6 +217,46 @@ test.describe('Magazine reader', () => {
|
||||
await expect(exportPicker.locator('.vp-item[data-value="smoke_voice"]')).toBeVisible();
|
||||
});
|
||||
|
||||
test('driving mode seeks and skips 15 seconds within the current article', async ({ page }) => {
|
||||
await mockBackend(page, { articleStatus: 'approved', hasAudio: true });
|
||||
await page.goto('/', { waitUntil: 'domcontentloaded' });
|
||||
await page.waitForSelector('#app-skeleton', { state: 'detached', timeout: 15_000 });
|
||||
await navTo(page, 's-magazine');
|
||||
await page.locator('#mag-library-list .mag-card').first().click();
|
||||
await page.locator('#mag-driving-open').click();
|
||||
await expect(page.locator('#mag-driving')).toBeVisible();
|
||||
|
||||
// Use a deterministic two-minute track so the transport controls can be
|
||||
// exercised without depending on the browser's media decoder clock.
|
||||
await page.locator('#mag-playlist-audio').evaluate((audio) => {
|
||||
audio.pause();
|
||||
Object.defineProperties(audio, {
|
||||
duration: { configurable: true, value: 120 },
|
||||
currentTime: { configurable: true, writable: true, value: 60 },
|
||||
});
|
||||
});
|
||||
const currentTime = () => page.locator('#mag-playlist-audio').evaluate((audio) => audio.currentTime);
|
||||
|
||||
await page.getByRole('button', { name: 'Back 15 seconds', exact: true }).click();
|
||||
await expect.poll(currentTime).toBe(45);
|
||||
await page.getByRole('button', { name: 'Forward 15 seconds', exact: true }).click();
|
||||
await expect.poll(currentTime).toBe(60);
|
||||
|
||||
await page.locator('#mag-playlist-audio').evaluate((audio) => { audio.currentTime = 5; });
|
||||
await page.getByRole('button', { name: 'Back 15 seconds', exact: true }).click();
|
||||
await expect.poll(currentTime).toBe(0);
|
||||
await page.locator('#mag-playlist-audio').evaluate((audio) => { audio.currentTime = 115; });
|
||||
await page.getByRole('button', { name: 'Forward 15 seconds', exact: true }).click();
|
||||
await expect.poll(currentTime).toBe(120);
|
||||
|
||||
const seek = page.getByRole('slider', { name: 'Position in article' });
|
||||
await seek.evaluate((input) => {
|
||||
input.value = '250';
|
||||
input.dispatchEvent(new Event('input', { bubbles: true }));
|
||||
});
|
||||
await expect.poll(currentTime).toBe(30);
|
||||
});
|
||||
|
||||
test('library lists a magazine and opens its contents tree', async ({ page }) => {
|
||||
await mockBackend(page);
|
||||
await page.goto('/', { waitUntil: 'domcontentloaded' });
|
||||
|
||||
Loading…
Reference in New Issue
Block a user