Skip to content

Add e2e rendered image verification tests#221

Merged
elazarcoh merged 21 commits intomainfrom
feat/e2e-image-verification
Mar 9, 2026
Merged

Add e2e rendered image verification tests#221
elazarcoh merged 21 commits intomainfrom
feat/e2e-image-verification

Conversation

@elazarcoh
Copy link
Copy Markdown
Owner

Add image-rendering verification to the e2e suite.\n\nThis PR: \n- Adds tests/ui-test/image-verification-utils.ts (canvas screenshot + pixel assertions).\n- Adds tests/ui-test/image-rendering.test.ts (rgb, grayscale, heatmap, bgr swap, channel filter tests).\n- Adds captureCanvasImage() to DebugTestHelper.\n- Adds pixel assertions to display-options.test.ts to ensure UI actions change visuals.\n\nUses WebDriver element screenshots and Jimp for decoding; assertions are region-based (dominant channel, luminance) to be robust across CI/GPU differences.\n\nCo-authored-by: Copilot 223556219+Copilot@users.noreply.github.com

@elazarcoh elazarcoh enabled auto-merge March 8, 2026 22:29
@elazarcoh elazarcoh disabled auto-merge March 8, 2026 22:31
@github-actions
Copy link
Copy Markdown

github-actions Bot commented Mar 9, 2026

✅ CI Results

🧪 Tests

Suite ✅ Passed ❌ Failed ⏭️ Skipped 📊 Total
E2E (UI) 16 0 2 18
Python Unit 98 0 0 98
TS Unit 56 0 0 56

📦 Artifacts

screenshots-ubuntu-latest  ·  test-results  ·  extension-vsix  ·  ts-unit-test-results  ·  python-unit-test-results

→ Full run details

elazarcoh and others added 10 commits March 9, 2026 15:38
…image-verification-utils.ts (canvas screenshot + pixel assertions)\n- New: tests/ui-test/image-rendering.test.ts (rendering verification tests)\n- Modified: tests/ui-test/DebugTestHelper.ts (captureCanvasImage)\n- Modified: tests/ui-test/display-options.test.ts (pixel assertions for display options)\n\nCo-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…pe declaration errors

The jimp package has upstream type declaration errors (missing pngjs types
and @jimp/plugin-print/load-font). These cause yarn test:compile to exit with
code 2, blocking CI. skipLibCheck suppresses these third-party .d.ts issues.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…nel assertion

Two bugs causing all new tests to fail:

1. image-rendering.test.ts: viewVariable() did not call getWebviewEditor() after
   clicking 'View Image', so the Image View panel was not focused. element.takeScreenshot()
   on #gl-canvas captured the VS Code background (dark gray ~44/255) instead of actual
   rendered pixels. Fix: call getWebviewEditor() + extra 1.5s wait in viewVariable().

2. display-options.test.ts: assertChannelSwapped for bgr_test right half had reversed
   channel args. bgr_test right half is stored as BGR-red ([0,0,255]) which the extension
   displays as blue (b>r) before the Swap RGB/BGR filter and red (r>b) after.
   Fix: change ('r','b') to ('b','r').

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Dispatch window resize event inside webview frame before taking element
  screenshot, forcing WebGL to re-render the canvas. Without this, the
  backbuffer (preserveDrawingBuffer=false) may be cleared after the initial
  render, yielding a blank/dark capture.
- Add 400ms sleep after resize dispatch to allow the RAF to complete.
- Log center pixel sample in captureCanvasImage to aid CI diagnosis.
- Retry clickDisplayOption up to 5 times to handle webview render delays.
- Increase viewVariable post-getWebviewEditor wait from 500ms to 1500ms.
- Add wait(1000) before getWebviewEditor() to match the working pattern
  in display-options.test.ts (viewVariableAndScreenshot).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…able

requestAnimationFrame is throttled in a freshly-opened webview with no
prior user interaction. Clicking any display-option button causes Yew to
schedule a full-speed RAF cycle. Using 'Reset' (no visual side-effect at
default settings) before captureCanvasImage ensures the canvas has rendered
real pixels rather than the cleared backbuffer.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The VS Code webview outer frame (.webview class) contains a nested
vscode-webview:// iframe with the actual extension content. On fresh
sessions, this nested iframe may not yet exist when switchToWebviewFrame
is first called, causing ChromeDriver to find elements via cross-frame
traversal while clicks are intercepted by the nested iframe.

Fix: after switching to the outer webview frame, poll for the nested
content iframe up to 8 seconds before proceeding. Applied to both
image-verification-utils.ts and display-options.test.ts.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
VS Code webviews use a 3-level iframe structure:
  Level 1: main window → <iframe class="webview [ready]"> (outer container)
  Level 2: <iframe src="vscode-webview://..."> (VS Code content manager)
  Level 3: <iframe id="active-frame"> (actual user HTML)

Previous code only navigated 2 levels deep. Clicking buttons found from
level 2 would fail with ElementClickInterceptedError because the #active-frame
iframe was intercepting. This is also why the canvas was dark — the warmup
Reset click was never actually executed.

Fix: after switching to level-2, wait up to 15s for #active-frame and switch
into it. This matches the pattern used by vscode-extension-tester's own
WebviewMixin.switchToFrame() (which uses activeFrame = By.id('active-frame')).

Applied to both image-verification-utils.ts and display-options.test.ts.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Remove the 15-second #active-frame wait that caused ElementClickInterceptedError
in display-options (the wait caused #active-frame to load and overlay buttons).

Both display-options and image-verification-utils now:
- Log all iframe attributes at each level for diagnostics
- Switch outer→level-2 immediately without long waits
- Check for #active-frame immediately (no polling) and switch if present
- Fall back to level-2 if no #active-frame found yet

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The Reset/channel buttons in the webview are covered by a Monaco panel
sash (resize handle) in CI, causing WebDriver's coordinate-based click
to fail with ElementClickInterceptedError. Using JavaScript .click()
dispatches the event directly on the element, bypassing hit-detection.

Also adds scrollIntoView before clicking for additional robustness.

CI diagnostics confirmed:
- Iframe structure: main -> .webview.ready -> #active-frame (user HTML)
- Canvas capture works: r=217 correctly shown for Red Channel
- Only button clicks were failing due to the sash overlay

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
All sampleRegion y-ranges updated to y=0.22–0.52 (h=0.30) to stay
within the rendered image extent (empirically y≈0.13–0.56).  The
previous y=0.40–0.65 extended ~10% past the image bottom, mixing in
dark background pixels and reducing the measured luminance enough to
fail the brightness-difference assertions.

x-coordinates also tightened:
- Red band:   x=0.52–0.62  (clear of RED/GREEN transition at x≈0.68)
- Green band: x=0.75–0.85  (solidly GREEN, away from transition)
- Blue band:  x=0.93–0.98  (the only visible slice of the blue column)
- BGR-swap right region and red-channel-filter middle region updated
  to match the same calibrated x values

Remove horizontal color-profile debug logging from captureCanvasImage
now that all 7 tests pass locally.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@elazarcoh elazarcoh force-pushed the feat/e2e-image-verification branch from 43f6ac2 to 1d40b96 Compare March 9, 2026 13:38
elazarcoh and others added 11 commits March 9, 2026 21:33
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Export switchToWebviewFrame from image-verification-utils.
Remove local switchToWebviewFrame, switchToMainContent, clickDisplayOptionButton,
testDisplayOption, and viewVariableAndScreenshot from display-options.test.ts.
Replace with imported switchToWebviewFrame and clickDisplayOption from utils.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Exclude docs/ from ESLint to avoid lint noise from TypeScript code
  blocks in Markdown plan files
- Rename viewVariable parameter name -> name to match image-rendering.test.ts
- Add explanatory comment on getWebviewEditor() call

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
New DebugAnnotator class in image-verification-utils.ts:
- record(relX, relY, relW, relH, color, assertFn, label): executes
  assertion, records pass/fail, re-throws on failure
- addRegion(...): records region without assertion (info-only)
- saveHtml(): writes <testName>.html with canvas screenshot +
  SVG rectangle overlays (green=pass, red=fail, grey=info)
  Only runs when SVIFPD_DEBUG_IMAGES=1 is set.

New captureAnnotatedCanvas(driver, testName) helper returns
{ img, annotator } pair for use in tests.

Integrated into image-rendering.test.ts (7 tests) and
display-options.test.ts (2 assertion blocks).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
… scope

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…ports

- display-options.test.ts: nest try/finally so annotatorBefore.saveHtml()
  always runs when capturedBefore succeeds (matches image-rendering pattern)
- image-verification-utils.ts: move SVG text labels above box when they
  would overflow the viewBox bottom edge
- image-verification-utils.ts: skip HTML write when no annotations recorded

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Switch captureCanvasImage from #gl-canvas (full viewport including
webview sidebar) to .view-container (main rendering cell only).
Apply img.autocrop() to trim VS Code background from all 4 edges.

Coordinates in image-rendering.test.ts and display-options.test.ts
updated to image-space fractions based on actual measured band
positions (left≈0.0–0.43, mid≈0.43–0.86, right≈0.86–1.0 for
rgb_gradient; bgr_test right half at x≈0.64–0.86) — no more 0.47
sidebar x-offset needed.

waitForCanvasToRender sampling updated to cover the full image width
instead of the old x>0.50 render-area heuristic.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Gate 'used .view-container' debug log on screenshot success
- Log warn on screenshot failure (was silently swallowed)
- Remove #gl-canvas fallback: all test coordinates are calibrated for
  .view-container geometry; fallback would silently produce wrong geometry
- Update stale JSDoc on captureCanvasImage and waitForCanvasToRender

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- sampleRegion: clamp x0/y0 to >= 0 to prevent negative-index reads
  into previous bitmap rows via getPixelColor
- image-rendering.test.ts: treat clickDisplayOption('Reset') failure as
  test error (was silently ignored, leaving stale state for next test)
- display-options.test.ts: throw when capturedAfter is null instead of
  silently skipping pixel assertions; same Reset hardening applied
- DebugTestHelper.ts: update stale JSDoc (was #gl-canvas, now .view-container)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@elazarcoh elazarcoh enabled auto-merge March 9, 2026 21:19
@elazarcoh elazarcoh merged commit 60ce6de into main Mar 9, 2026
10 checks passed
@elazarcoh elazarcoh deleted the feat/e2e-image-verification branch March 9, 2026 21:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant