Skip to content

Annotate on failure with the DSL

annotateScreenshot() accepts the annotation DSL directly since annot-playwright@0.2.0. The DSL is typically clearer than hand-rolled SVG when the goal is “highlight the failing element + label it”:

import {
test,
expect,
type BboxAnnotation,
} from "@ingcreators/annot-playwright";
test("submit button is reachable", async ({ page, annotator }, testInfo) => {
await page.goto("https://example.com/login");
const submit = page.getByRole("button", { name: "Sign in" });
try {
await expect(submit).toBeEnabled();
} catch (err) {
const box = await submit.boundingBox();
if (box) {
const annotated = await annotator.annotateScreenshot(page, {
annotations: [
{ type: "rect", bbox: box, intent: "error" },
{
type: "callout",
at: { x: 30, y: 30 },
targetBbox: box,
content: "Submit button disabled",
},
] satisfies BboxAnnotation[],
});
await testInfo.attach("failure.png", {
body: annotated,
contentType: "image/png",
});
}
throw err;
}
});

intent: "error" pulls colours from the Annot design system (stroke #ef4444, fill 12% alpha, callout text #991b1b) — no manual hex picking, the failure looks identical to redacts / error chrome elsewhere in the product.

The annotationsSvg: string flavour from the SVG helpers page still works and is appropriate when:

  • You need a shape the DSL doesn’t model (use { type: "raw" } or stick with the helpers).
  • You’re composing many small fragments programmatically (string concatenation is fine; the DSL doesn’t add value).

The DSL flavour is appropriate when:

  • You want intent → colour shorthand.
  • You want callouts (rect + arrow + text in one entry).
  • You’re generating annotations from data + want a typed shape that survives JSON.parse round-trips.

The fixture’s annotateScreenshot() accepts either — they’re not mutually exclusive at the API level, just pick whichever maps cleanly to the call site.

The DSL doesn’t resolve locators itself — Playwright tests already hold a Page and can resolve via await locator.boundingBox(). If you find yourself writing the same locator → bbox dance many times, the draw-by-DOM-locator recipe lifts it into a helper.

For agent-side workflows where the agent has the locator string but no Page, the MCP annot_annotate_url tool takes locators directly.