tests: harden pdf readiness waits and extend tts/pdf timeouts

This commit is contained in:
Richard R 2026-05-24 20:58:57 -06:00
parent 3f17874fe5
commit e70b3619e0
8 changed files with 31 additions and 15 deletions

View file

@ -443,7 +443,7 @@ export default function PDFViewerPage() {
</div>
) : null}
{isLoading || !isParseReady || !isPdfViewerReady ? (
<div className="absolute inset-0 z-10">
<div className="absolute inset-0 z-10" data-testid="pdf-status-loader">
{renderPdfStatusLoader()}
</div>
) : null}

View file

@ -53,6 +53,7 @@ test.describe('Accessibility smoke', () => {
});
test('TTS controls expose aria labels and are keyboard focusable', async ({ page }) => {
test.setTimeout(120_000);
await uploadAndDisplay(page, 'sample.pdf');
// TTS bar present

View file

@ -271,7 +271,7 @@ async function resetAudiobookIfPresent(page: Page, bookId?: string) {
}
test('exports full MP3 audiobook for PDF using mocked 10s TTS sample', async ({ page }, testInfo) => {
test.setTimeout(60_000);
test.setTimeout(120_000);
// Ensure TTS is mocked and app is ready
await setupTest(page, testInfo);
@ -382,7 +382,7 @@ test('exports partial MP3 audiobook for EPUB using mocked 10s TTS sample', async
});
test('exports a single MP3 audiobook PDF page via chapters menu', async ({ page }, testInfo) => {
test.setTimeout(60_000);
test.setTimeout(120_000);
await setupTest(page, testInfo);
await uploadAndDisplay(page, 'sample.pdf');
@ -413,6 +413,7 @@ test('exports a single MP3 audiobook PDF page via chapters menu', async ({ page
});
test('resets all MP3 audiobook PDF pages', async ({ page }, testInfo) => {
test.setTimeout(120_000);
await setupTest(page, testInfo);
await uploadAndDisplay(page, 'sample.pdf');
@ -451,7 +452,7 @@ test('resets all MP3 audiobook PDF pages', async ({ page }, testInfo) => {
});
test('regenerates a single MP3 audiobook PDF page and exports full audiobook', async ({ page }, testInfo) => {
test.setTimeout(60_000);
test.setTimeout(120_000);
await setupTest(page, testInfo);
await uploadAndDisplay(page, 'sample.pdf');
@ -525,7 +526,7 @@ test('regenerates a single MP3 audiobook PDF page and exports full audiobook', a
});
test('resumes audiobook when a chapter is missing and full download succeeds (PDF)', async ({ page }, testInfo) => {
test.setTimeout(60_000);
test.setTimeout(120_000);
await setupTest(page, testInfo);
await uploadAndDisplay(page, 'sample.pdf');

View file

@ -24,17 +24,23 @@ function sha256HexOfFile(filePath: string) {
async function waitForPdfViewerReady(page: Page, timeout = 60000) {
await expect(page).toHaveURL(/\/pdf\/[A-Za-z0-9._%-]+$/, { timeout: Math.min(timeout, 20000) });
const loader = page.getByTestId('pdf-status-loader').first();
const parseFailedBanner = page.getByText('PDF parsing failed. Retry to continue.').first();
await expect
.poll(async () => {
const parseFailed = await parseFailedBanner.isVisible().catch(() => false);
if (parseFailed) return 'failed';
const documentVisible = await page.locator('.react-pdf__Document').first().isVisible().catch(() => false);
if (documentVisible) return true;
const pageVisible = await page.locator('.react-pdf__Page').first().isVisible().catch(() => false);
if (pageVisible) return true;
const canvasVisible = await page.locator('.react-pdf__Page canvas').first().isVisible().catch(() => false);
if (canvasVisible) return true;
return false;
const hasRenderedPdf = documentVisible || pageVisible || canvasVisible;
const loaderVisible = await loader.isVisible().catch(() => false);
if (hasRenderedPdf && !loaderVisible) return 'ready';
return 'loading';
}, { timeout })
.toBe(true);
.toBe('ready');
}
/**
@ -396,7 +402,7 @@ export async function expectViewerForFile(page: Page, fileName: string) {
if (lower.endsWith('.pdf') || lower.endsWith('.docx')) {
// DOCX converts to PDF, so viewer expectations are PDF
await expect(page).toHaveURL(/\/pdf\/[A-Za-z0-9._%-]+$/);
await expect(page.locator('.react-pdf__Document')).toBeVisible({ timeout: 15000 });
await waitForPdfViewerReady(page, 60000);
return;
}
if (lower.endsWith('.epub')) {

View file

@ -37,6 +37,7 @@ test.describe('Landing and app routing', () => {
});
test('documents back link returns to /app', async ({ page }, testInfo) => {
test.setTimeout(120_000);
await setupTest(page, testInfo);
await uploadAndDisplay(page, 'sample.pdf');

View file

@ -37,6 +37,7 @@ test.describe('Document link navigation by type', () => {
});
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');
@ -65,10 +66,9 @@ test.describe('PDF view modes and Navigator', () => {
});
test('switches Single/Dual/Scroll modes and uses Navigator to change page', async ({ page }) => {
test.setTimeout(60_000);
test.setTimeout(120_000);
// Open PDF viewer
await uploadAndDisplay(page, 'sample.pdf');
await expect(page.locator('.react-pdf__Document')).toBeVisible({ timeout: 15000 });
// Open document settings (page-level settings)
await page.getByRole('button', { name: 'Open settings' }).click();

View file

@ -45,6 +45,7 @@ test.describe('Play/Pause Tests', () => {
test.describe.configure({ mode: 'serial', timeout: 60000 });
test('plays and pauses TTS for a PDF document', async ({ page }) => {
test.setTimeout(120_000);
// Play TTS for the PDF document
await playTTSAndWaitForASecond(page, 'sample.pdf');
@ -61,6 +62,7 @@ test.describe('Play/Pause Tests', () => {
});
test('plays and pauses TTS for an DOCX document', async ({ page }) => {
test.setTimeout(120_000);
// Play TTS for the DOCX document
await playTTSAndWaitForASecond(page, 'sample.docx');
@ -77,6 +79,7 @@ test.describe('Play/Pause Tests', () => {
});
test('switches to a single voice and resumes playing', async ({ page }) => {
test.setTimeout(120_000);
// Start playback
await playTTSAndWaitForASecond(page, 'sample.pdf');
@ -96,6 +99,7 @@ test.describe('Play/Pause Tests', () => {
});
test('keeps selected single voice instead of resetting to first option', async ({ page }) => {
test.setTimeout(120_000);
await playTTSAndWaitForASecond(page, 'sample.pdf');
await openVoicesMenu(page);
@ -122,6 +126,7 @@ test.describe('Play/Pause Tests', () => {
});
if (!process.env.CI) test('selects multiple Kokoro voices and resumes playing', async ({ page }) => {
test.setTimeout(120_000);
// Start playback
await playTTSAndWaitForASecond(page, 'sample.pdf');
@ -142,6 +147,7 @@ test.describe('Play/Pause Tests', () => {
});
test('changing TTS native speed toggles processing and returns to playing', async ({ page }) => {
test.setTimeout(120_000);
await playTTSAndWaitForASecond(page, 'sample.pdf');
await changeNativeSpeedAndAssert(page, 1.5);
await expectMediaState(page, 'playing');

View file

@ -88,10 +88,10 @@ test.describe('Document Upload Tests', () => {
});
test('displays a PDF document', async ({ page }) => {
test.setTimeout(120_000);
await uploadAndDisplay(page, 'sample.pdf');
await expectViewerForFile(page, 'sample.pdf');
// Additional content checks specific to the sample PDF
await expect(page.locator('.react-pdf__Page')).toBeVisible();
await expect(page.getByRole('heading', { level: 1, name: 'sample.pdf' })).toBeVisible();
await expect(page.getByRole('button', { name: /1\s*\/\s*2/ })).toBeVisible();
});
@ -105,10 +105,10 @@ test.describe('Document Upload Tests', () => {
});
test('displays a DOCX document as PDF after conversion', async ({ page }) => {
test.setTimeout(120_000);
await uploadAndDisplay(page, 'sample.docx');
await expectViewerForFile(page, 'sample.docx'); // DOCX converts to PDF
// Keep specific content checks
await expect(page.locator('.react-pdf__Page')).toBeVisible();
await expect(page.getByText('Demonstration of DOCX')).toBeVisible();
});
@ -119,6 +119,7 @@ test.describe('Document Upload Tests', () => {
});
test('uploads PDF/EPUB/TXT and opens correct viewer for each', async ({ page }) => {
test.setTimeout(120_000);
// Upload multiple files
await uploadFiles(page, 'sample.pdf', 'sample.epub', 'sample.txt');