ohif-viewer/.agents/skills/ohif-test-agent/references/failure-triage.md

4.0 KiB

Failure triage

Before debugging, classify. Most OHIF test failures are timing or hydration — not real regressions.

Category Symptom Fix
Timing Element not visible, action timeout Add / increase the delay param of visitStudy; for actions that re-render viewports, use waitForViewportRenderCycle(page) (started before the action) instead of waitForTimeout; wrap the assertion in expect.toPass({ timeout })
Selector Element not found Verify data-cy on the target; confirm the panel is open (toggle() / select() before interacting); check for capital D in DOMOverlayPageObject when destructuring
Hydration Segmentation/RT not interactive Ensure the segmentationHydration.yes.click() fired; add waitForTimeout(3000) after loadSeriesByModality('SEG'|'RTSTRUCT'|'SR')
Data Study not found, empty viewport Confirm the UID is in the canonical list (see patterns-by-feature.md); confirm the mode supports the feature (segmentation tools aren't in viewer mode)
Visual drift Screenshot mismatch but feature works Have a human review the diff, then regenerate the baseline with yarn playwright test --update-snapshots. Do not adjust maxDiffPixelRatio or threshold to make a failing screenshot pass.
Real regression Feature is actually broken Report as a bug — this is the test doing its job

Prefer render-cycle waits over sleeps

If you're tempted to add await page.waitForTimeout(2000) after an action, ask whether the action re-rendered the viewport. If it did, use:

const cycle = waitForViewportRenderCycle(page);
await action();
await cycle;
await check();

The watcher must be created before the action — it waits for needsRender first, and that transition is gone by the time the action returns. See the "Wait for renders, don't sleep" section in SKILL.md and tests/SEGHydrationFromMPR.spec.ts.

When the cycle helper times out at waitForAnyViewportNeedsRender

Symptom: the test fails inside waitForAnyViewportNeedsRender after 5s, with the action having actually completed in the UI. The action just doesn't transition the viewport through needsRender synchronously. Known cases:

  • Hanging-protocol changes.
  • RTSTRUCT / contour segmentation hydration confirm. SEG (labelmap) hydration does fire needsRender; contour does not. The fix is to gate on the actual end-state — for hydration in beforeEach, await expect(page.getByTestId('data-row')).toHaveCount(N) is the right wait.

Don't react by raising the cycle's timeout — the transition isn't coming. Replace the cycle wrapper with an auto-retrying DOM/SVG assertion, or expect.toPass({ timeout }) around the assertion block.

The toPass pattern

When an assertion needs to wait for async render / propagation:

await expect(async () => {
  await rightPanelPageObject.labelMapSegmentationPanel.panel.segmentByText('Spleen').click();
  await expect(activeViewport.overlayText.bottomRight.instanceNumber).toContainText('17/');
}).toPass({ timeout: 10_000 });

toPass reruns the assertion block until it succeeds or the timeout expires — cleaner than a hand-rolled retry loop and surfaces the last failure reason if it times out.

Common DOMOverlayPageObject mistake

The fixture key is capital-D DOMOverlayPageObject, not lowercase. If your destructure is silently undefined, check the casing.

press import mistake

press is NOT re-exported from ./utils. import { press } from './utils' resolves to undefined and fails at runtime. Use:

import { press } from './utils/keyboardUtils';
await press({ page, key: 'ArrowDown', nTimes: 50 }); // object param, not (page, key)

Visual regression iteration

Screenshots live under tests/screenshots/chromium/<testFilePath>/. To accept new output as the baseline:

yarn playwright test tests/YourSpec.spec.ts --update-snapshots

Review the resulting PNGs carefully — an agent-accepted baseline that's subtly wrong is worse than a failing test.