From 66c25efe18775f91640e66d1b2c6868bc70f0b55 Mon Sep 17 00:00:00 2001 From: Amruth Pillai Date: Sat, 5 Sep 2026 20:30:37 -0700 Subject: [PATCH] test(e2e): scope imported table border geometry (#3476) --- .../imported-table-raster-ci-fix.md | 39 +++++++++++++++++++ tests/e2e/fixtures/pdf-borders.test.ts | 20 ++++++++++ tests/e2e/fixtures/pdf-borders.ts | 35 +++++++++++++++++ tests/e2e/specs/imported-table.spec.ts | 15 +++---- 4 files changed, 102 insertions(+), 7 deletions(-) create mode 100644 .orchestration/imported-table-raster-ci-fix.md create mode 100644 tests/e2e/fixtures/pdf-borders.test.ts create mode 100644 tests/e2e/fixtures/pdf-borders.ts diff --git a/.orchestration/imported-table-raster-ci-fix.md b/.orchestration/imported-table-raster-ci-fix.md new file mode 100644 index 000000000..e7bf3a4f1 --- /dev/null +++ b/.orchestration/imported-table-raster-ci-fix.md @@ -0,0 +1,39 @@ +# Imported-table raster CI fix + +## Root cause + +Hosted runs `34007560930` (PR #3471) and `34007788443` (PR #3472) failed only in +`tests/e2e/specs/imported-table.spec.ts` with `horizontal: 18` instead of Plan 16's +`horizontal: 17`; text and vertical checks passed. + +The PDF operator dump from the failed hosted artifact showed 29 table path records matching the Plan 16 contract +(17 horizontal, 12 vertical), followed by an unrelated `constructPath` `endPath` bbox: +`[0, 19.65, 358.93, 20.65]`. Its stroke color was reported as `#cc00cc` only because the helper retained the +last table stroke color. It was a later red section-divider fill/no-paint path, not an extra table border. The old +helper classified every thin bbox after the last matching color state, so it counted this false positive. + +The table's explicit width is stable at 300pt, while row height legitimately changes from 30pt to 31pt after the +`Beta!` edit. The helper therefore scopes candidate paths by the fixture's 300pt horizontal grid envelope, not by a +row-height tolerance. Missing or duplicated paths inside that envelope still change the exact 17/12 contract. + +## Change + +- Added `tests/e2e/fixtures/pdf-borders.ts` with deterministic `countTableBorderGeometry` filtering. +- Updated browser/server PDF inspection in `tests/e2e/specs/imported-table.spec.ts` to use the helper. +- Added `tests/e2e/fixtures/pdf-borders.test.ts`; regression proves old stale-color counting returns 2 horizontal + paths while topology-scoped counting returns 1. + +## Verification + +- Intent skill inventory: 7 packages, 26 skills; no matching local skill for this E2E/PDF helper. +- Focused helper regression: 1 file, 1 passed. +- Dedicated imported-table E2E: 2 consecutive runs, each 1 passed; both exercise initial, unrelated-edit, and table-edit + stages plus browser and server PDF exports. +- Production build: 3/3 tasks successful. +- Web typecheck via `rtk proxy pnpm --filter web typecheck`: passed (`tsgo --noEmit`). +- Turbo boundaries: 1,443 files across 20 packages, no issues. +- Targeted Biome: 3 files, no issues. +- `git diff --check`: passed. + +The root `pnpm typecheck` wrapper was also tried but invokes an incompatible `tsc` path and reports TS5096 for +`allowImportingTsExtensions`; the package's documented `tsgo --noEmit` typecheck passes. diff --git a/tests/e2e/fixtures/pdf-borders.test.ts b/tests/e2e/fixtures/pdf-borders.test.ts new file mode 100644 index 000000000..509a3c769 --- /dev/null +++ b/tests/e2e/fixtures/pdf-borders.test.ts @@ -0,0 +1,20 @@ +import type { BorderPath } from "./pdf-borders"; +import { describe, expect, it } from "vitest"; +import { countTableBorderGeometry } from "./pdf-borders"; + +describe("table border geometry", () => { + it("does not count a later path after stale magenta stroke state", () => { + const paths: BorderPath[] = [ + { color: "#cc00cc", bounds: [0, 0, 100, 1] }, + { color: "#cc00cc", bounds: [0, 19.65, 358.93, 20.65] }, + ]; + const oldHorizontal = paths.filter(({ color, bounds: [x0, y0, x1, y1] }) => { + const width = Math.abs((x1 ?? 0) - (x0 ?? 0)); + const height = Math.abs((y1 ?? 0) - (y0 ?? 0)); + return color === "#cc00cc" && height > 0 && height <= 1.01 && width > height; + }).length; + + expect(oldHorizontal).toBe(2); + expect(countTableBorderGeometry(paths)).toEqual({ horizontal: 1, vertical: 0 }); + }); +}); diff --git a/tests/e2e/fixtures/pdf-borders.ts b/tests/e2e/fixtures/pdf-borders.ts new file mode 100644 index 000000000..1276fc7c5 --- /dev/null +++ b/tests/e2e/fixtures/pdf-borders.ts @@ -0,0 +1,35 @@ +export type BorderPath = { + color: string; + bounds: readonly number[]; +}; + +export type BorderGeometry = { + horizontal: number; + vertical: number; +}; + +// The imported-table fixture is explicitly 300pt wide. Row height can change when a cell is edited, +// so use width as stable topology while excluding unrelated paths that inherit the table's stroke color. +const TABLE_GRID_MAX_X = 300; + +const isInsideTableWidth = (bounds: readonly number[]) => { + const [x0, y0, x1, y1] = bounds; + if (![x0, y0, x1, y1].every((value) => Number.isFinite(value))) return false; + return Math.min(x0 ?? 0, x1 ?? 0) >= 0 && Math.max(x0 ?? 0, x1 ?? 0) <= TABLE_GRID_MAX_X; +}; + +export function countTableBorderGeometry(paths: readonly BorderPath[]): BorderGeometry { + let horizontal = 0; + let vertical = 0; + + for (const path of paths) { + if (path.color !== "#cc00cc" || !isInsideTableWidth(path.bounds)) continue; + const [x0, y0, x1, y1] = path.bounds; + const width = Math.abs((x1 ?? 0) - (x0 ?? 0)); + const height = Math.abs((y1 ?? 0) - (y0 ?? 0)); + if (height > 0 && height <= 1.01 && width > height) horizontal++; + if (width > 0 && width <= 1.01 && height > width) vertical++; + } + + return { horizontal, vertical }; +} diff --git a/tests/e2e/specs/imported-table.spec.ts b/tests/e2e/specs/imported-table.spec.ts index 65aa55c22..7721cec03 100644 --- a/tests/e2e/specs/imported-table.spec.ts +++ b/tests/e2e/specs/imported-table.spec.ts @@ -3,6 +3,7 @@ import { readFile, writeFile } from "node:fs/promises"; import { createRequire } from "node:module"; import { Pool } from "pg"; import { defaultResumeData } from "@reactive-resume/schema/resume/default"; +import { countTableBorderGeometry } from "../fixtures/pdf-borders"; import { ACTIVE_PREVIEW_PAGE_SELECTOR } from "../fixtures/preview"; import { openSidebarSection } from "../fixtures/resume"; import { expect, test } from "../fixtures/test"; @@ -59,17 +60,17 @@ async function inspectPdf(bytes: Uint8Array) { const text = (await page.getTextContent()).items.flatMap((item: { str?: string }) => item.str ?? []); const operators = await page.getOperatorList(); let stroke = ""; - let horizontal = 0; - let vertical = 0; + const borderPaths = []; for (const [index, fn] of operators.fnArray.entries()) { if (fn === OPS.setStrokeRGBColor) stroke = operators.argsArray[index][0]; - if (fn !== OPS.constructPath || stroke !== "#cc00cc") continue; + if (fn !== OPS.constructPath) continue; const bounds = operators.argsArray[index][2] as ArrayLike; - const width = Math.abs((bounds[2] ?? 0) - (bounds[0] ?? 0)); - const height = Math.abs((bounds[3] ?? 0) - (bounds[1] ?? 0)); - if (height > 0 && height <= 1.01 && width > height) horizontal++; - if (width > 0 && width <= 1.01 && height > width) vertical++; + borderPaths.push({ + color: stroke, + bounds: [bounds[0] ?? 0, bounds[1] ?? 0, bounds[2] ?? 0, bounds[3] ?? 0], + }); } + const { horizontal, vertical } = countTableBorderGeometry(borderPaths); return { text, horizontal, vertical }; } finally { await loading.destroy();