Skip to content
This repository was archived by the owner on Jun 8, 2026. It is now read-only.

Commit da28e97

Browse files
Andrew Eyeclaude
andcommitted
FEA-1437: Phase 4 — Tools selector binding + dom_count assertion pattern
- audit-tile.ts: new dom_count branch — for list_length/section_card_count tiles (no single numeric element), assert the rendered item count == oracle via toHaveCount(selector). Generalizes the table-driven audit to count tiles. - Tools.tsx: data-testid='audit-tool-row' on each tool button. tools.list.length now asserts count == events_facets_tool_names_length (passes). - specs/audit/tools.ui-audit.spec.ts: new spec; skips tiles not yet bound (tools.event_types.length — event_type has no countable rendered list; only shows per-event in the detail panel) with a visible "(skip — selector pending)". - Plans.tsx: data-testid='audit-plan-row' added (infra for later) but the Plans UI audit is NOT enabled — the fixture seeds zero plans, so it can't be meaningfully asserted yet; manifest followup documents the fixture-seeding prerequisite. No Plans spec shipped (would be empty/false-confidence). - coverage.json: detection line numbers refreshed after the testid edits. Testing: test:audit:ui -> 9 passed / 5 skipped / 0 failed. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
1 parent 001dd4c commit da28e97

6 files changed

Lines changed: 81 additions & 12 deletions

File tree

apps/desktop/scripts/agent-monitor-packs/client/Tools.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,7 @@ export function Tools() {
125125
return (
126126
<button
127127
key={t.tool_name}
128+
data-testid="audit-tool-row"
128129
onClick={() => setSelected(t.tool_name)}
129130
className={`block w-full text-left rounded-lg border px-3 py-2 transition-colors ${
130131
active

apps/desktop/scripts/agent-monitor-plans/client/Plans.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,7 @@ export function Plans() {
166166
return (
167167
<button
168168
key={p.id}
169+
data-testid="audit-plan-row"
169170
onClick={() => setSelectedId(p.id)}
170171
className={`block w-full text-left rounded-lg border px-3 py-2 transition-colors ${
171172
active

apps/desktop/test-e2e/agent-monitor/helpers/audit-tile.ts

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,11 @@ export async function assertTileMatchesOracle(
5454
oracle: string;
5555
tile_kind?: string;
5656
trend_label?: string;
57-
selector?: { value?: string; present_in_code?: boolean };
57+
selector?: {
58+
value?: string;
59+
present_in_code?: boolean;
60+
render_kind?: string;
61+
};
5862
},
5963
): Promise<void> {
6064
const db = openDb(resolveFixtureDbPath());
@@ -68,6 +72,22 @@ export async function assertTileMatchesOracle(
6872
db.close();
6973
}
7074

75+
// dom_count tiles (list_length / section_card_count): there is no single
76+
// numeric element — the tile's value IS the number of rendered item nodes.
77+
// Assert the count of the item selector equals the oracle. selector.value is
78+
// the per-item selector for these tiles (e.g. [data-testid='audit-plan-row']).
79+
if (row.selector?.render_kind === "dom_count" && row.selector.present_in_code) {
80+
const expectedCount = Number(expectedRaw);
81+
await expect(
82+
page.locator(row.selector.value as string),
83+
`\n manifest id: ${row.id}\n` +
84+
` oracle: ${row.oracle} -> ${expectedRaw}\n` +
85+
` selector: ${row.selector.value} (counted)\n` +
86+
` note: dom_count tile — rendered item count must equal oracle.\n`,
87+
).toHaveCount(expectedCount);
88+
return;
89+
}
90+
7191
const region = await sliceForTile(page, row);
7292

7393
if (row.tile_kind === "trend") {

apps/desktop/test-e2e/agent-monitor/inventory/coverage.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1147,7 +1147,7 @@
11471147
"detection_id": "auto.tools.102",
11481148
"screen": "Tools",
11491149
"file": "apps/desktop/scripts/agent-monitor-packs/client/Tools.tsx",
1150-
"line": 155,
1150+
"line": 156,
11511151
"detected_kind": "formatter_call",
11521152
"value_expr": "fmt(ev.created_at)",
11531153
"status": "out_of_scope",
@@ -1157,7 +1157,7 @@
11571157
"detection_id": "auto.plans.103",
11581158
"screen": "Plans",
11591159
"file": "apps/desktop/scripts/agent-monitor-plans/client/Plans.tsx",
1160-
"line": 209,
1160+
"line": 210,
11611161
"detected_kind": "formatter_call",
11621162
"value_expr": "fmt(v.created_at)",
11631163
"status": "out_of_scope",

apps/desktop/test-e2e/agent-monitor/inventory/manifest.json

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -821,11 +821,11 @@
821821
"status": "pending",
822822
"bug_ref": null,
823823
"selector": {
824-
"value": "[data-testid='audit-tools-list-length']",
825-
"present_in_code": false,
824+
"value": "[data-testid='audit-tool-row']",
825+
"present_in_code": true,
826826
"render_kind": "dom_count",
827-
"renders_at": "scripts/agent-monitor-packs/client/Tools.tsx:123",
828-
"followup": "Either add a numeric total element with a data-testid, or test by counting rendered items under a data-testid'd .map(tools) container."
827+
"renders_at": "scripts/agent-monitor-packs/client/Tools.tsx (one button per distinct tool from /api/events/facets; testid added FEA-1437 Phase 4)",
828+
"followup": "Done — each tool row has data-testid='audit-tool-row'; the audit asserts toHaveCount == events_facets_tool_names_length."
829829
}
830830
},
831831
{
@@ -891,11 +891,11 @@
891891
"status": "pending",
892892
"bug_ref": null,
893893
"selector": {
894-
"value": "[data-testid='audit-plans-total']",
894+
"value": "[data-testid='audit-plan-row']",
895895
"present_in_code": false,
896896
"render_kind": "dom_count",
897-
"renders_at": "scripts/agent-monitor-plans/client/Plans.tsx:164",
898-
"followup": "Either add a numeric total element with a data-testid, or test by counting rendered items under a data-testid'd .map(plans) container."
897+
"renders_at": "scripts/agent-monitor-plans/client/Plans.tsx (one button per plan; data-testid='audit-plan-row' added FEA-1437 Phase 4)",
898+
"followup": "testid + dom_count assertion ready, but UI audit is BLOCKED on fixture data — seed-fixture-db.mjs seeds no plans, so /api/plans returns 0. Before enabling: seed N plan rows, confirm planStore.listPlans count == countPlans (no version-join drop), then flip present_in_code true and add specs/audit/plans.ui-audit.spec.ts (dom_count branch already supports it)."
899899
}
900900
},
901901
{
@@ -917,8 +917,8 @@
917917
"value": "[data-testid='audit-tools-event-types-length']",
918918
"present_in_code": false,
919919
"render_kind": "dom_count",
920-
"renders_at": "scripts/agent-monitor-packs/client/Tools.tsx:123",
921-
"followup": "Either add a numeric total element with a data-testid, or test by counting rendered items under a data-testid'd .map(event facets) container."
920+
"renders_at": "scripts/agent-monitor-packs/client/Tools.tsx — NOT rendered as a countable list. event_type only appears per-event in the right-hand detail panel; there is no distinct-event-types card list.",
921+
"followup": "BLOCKED: no rendered element represents the distinct-event-types count. Add a count element (data-testid) on the Tools screen, or descope this tile. The Tools audit spec skips it (present_in_code false)."
922922
}
923923
},
924924
{
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
// Layer-2 audit: Tools screen. `tools.list.length` is a dom_count tile — one
2+
// button per distinct tool from /api/events/facets, so the rendered row count
3+
// must equal the events_facets_tool_names_length oracle. `tools.event_types.length`
4+
// has no countable rendered element yet (event_type only shows per-event in the
5+
// detail panel) so it is skipped until bound. Shared logic in audit-tile.ts.
6+
7+
import { expect, test } from "@playwright/test";
8+
9+
import {
10+
loadManifest,
11+
tilesForScreen,
12+
} from "../../inventory/manifest-loader.mjs";
13+
// @ts-expect-error — .ts helper imported by Playwright's ts loader
14+
import { assertTileMatchesOracle } from "../../helpers/audit-tile";
15+
16+
const manifest = loadManifest();
17+
const toolTiles = tilesForScreen(manifest, "Tools");
18+
19+
test.describe("Tools tiles · UI audit (manifest-driven)", () => {
20+
test.beforeEach(async ({ page }) => {
21+
await page.goto("/tools");
22+
await expect
23+
.poll(
24+
async () => page.locator("[data-testid='audit-tool-row']").count(),
25+
{ timeout: 10_000 },
26+
)
27+
.toBeGreaterThan(0);
28+
});
29+
30+
for (const row of toolTiles) {
31+
// Skip tiles with a filed bug, or tiles not yet bound to a selector
32+
// (no countable rendered element). Visible skip > silent drop.
33+
const pending = !row.bug_ref && !row.selector?.present_in_code;
34+
const testFn = row.bug_ref || pending ? test.skip : test;
35+
const suffix = row.bug_ref
36+
? ` (skip — bug ${row.bug_ref})`
37+
: pending
38+
? " (skip — selector pending)"
39+
: "";
40+
testFn(
41+
`UI audit · ${row.id} matches oracle "${row.oracle}"${suffix}`,
42+
async ({ page }) => {
43+
await assertTileMatchesOracle(page, row);
44+
},
45+
);
46+
}
47+
});

0 commit comments

Comments
 (0)