import type { Meta, StoryObj } from "@storybook/vue3-vite"; import { expect, within } from "storybook/test"; import { createRouter, createMemoryHistory } from "vue-router"; import ErrorCard from "./ErrorCard.vue"; // A throwaway router so the correlation chip's RouterLink resolves in the // iframe instead of rendering as an unresolved element. const router = createRouter({ history: createMemoryHistory(), routes: [{ path: "/trace", component: { template: "
" } }], }); const meta: Meta = { title: "Errors/ErrorCard", component: ErrorCard, tags: ["autodocs"], parameters: { vueRouter: { router } }, }; export default meta; type Story = StoryObj; const env = (corr: string) => ({ messageId: "m-1", causationId: "m-1", correlationId: corr }); export const DeadLettered: Story = { args: { record: { kind: "dlq.recorded", action: "orders.ship", attempts: 5, ts: "2026-01-01T12:30:00.000Z", error: { name: "TypeError", message: "Cannot read properties of undefined (reading 'sku')" }, envelope: env("c-9f8e21"), }, }, play: async ({ canvasElement }) => { const c = within(canvasElement); await expect(c.getByTestId("error-title")).toHaveTextContent("Dead-lettered"); await expect(c.getByTestId("error-card")).toHaveAttribute("data-severity", "critical"); await expect(c.getByTestId("error-summary")).toHaveTextContent("orders.ship"); }, }; export const Declined: Story = { args: { record: { kind: "action.failed", action: "billing.charge", attempt: 1, maxAttempts: 1, willRetry: false, ts: "2026-01-01T12:31:00.000Z", error: { name: "PaymentDeclined", message: "card declined", code: 402 }, envelope: env("c-aa11bb"), }, }, play: async ({ canvasElement }) => { const c = within(canvasElement); await expect(c.getByTestId("error-card")).toHaveAttribute("data-category", "declined"); await expect(c.getByTestId("error-severity")).toHaveTextContent("Warning"); }, }; export const Retrying: Story = { args: { record: { kind: "action.failed", action: "inventory.reserve", attempt: 2, maxAttempts: 3, willRetry: true, ts: "2026-01-01T12:32:00.000Z", error: { name: "TimeoutError", message: "upstream timed out" }, envelope: env("c-cc22dd"), }, }, play: async ({ canvasElement }) => { await expect(within(canvasElement).getByTestId("error-retry")).toHaveTextContent( "attempt 2 of 3", ); }, }; export const ExternalCall: Story = { args: { record: { kind: "external.call.failed", call: "stripe.charge", target: "https://api.stripe.com/v1/charges", attempt: 1, willRetry: false, ts: "2026-01-01T12:33:00.000Z", error: { name: "FetchError", message: "ECONNREFUSED" }, }, }, }; export const Gallery: Story = { render: () => ({ components: { ErrorCard }, setup: () => ({ records: [ { kind: "dlq.recorded", action: "orders.ship", attempts: 5, error: { name: "TypeError", message: "boom" }, }, { kind: "action.failed", action: "billing.charge", willRetry: false, error: { name: "PaymentDeclined", message: "declined", code: 402 }, }, { kind: "action.failed", action: "inventory.reserve", attempt: 2, maxAttempts: 3, willRetry: true, error: { name: "TimeoutError", message: "timed out" }, }, { kind: "projection.failed", projection: "orders.summary", event: "OrderPlaced", error: { name: "RangeError", message: "bad index" }, }, ], }), template: `
`, }), };