Skip to content

Commit fc9ce3b

Browse files
committed
fix(webapp): repair the agent gallery's fixtures after the rebase, and pin them to the code that reads them
The confirmation card's external notification became a three-state object while the gallery branch sat still, and the report card's untrustworthy reason was renamed under a free-form key that nothing typechecked. Moves the hand-written fixtures into the shared gallery module and checks them against the schemas and readers the product uses, so the next rename fails a test instead of rendering an unreachable state.
1 parent 8bc8d00 commit fc9ce3b

7 files changed

Lines changed: 366 additions & 203 deletions

File tree

apps/webapp/app/routes/storybook.agent-report/route.tsx

Lines changed: 1 addition & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,50 +1,10 @@
1-
import type { ReportViewModelPayload } from "@internal/dashboard-agent-contracts";
21
import { DEMO_WORLD, demoFixtures, demoReportUri } from "~/components/dashboard-agent/demo";
32
import { ReportView } from "~/components/dashboard-agent/ReportView";
3+
import { untrustworthyReport } from "../storybook.agent-ui/fixtures";
44
import { fixtureResolveUri, GalleryPage, noop } from "../storybook.agent-ui/gallery";
55

66
const reportUri = demoReportUri(DEMO_WORLD.reportKey);
77

8-
const untrustworthyReport: ReportViewModelPayload = {
9-
...demoFixtures.demoDegradedReport,
10-
summary: {
11-
severity: "crit",
12-
statements: [
13-
{ findingType: "flow", severity: "crit", reason: "unknown" },
14-
{ findingType: "execution", severity: "crit", reason: "unknown" },
15-
{ findingType: "liveness", severity: "crit" },
16-
],
17-
},
18-
findings: demoFixtures.demoDegradedReport.findings.map((finding) =>
19-
finding.type === "liveness"
20-
? {
21-
...finding,
22-
severity: "crit",
23-
reason: "stale",
24-
recommendation: { code: "check_control_plane", link: "status" },
25-
}
26-
: {
27-
...finding,
28-
severity: "crit",
29-
reason: "unknown",
30-
recommendation: undefined,
31-
attribution: undefined,
32-
exclusions: undefined,
33-
observations: undefined,
34-
hedge: undefined,
35-
anomalyWindow: undefined,
36-
}
37-
),
38-
metrics: demoFixtures.demoDegradedReport.metrics.map((metric) =>
39-
metric.id === "liveness"
40-
? { ...metric, value: 21 * 60_000, severity: "crit" }
41-
: { ...metric, annotation: undefined }
42-
),
43-
facts: { trustworthy: false, staleReason: "telemetry_stale" },
44-
links: [{ key: "status", label: "status.trigger.dev", url: "https://status.trigger.dev" }],
45-
footer: [{ code: "check_control_plane", link: "status" }],
46-
};
47-
488
const STATES: Record<string, React.ReactNode> = {
499
"report-view-healthy": (
5010
<ReportView
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
import {
2+
safeParseStoredViewBlock,
3+
viewBlockSchema,
4+
watchExternalNotificationLine,
5+
} from "@internal/dashboard-agent-contracts";
6+
import { ErrorId } from "@trigger.dev/core/v3/isomorphic";
7+
import { describe, expect, it } from "vitest";
8+
import { planDiagnosisActions } from "~/components/dashboard-agent/diagnosis-actions";
9+
import { renderableActions } from "~/components/dashboard-agent/view-actions";
10+
import { reportTrust } from "~/presenters/v3/reports/report-layout";
11+
import {
12+
externalServiceDiagnosis,
13+
fullDiagnosis,
14+
lowConfidenceDiagnosis,
15+
offerActionsBlock,
16+
revisedDiagnosisBlocks,
17+
untrustworthyReport,
18+
watchConfirmationBlock,
19+
watchDegradedConfirmationBlock,
20+
watchSatisfiedBlock,
21+
} from "./fixtures";
22+
23+
/**
24+
* The gallery's hand-written fixtures, checked against the code that reads them rather
25+
* than against themselves. A fixture that still typechecks but no longer matches what
26+
* the product emits would otherwise render a state nobody can reach.
27+
*/
28+
29+
describe("gallery view blocks", () => {
30+
it("parses every enveloped block through the schema the product persists with", () => {
31+
for (const block of [
32+
...revisedDiagnosisBlocks,
33+
offerActionsBlock,
34+
watchConfirmationBlock,
35+
watchDegradedConfirmationBlock,
36+
watchSatisfiedBlock,
37+
]) {
38+
const result = viewBlockSchema.safeParse(block);
39+
expect(result.success, JSON.stringify(result.error?.issues)).toBe(true);
40+
}
41+
});
42+
43+
it("parses the envelope-less diagnoses through the stored-block schema", () => {
44+
for (const block of [fullDiagnosis, externalServiceDiagnosis, lowConfidenceDiagnosis]) {
45+
const result = safeParseStoredViewBlock(block);
46+
expect(result.success, JSON.stringify(result.error?.issues)).toBe(true);
47+
}
48+
});
49+
50+
/** Every stored spec is normalized, so an unprefixed fingerprint is a shape no card sees. */
51+
it("cites error fingerprints in the normalized form the watch service stores", () => {
52+
for (const action of offerActionsBlock.actions) {
53+
if (action.intent.kind !== "watch" || action.intent.spec.kind !== "error_recurrence")
54+
continue;
55+
const { fingerprint } = action.intent.spec;
56+
expect(fingerprint).toBe(ErrorId.toId(fingerprint));
57+
}
58+
});
59+
60+
it("offers only actions the panel would render", () => {
61+
expect(renderableActions(offerActionsBlock.actions)).toHaveLength(
62+
offerActionsBlock.actions.length
63+
);
64+
});
65+
66+
/**
67+
* The gallery renders outside a project route, so the card resolves no run path and drops
68+
* the button. With a resolver both survive: the fixture's actions are still ones the card
69+
* can plan, not ones it silently discards.
70+
*/
71+
it("plans every diagnosis action once its destination resolves", () => {
72+
const planned = planDiagnosisActions(fullDiagnosis.actions ?? [], {
73+
runPath: (runId) => `/runs/${runId}`,
74+
docsUrl: (target) => target,
75+
});
76+
expect(planned.map((action) => action.kind)).toEqual(["view_run", "docs"]);
77+
});
78+
});
79+
80+
describe("gallery watch confirmations", () => {
81+
it("states the external outcome each confirmation claims", () => {
82+
expect(watchConfirmationBlock.followUp).toContain(
83+
watchExternalNotificationLine({ status: "enabled" })
84+
);
85+
expect(watchDegradedConfirmationBlock.followUp).toContain(
86+
watchExternalNotificationLine({
87+
status: "unavailable",
88+
reason: "email_alerts_not_configured",
89+
})
90+
);
91+
});
92+
93+
it("says the first check could not run on the degraded one, and not on the other", () => {
94+
expect(watchDegradedConfirmationBlock.detail).toBeTruthy();
95+
expect(watchConfirmationBlock.detail).toBeNull();
96+
});
97+
});
98+
99+
describe("gallery report", () => {
100+
it("names an untrustworthy reason the card has a caveat for", () => {
101+
const trust = reportTrust(untrustworthyReport);
102+
expect(trust?.badge).toBe("stale data");
103+
});
104+
});

0 commit comments

Comments
 (0)