diff --git a/docs/RELEASE-PLAN.md b/docs/RELEASE-PLAN.md index d383e53..073d516 100644 --- a/docs/RELEASE-PLAN.md +++ b/docs/RELEASE-PLAN.md @@ -47,6 +47,7 @@ Workers coordinate protocol changes directly. Only the coordinator changes Git s - Minimum-version exports with translucent diagram backgrounds also require captured page colors. Resolved background/foreground variables now apply to both the exported body and content. Contrast assertions composite actual ancestor backgrounds and pass on both supported-version test targets. - Initial GitHub CI passed all three OS build/type/unit jobs and stable VS Code E2E. Minimum-version Linux screenshots exposed a test harness mismatch: the emulated viewport was 1,440 pixels wide but the native window was 1,024, so right-side mouse targets fell outside it. The harness now sizes the actual BrowserWindow and CI provides a 1,920-pixel display. Both corrected CI runs passed all jobs on `dd88879`. - The final automated review identified an IME composition-completion gap and persisted CI checkout credentials. Search now schedules the committed composition value and cancels pending searches while composing; CI and release checkouts remove credentials before subsequent steps. The follow-up commit must pass its own checks before merge. +- PR #13 merged after both final CI runs passed, and its exact packaged VSIX passed rendering, HTML, PDF and IME smoke checks. The post-merge release gate then caught a stable-VS-Code PDF export still pending at the test's 30-second deadline. Trace evidence confirms the correct save path and no visible error; exporter logs were missing, so it cannot distinguish slow Chromium from a hang. PDF tests now allow 60 seconds for the two browser processes and retain isolated application logs on failure; assertions remain unchanged and automatic retries remain disabled. - The candidate VSIX contains 19 files (about 3 MB), with manifest version 0.5.0 and bundled outputs byte-matching the production build. Sources, tests, node_modules, maps, declarations and demo GIFs are excluded. ## Research sources @@ -67,5 +68,6 @@ Workers coordinate protocol changes directly. Only the coordinator changes Git s - Consider a persistent preview lock and richer Markdown workspace navigation after current lifecycle behavior is tested. - Evaluate additional export layout controls against real authoring needs after export safety and fidelity are verified. - Consider an explicit print-friendly palette option; exports now preserve the captured preview background/foreground to keep translucent diagram backgrounds legible. +- Add an explicit wall-clock deadline and cancellation/progress for PDF browser subprocesses; Chromium's virtual-time budget does not bound real process duration. - Consider architectural extraction from the large preview controller where it simplifies future changes; avoid a broad rewrite in this delivery. - Evaluate publishing identity modernization when account configuration is available; keep release authentication aligned with official guidance. diff --git a/test/e2e/export.e2e.test.ts b/test/e2e/export.e2e.test.ts index cacf470..f381a6f 100644 --- a/test/e2e/export.e2e.test.ts +++ b/test/e2e/export.e2e.test.ts @@ -71,7 +71,9 @@ test('exports a PDF from a cold preview through the real command and Chromium re const target = join(vscode.workspace, 'journey.pdf'); await saveInput.fill(target); await saveInput.press('Enter'); - const pdf = await waitForFile(target); + // PDF export starts two Chromium processes; virtual-time budgets do not bound + // their wall time on a busy CI worker. Keep a finite deadline for the real file. + const pdf = await waitForFile(target, 60_000); expect(pdf.subarray(0, 5).toString()).toBe('%PDF-'); expect(pdf.length).toBeGreaterThan(5000); expect(pdf.subarray(-1024).toString()).toContain('%%EOF'); diff --git a/test/e2e/vscodeFixture.ts b/test/e2e/vscodeFixture.ts index 5509311..121ad62 100644 --- a/test/e2e/vscodeFixture.ts +++ b/test/e2e/vscodeFixture.ts @@ -29,6 +29,7 @@ export const test = base.extend<{ vscode: VsCodeSession }>({ const sandbox = await mkdtemp(join(tmpdir(), 'omv-e2e-')); const workspace = join(sandbox, 'workspace'); const userData = join(sandbox, 'user-data'); + const processOutput = { stdout: '', stderr: '' }; let closeApp: () => Promise = async () => undefined; try { await cp(join(root, '.e2e-workspace'), workspace, { @@ -103,6 +104,14 @@ export const test = base.extend<{ vscode: VsCodeSession }>({ env, timeout: 60_000 }); + for (const stream of ['stdout', 'stderr'] as const) { + app.process()[stream]?.on('data', (chunk: Buffer | string) => { + // Keep useful tail diagnostics without unbounded memory on a noisy failure. + processOutput[stream] = ( + processOutput[stream] + chunk.toString() + ).slice(-1_048_576); + }); + } closeApp = async () => { const timer = setTimeout(() => app.process().kill('SIGKILL'), 5_000); try { @@ -222,7 +231,32 @@ export const test = base.extend<{ vscode: VsCodeSession }>({ try { await closeApp(); } finally { - await rm(sandbox, { recursive: true, force: true }); + try { + if (testInfo.status !== testInfo.expectedStatus) { + await writeFile( + testInfo.outputPath('vscode-process.log'), + `STDOUT (last 1 MiB):\n${processOutput.stdout}\nSTDERR (last 1 MiB):\n${processOutput.stderr}` + ); + try { + // Includes extension-host and output-channel logs, flushed on close. + await cp( + join(userData, 'logs'), + testInfo.outputPath('vscode-logs'), + { + recursive: true + } + ); + } catch (error) { + // Preserve the original test failure and make missing diagnostics explicit. + await writeFile( + testInfo.outputPath('vscode-log-capture-error.txt'), + String(error) + ); + } + } + } finally { + await rm(sandbox, { recursive: true, force: true }); + } } } } @@ -230,7 +264,10 @@ export const test = base.extend<{ vscode: VsCodeSession }>({ export { expect }; -export async function waitForFile(path: string): Promise { +export async function waitForFile( + path: string, + timeout = 30_000 +): Promise { let bytes: Buffer | undefined; await expect .poll( @@ -242,7 +279,7 @@ export async function waitForFile(path: string): Promise { return 0; } }, - { timeout: 30_000, message: `Export should write ${path}` } + { timeout, message: `Export should write ${path}` } ) .toBeGreaterThan(0); return bytes!;