Add PDF_PARSER_VERSION constant and propagate parser versioning throughout the PDF parsing, job, and API layers. Implement normalization of parse state to ensure compatibility with the current parser version, and enable reuse of parsed PDF results when possible. Add isPlaybackReady state to document hooks and TTS player, improving playback UX by disabling controls until content is ready. Update tests to reflect new playback readiness logic. BREAKING CHANGE: PDF parse state and job logic now require explicit parserVersion; older parse states may be treated as pending until reprocessed.
126 lines
4.6 KiB
TypeScript
126 lines
4.6 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('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);
|
|
});
|
|
});
|