openreader/tests/navigation.spec.ts

143 lines
5.3 KiB
TypeScript

import { test, expect, type Page } from '@playwright/test';
import {
setupTest,
uploadFiles,
ensureDocumentsListed,
clickDocumentLink,
expectViewerForFile,
uploadAndDisplay,
playTTSAndWaitForASecond,
expectProcessingTransition,
} from './helpers';
// Single-spec helpers kept local to avoid cluttering shared helpers:
async function navigateToPdfPageViaNavigator(page: Page, targetPage: number) {
// Navigator popover shows "X / Y"
const navTrigger = page.getByRole('button', { name: /\d+\s*\/\s*\d+/ });
await expect(navTrigger).toBeVisible({ timeout: 10000 });
await navTrigger.click();
const input = page.getByLabel('Page number');
await expect(input).toBeVisible({ timeout: 10000 });
await input.fill(String(targetPage));
await input.press('Enter');
}
async function countRenderedPdfPages(page: Page): Promise<number> {
return await page.locator('.react-pdf__Page').count();
}
async function triggerViewportResize(page: Page, width: number, height: number) {
await page.setViewportSize({ width, height });
}
test.describe('Document link navigation by type', () => {
test.beforeEach(async ({ page }, testInfo) => {
await setupTest(page, testInfo);
});
test('navigates to /pdf, /epub, /html and renders correct viewers', async ({ page }) => {
test.setTimeout(120_000);
// Upload documents
await uploadFiles(page, 'sample.pdf', 'sample.epub', 'sample.txt');
// Ensure links exist
await ensureDocumentsListed(page, ['sample.pdf', 'sample.epub', 'sample.txt']);
// PDF
await clickDocumentLink(page, 'sample.pdf');
await expectViewerForFile(page, 'sample.pdf');
await page.goBack();
// EPUB
await clickDocumentLink(page, 'sample.epub');
await expectViewerForFile(page, 'sample.epub');
await page.goBack();
// TXT (HTML viewer)
await clickDocumentLink(page, 'sample.txt');
await expectViewerForFile(page, 'sample.txt');
});
});
test.describe('PDF view modes and Navigator', () => {
test.beforeEach(async ({ page }, testInfo) => {
await setupTest(page, testInfo);
});
test('restores the active PDF text highlight after narrowing the viewport', async ({ page }) => {
test.setTimeout(120_000);
await playTTSAndWaitForASecond(page, 'sample.pdf');
const textLayer = page.locator('.react-pdf__Page__textContent');
const textHighlight = page.locator('.pdf-text-highlight-overlay');
await expect(textLayer).toHaveCount(1);
await expect(textLayer.locator('span')).not.toHaveCount(0);
await expect(textHighlight).not.toHaveCount(0);
await triggerViewportResize(page, 615, 796);
await expect(textLayer).toHaveCount(1);
await expect(textLayer.locator('span')).not.toHaveCount(0);
await expect(textHighlight).not.toHaveCount(0);
});
test('switches Single/Dual/Scroll modes and uses Navigator to change page', async ({ page }) => {
test.setTimeout(120_000);
// Open PDF viewer
await uploadAndDisplay(page, 'sample.pdf');
// Open document settings (page-level settings)
await page.getByRole('button', { name: 'Open settings' }).click();
// Page mode now uses pill-style radios; switch from Single Page to Two Pages
await page.getByRole('radiogroup', { name: 'Page mode' }).getByRole('radio', { name: 'Two Pages' }).click();
await page.getByRole('button', { name: 'Close' }).click();
// Expect dual-page rendering (sample.pdf has >= 2 pages)
const dualCount = await countRenderedPdfPages(page);
expect(dualCount).toBeGreaterThanOrEqual(2);
// Switch to Continuous Scroll
await page.getByRole('button', { name: 'Open settings' }).click();
await page.getByRole('radiogroup', { name: 'Page mode' }).getByRole('radio', { name: 'Continuous Scroll' }).click();
await page.getByRole('button', { name: 'Close' }).click();
// Expect continuous scroll renders at least as many pages as dual mode
const scrollCount = await countRenderedPdfPages(page);
expect(scrollCount).toBeGreaterThanOrEqual(dualCount);
// Use Navigator to go to a page (clamps to last if too large)
await navigateToPdfPageViaNavigator(page, 999);
// Navigator jump is configured to pause; ensure Play is visible then resume
await expect(page.getByRole('button', { name: 'Play' })).toBeVisible({ timeout: 10000 });
await page.getByRole('button', { name: 'Play' }).click();
await expectProcessingTransition(page);
});
});
test.describe('EPUB resize pauses TTS', () => {
test.beforeEach(async ({ page }, testInfo) => {
await setupTest(page, testInfo);
});
test('resizing viewport pauses playback and play resumes after', async ({ page }) => {
// Start playback on EPUB
await playTTSAndWaitForASecond(page, 'sample.epub');
// Trigger a significant viewport resize to fire useEPUBResize
await triggerViewportResize(page, 1200, 900);
await page.waitForTimeout(750); // allow resize flag to propagate
await triggerViewportResize(page, 900, 700);
await page.waitForTimeout(750);
// After resize, playback should have paused (Play button visible)
const playButton = page.getByRole('button', { name: 'Play' });
await expect(playButton).toBeVisible({ timeout: 15000 });
await expect(playButton).toBeEnabled({ timeout: 15000 });
// Resume playback and ensure processing -> playing
await playButton.click();
await expectProcessingTransition(page);
});
});