| |
|
|
| const { getCommentMock } = vi.hoisted(() => ({ |
| getCommentMock: vi.fn(), |
| })); |
|
|
| vi.mock("../api/issues", () => ({ |
| issuesApi: { |
| getComment: getCommentMock, |
| }, |
| })); |
|
|
| import { describe, expect, it, vi } from "vitest"; |
| import { __liveUpdatesTestUtils } from "./LiveUpdatesProvider"; |
| import { queryKeys } from "../lib/queryKeys"; |
|
|
| function buildFakeT(translations: Record<string, string>) { |
| return ((key: string, options?: Record<string, unknown>) => { |
| const template = translations[key] ?? (typeof options?.defaultValue === "string" ? options.defaultValue : key); |
| return template.replace(/{{(\w+)}}/g, (_, name: string) => String(options?.[name] ?? "")); |
| }) as never; |
| } |
|
|
| describe("LiveUpdatesProvider issue invalidation", () => { |
| it("refreshes touched inbox queries and only the changed issue data for issue updates", () => { |
| const invalidations: unknown[] = []; |
| const queryClient = { |
| invalidateQueries: (input: unknown) => { |
| invalidations.push(input); |
| }, |
| getQueryData: () => undefined, |
| }; |
|
|
| __liveUpdatesTestUtils.invalidateActivityQueries( |
| queryClient as never, |
| "company-1", |
| { |
| entityType: "issue", |
| entityId: "issue-1", |
| action: "issue.updated", |
| details: null, |
| }, |
| { userId: null, agentId: null }, |
| ); |
|
|
| expect(invalidations).toContainEqual({ |
| queryKey: queryKeys.issues.listMineByMe("company-1"), |
| }); |
| expect(invalidations).toContainEqual({ |
| queryKey: queryKeys.issues.listTouchedByMe("company-1"), |
| }); |
| expect(invalidations).toContainEqual({ |
| queryKey: queryKeys.issues.listUnreadTouchedByMe("company-1"), |
| }); |
| expect(invalidations).toContainEqual({ |
| queryKey: queryKeys.issues.detail("issue-1"), |
| }); |
| expect(invalidations).toContainEqual({ |
| queryKey: queryKeys.issues.activity("issue-1"), |
| }); |
| expect(invalidations).not.toContainEqual({ |
| queryKey: queryKeys.issues.comments("issue-1"), |
| }); |
| expect(invalidations).not.toContainEqual({ |
| queryKey: queryKeys.issues.runs("issue-1"), |
| }); |
| expect(invalidations).not.toContainEqual({ |
| queryKey: queryKeys.issues.documents("issue-1"), |
| }); |
| expect(invalidations).not.toContainEqual({ |
| queryKey: queryKeys.issues.attachments("issue-1"), |
| }); |
| expect(invalidations).not.toContainEqual({ |
| queryKey: queryKeys.issues.approvals("issue-1"), |
| }); |
| expect(invalidations).not.toContainEqual({ |
| queryKey: queryKeys.issues.liveRuns("issue-1"), |
| }); |
| expect(invalidations).not.toContainEqual({ |
| queryKey: queryKeys.issues.activeRun("issue-1"), |
| }); |
| }); |
|
|
| it("still refreshes comments when a comment activity event arrives", () => { |
| const invalidations: unknown[] = []; |
| const queryClient = { |
| invalidateQueries: (input: unknown) => { |
| invalidations.push(input); |
| }, |
| getQueryData: () => undefined, |
| }; |
|
|
| __liveUpdatesTestUtils.invalidateActivityQueries( |
| queryClient as never, |
| "company-1", |
| { |
| entityType: "issue", |
| entityId: "issue-1", |
| action: "issue.comment_added", |
| details: null, |
| }, |
| { userId: null, agentId: null }, |
| ); |
|
|
| expect(invalidations).toContainEqual({ |
| queryKey: queryKeys.issues.comments("issue-1"), |
| }); |
| }); |
|
|
| it("keeps self-authored comment events from refetching the active issue tree", () => { |
| const invalidations: unknown[] = []; |
| const queryClient = { |
| invalidateQueries: (input: unknown) => { |
| invalidations.push(input); |
| }, |
| getQueryData: () => undefined, |
| }; |
|
|
| __liveUpdatesTestUtils.invalidateActivityQueries( |
| queryClient as never, |
| "company-1", |
| { |
| entityType: "issue", |
| entityId: "issue-1", |
| action: "issue.comment_added", |
| actorType: "user", |
| actorId: "user-1", |
| details: null, |
| }, |
| { userId: "user-1", agentId: null }, |
| ); |
|
|
| expect(invalidations).toContainEqual({ |
| queryKey: queryKeys.issues.detail("issue-1"), |
| refetchType: "inactive", |
| }); |
| expect(invalidations).toContainEqual({ |
| queryKey: queryKeys.issues.activity("issue-1"), |
| refetchType: "inactive", |
| }); |
| expect(invalidations).toContainEqual({ |
| queryKey: queryKeys.issues.comments("issue-1"), |
| refetchType: "inactive", |
| }); |
| }); |
|
|
| it("treats self-authored comment-driven issue updates as inactive-only refreshes", () => { |
| const invalidations: unknown[] = []; |
| const queryClient = { |
| invalidateQueries: (input: unknown) => { |
| invalidations.push(input); |
| }, |
| getQueryData: () => undefined, |
| }; |
|
|
| __liveUpdatesTestUtils.invalidateActivityQueries( |
| queryClient as never, |
| "company-1", |
| { |
| entityType: "issue", |
| entityId: "issue-1", |
| action: "issue.updated", |
| actorType: "user", |
| actorId: "user-1", |
| details: { source: "comment" }, |
| }, |
| { userId: "user-1", agentId: null }, |
| ); |
|
|
| expect(invalidations).toContainEqual({ |
| queryKey: queryKeys.issues.detail("issue-1"), |
| refetchType: "inactive", |
| }); |
| expect(invalidations).toContainEqual({ |
| queryKey: queryKeys.issues.activity("issue-1"), |
| refetchType: "inactive", |
| }); |
| expect(invalidations).not.toContainEqual({ |
| queryKey: queryKeys.issues.comments("issue-1"), |
| refetchType: "inactive", |
| }); |
| }); |
|
|
| it("keeps visible issue detail refetches inactive for downstream agent updates", () => { |
| const invalidations: unknown[] = []; |
| const queryClient = { |
| invalidateQueries: (input: unknown) => { |
| invalidations.push(input); |
| }, |
| getQueryData: (key: unknown) => { |
| if (JSON.stringify(key) === JSON.stringify(queryKeys.issues.detail("PAP-759"))) { |
| return { |
| id: "issue-1", |
| identifier: "PAP-759", |
| assigneeAgentId: "agent-1", |
| }; |
| } |
| return undefined; |
| }, |
| }; |
|
|
| __liveUpdatesTestUtils.invalidateActivityQueries( |
| queryClient as never, |
| "company-1", |
| { |
| entityType: "issue", |
| entityId: "issue-1", |
| action: "issue.updated", |
| actorType: "system", |
| actorId: "heartbeat", |
| details: { |
| identifier: "PAP-759", |
| source: "deferred_comment_wake", |
| }, |
| }, |
| { userId: null, agentId: null }, |
| { pathname: "/PAP/issues/PAP-759", isForegrounded: true }, |
| ); |
|
|
| expect(invalidations).toContainEqual({ |
| queryKey: queryKeys.issues.detail("issue-1"), |
| refetchType: "inactive", |
| }); |
| expect(invalidations).toContainEqual({ |
| queryKey: queryKeys.issues.activity("issue-1"), |
| refetchType: "inactive", |
| }); |
| }); |
|
|
| it("still actively refetches visible issue detail for board-authored updates", () => { |
| const invalidations: unknown[] = []; |
| const queryClient = { |
| invalidateQueries: (input: unknown) => { |
| invalidations.push(input); |
| }, |
| getQueryData: (key: unknown) => { |
| if (JSON.stringify(key) === JSON.stringify(queryKeys.issues.detail("PAP-759"))) { |
| return { |
| id: "issue-1", |
| identifier: "PAP-759", |
| assigneeAgentId: "agent-1", |
| }; |
| } |
| return undefined; |
| }, |
| }; |
|
|
| __liveUpdatesTestUtils.invalidateActivityQueries( |
| queryClient as never, |
| "company-1", |
| { |
| entityType: "issue", |
| entityId: "issue-1", |
| action: "issue.updated", |
| actorType: "user", |
| actorId: "user-2", |
| details: { |
| identifier: "PAP-759", |
| status: "in_progress", |
| }, |
| }, |
| { userId: "user-1", agentId: null }, |
| { pathname: "/PAP/issues/PAP-759", isForegrounded: true }, |
| ); |
|
|
| expect(invalidations).toContainEqual({ |
| queryKey: queryKeys.issues.detail("issue-1"), |
| }); |
| expect(invalidations).toContainEqual({ |
| queryKey: queryKeys.issues.activity("issue-1"), |
| }); |
| expect(invalidations).not.toContainEqual({ |
| queryKey: queryKeys.issues.detail("issue-1"), |
| refetchType: "inactive", |
| }); |
| }); |
|
|
| it("keeps visible issue comment updates inactive-only instead of active refetching", () => { |
| const invalidations: unknown[] = []; |
| const queryClient = { |
| invalidateQueries: (input: unknown) => { |
| invalidations.push(input); |
| }, |
| getQueryData: (key: unknown) => { |
| if (JSON.stringify(key) === JSON.stringify(queryKeys.issues.detail("PAP-759"))) { |
| return { |
| id: "issue-1", |
| identifier: "PAP-759", |
| assigneeAgentId: "agent-1", |
| }; |
| } |
| return undefined; |
| }, |
| }; |
|
|
| __liveUpdatesTestUtils.invalidateActivityQueries( |
| queryClient as never, |
| "company-1", |
| { |
| entityType: "issue", |
| entityId: "issue-1", |
| action: "issue.comment_added", |
| actorType: "agent", |
| actorId: "agent-1", |
| details: { |
| identifier: "PAP-759", |
| commentId: "comment-1", |
| bodySnippet: "New agent comment", |
| }, |
| }, |
| { userId: null, agentId: null }, |
| { pathname: "/PAP/issues/PAP-759", isForegrounded: true }, |
| ); |
|
|
| expect(invalidations).toContainEqual({ |
| queryKey: queryKeys.issues.detail("issue-1"), |
| refetchType: "inactive", |
| }); |
| expect(invalidations).toContainEqual({ |
| queryKey: queryKeys.issues.activity("issue-1"), |
| refetchType: "inactive", |
| }); |
| expect(invalidations).toContainEqual({ |
| queryKey: queryKeys.issues.comments("issue-1"), |
| refetchType: "inactive", |
| }); |
| }); |
|
|
| it("refreshes visible issue run queries when the displayed run changes status", () => { |
| const invalidations: unknown[] = []; |
| const cache = new Map<string, unknown>([ |
| [JSON.stringify(queryKeys.issues.detail("PAP-759")), { |
| id: "issue-1", |
| identifier: "PAP-759", |
| assigneeAgentId: "agent-1", |
| executionRunId: "run-1", |
| executionAgentNameKey: "codexcoder", |
| executionLockedAt: new Date("2026-04-08T21:00:00.000Z"), |
| }], |
| [JSON.stringify(queryKeys.issues.activeRun("PAP-759")), { |
| id: "run-1", |
| }], |
| [JSON.stringify(queryKeys.issues.liveRuns("PAP-759")), [{ id: "run-1" }]], |
| [JSON.stringify(queryKeys.issues.runs("PAP-759")), [{ runId: "run-1" }]], |
| ]); |
| const queryClient = { |
| invalidateQueries: (input: unknown) => { |
| invalidations.push(input); |
| }, |
| getQueryData: (key: unknown) => { |
| return cache.get(JSON.stringify(key)); |
| }, |
| setQueryData: (key: unknown, updater: unknown) => { |
| const cacheKey = JSON.stringify(key); |
| const current = cache.get(cacheKey); |
| cache.set(cacheKey, typeof updater === "function" ? updater(current) : updater); |
| }, |
| }; |
|
|
| const invalidated = __liveUpdatesTestUtils.invalidateVisibleIssueRunQueries( |
| queryClient as never, |
| "/PAP/issues/PAP-759", |
| { |
| runId: "run-1", |
| agentId: "agent-1", |
| status: "succeeded", |
| }, |
| { isForegrounded: true }, |
| ); |
|
|
| expect(invalidated).toBe(true); |
| expect(invalidations).toContainEqual({ |
| queryKey: queryKeys.issues.detail("PAP-759"), |
| }); |
| expect(invalidations).toContainEqual({ |
| queryKey: queryKeys.issues.activity("PAP-759"), |
| }); |
| expect(invalidations).toContainEqual({ |
| queryKey: queryKeys.issues.runs("PAP-759"), |
| }); |
| expect(invalidations).toContainEqual({ |
| queryKey: queryKeys.issues.liveRuns("PAP-759"), |
| }); |
| expect(invalidations).toContainEqual({ |
| queryKey: queryKeys.issues.activeRun("PAP-759"), |
| }); |
| expect(cache.get(JSON.stringify(queryKeys.issues.activeRun("PAP-759")))).toBeNull(); |
| expect(cache.get(JSON.stringify(queryKeys.issues.liveRuns("PAP-759")))).toEqual([]); |
| expect(cache.get(JSON.stringify(queryKeys.issues.detail("PAP-759")))).toMatchObject({ |
| executionRunId: null, |
| executionAgentNameKey: null, |
| executionLockedAt: null, |
| }); |
| }); |
|
|
| it("ignores run status events for other issues", () => { |
| const invalidations: unknown[] = []; |
| const queryClient = { |
| invalidateQueries: (input: unknown) => { |
| invalidations.push(input); |
| }, |
| getQueryData: (key: unknown) => { |
| if (JSON.stringify(key) === JSON.stringify(queryKeys.issues.detail("PAP-759"))) { |
| return { |
| id: "issue-1", |
| identifier: "PAP-759", |
| assigneeAgentId: "agent-1", |
| }; |
| } |
| if (JSON.stringify(key) === JSON.stringify(queryKeys.issues.activeRun("PAP-759"))) { |
| return { |
| id: "run-1", |
| }; |
| } |
| if (JSON.stringify(key) === JSON.stringify(queryKeys.issues.liveRuns("PAP-759"))) { |
| return [{ id: "run-1" }]; |
| } |
| if (JSON.stringify(key) === JSON.stringify(queryKeys.issues.runs("PAP-759"))) { |
| return [{ runId: "run-1" }]; |
| } |
| return undefined; |
| }, |
| setQueryData: vi.fn(), |
| }; |
|
|
| const invalidated = __liveUpdatesTestUtils.invalidateVisibleIssueRunQueries( |
| queryClient as never, |
| "/PAP/issues/PAP-759", |
| { |
| runId: "run-2", |
| agentId: "agent-2", |
| status: "succeeded", |
| }, |
| { isForegrounded: true }, |
| ); |
|
|
| expect(invalidated).toBe(false); |
| expect(invalidations).toEqual([]); |
| }); |
| }); |
|
|
| describe("LiveUpdatesProvider visible issue comment hydration", () => { |
| it("hydrates the visible issue comments cache with only the new comment", async () => { |
| getCommentMock.mockResolvedValueOnce({ |
| id: "comment-2", |
| companyId: "company-1", |
| issueId: "issue-1", |
| authorAgentId: "agent-1", |
| authorUserId: null, |
| body: "Second comment", |
| createdAt: "2026-04-13T15:00:00.000Z", |
| updatedAt: "2026-04-13T15:00:00.000Z", |
| }); |
|
|
| const setCalls: Array<{ key: unknown; value: unknown }> = []; |
| const queryClient = { |
| getQueryData: (key: unknown) => { |
| if (JSON.stringify(key) === JSON.stringify(queryKeys.issues.detail("PAP-759"))) { |
| return { |
| id: "issue-1", |
| identifier: "PAP-759", |
| assigneeAgentId: "agent-1", |
| }; |
| } |
| if (JSON.stringify(key) === JSON.stringify(queryKeys.issues.comments("PAP-759"))) { |
| return { |
| pages: [[{ |
| id: "comment-1", |
| companyId: "company-1", |
| issueId: "issue-1", |
| authorAgentId: null, |
| authorUserId: "user-1", |
| body: "First comment", |
| createdAt: "2026-04-13T14:00:00.000Z", |
| updatedAt: "2026-04-13T14:00:00.000Z", |
| }]], |
| pageParams: [null], |
| }; |
| } |
| return undefined; |
| }, |
| setQueryData: (key: unknown, updater: (value: unknown) => unknown) => { |
| const current = queryClient.getQueryData(key); |
| setCalls.push({ key, value: updater(current) }); |
| }, |
| invalidateQueries: vi.fn(), |
| }; |
|
|
| await __liveUpdatesTestUtils.hydrateVisibleIssueComment( |
| queryClient as never, |
| "/PAP/issues/PAP-759", |
| { |
| entityType: "issue", |
| entityId: "issue-1", |
| action: "issue.comment_added", |
| details: { |
| identifier: "PAP-759", |
| commentId: "comment-2", |
| }, |
| }, |
| { isForegrounded: true }, |
| ); |
|
|
| expect(getCommentMock).toHaveBeenCalledWith("PAP-759", "comment-2"); |
| expect(setCalls).toHaveLength(1); |
| expect(setCalls[0]?.key).toEqual(queryKeys.issues.comments("PAP-759")); |
| expect(setCalls[0]?.value).toEqual({ |
| pages: [[ |
| { |
| id: "comment-2", |
| companyId: "company-1", |
| issueId: "issue-1", |
| authorAgentId: "agent-1", |
| authorUserId: null, |
| body: "Second comment", |
| createdAt: "2026-04-13T15:00:00.000Z", |
| updatedAt: "2026-04-13T15:00:00.000Z", |
| }, |
| { |
| id: "comment-1", |
| companyId: "company-1", |
| issueId: "issue-1", |
| authorAgentId: null, |
| authorUserId: "user-1", |
| body: "First comment", |
| createdAt: "2026-04-13T14:00:00.000Z", |
| updatedAt: "2026-04-13T14:00:00.000Z", |
| }, |
| ]], |
| pageParams: [null], |
| }); |
| }); |
| }); |
|
|
| describe("LiveUpdatesProvider visible issue toast suppression", () => { |
| it("suppresses activity toasts for the issue page currently in view", () => { |
| const queryClient = { |
| getQueryData: (key: unknown) => { |
| if (JSON.stringify(key) === JSON.stringify(queryKeys.issues.detail("PAP-759"))) { |
| return { |
| id: "issue-1", |
| identifier: "PAP-759", |
| assigneeAgentId: "agent-1", |
| }; |
| } |
| return undefined; |
| }, |
| }; |
|
|
| expect( |
| __liveUpdatesTestUtils.shouldSuppressActivityToastForVisibleIssue( |
| queryClient as never, |
| "/PAP/issues/PAP-759", |
| { |
| entityType: "issue", |
| entityId: "issue-1", |
| details: { identifier: "PAP-759" }, |
| }, |
| { isForegrounded: true }, |
| ), |
| ).toBe(true); |
|
|
| expect( |
| __liveUpdatesTestUtils.shouldSuppressActivityToastForVisibleIssue( |
| queryClient as never, |
| "/PAP/issues/PAP-759", |
| { |
| entityType: "issue", |
| entityId: "issue-2", |
| details: { identifier: "PAP-760" }, |
| }, |
| { isForegrounded: true }, |
| ), |
| ).toBe(false); |
| }); |
|
|
| it("suppresses run and agent status toasts for the assignee of the visible issue", () => { |
| const queryClient = { |
| getQueryData: (key: unknown) => { |
| if (JSON.stringify(key) === JSON.stringify(queryKeys.issues.detail("PAP-759"))) { |
| return { |
| id: "issue-1", |
| identifier: "PAP-759", |
| assigneeAgentId: "agent-1", |
| }; |
| } |
| return undefined; |
| }, |
| }; |
|
|
| expect( |
| __liveUpdatesTestUtils.shouldSuppressRunStatusToastForVisibleIssue( |
| queryClient as never, |
| "/PAP/issues/PAP-759", |
| { |
| runId: "run-1", |
| agentId: "agent-1", |
| }, |
| { isForegrounded: true }, |
| ), |
| ).toBe(true); |
|
|
| expect( |
| __liveUpdatesTestUtils.shouldSuppressAgentStatusToastForVisibleIssue( |
| queryClient as never, |
| "/PAP/issues/PAP-759", |
| { |
| agentId: "agent-1", |
| status: "running", |
| }, |
| { isForegrounded: true }, |
| ), |
| ).toBe(true); |
| }); |
| }); |
|
|
| describe("LiveUpdatesProvider run lifecycle toasts", () => { |
| it("routes issue activity copy through translation helpers", () => { |
| const queryClient = { |
| getQueryData: (key: unknown) => { |
| if (JSON.stringify(key) === JSON.stringify(queryKeys.issues.detail("issue-1"))) { |
| return { |
| id: "issue-1", |
| identifier: "PAP-759", |
| title: "Fix localization regression", |
| }; |
| } |
| if (JSON.stringify(key) === JSON.stringify(queryKeys.issues.list("company-1"))) { |
| return [ |
| { |
| id: "issue-1", |
| identifier: "PAP-759", |
| title: "Fix localization regression", |
| }, |
| ]; |
| } |
| return undefined; |
| }, |
| }; |
| const t = buildFakeT({ |
| "liveUpdates.actor.board": "董事会", |
| "liveUpdates.activity.issueUpdatedTitle": "{{actor}} 更新了 {{issueRef}}", |
| "liveUpdates.action.viewIssue": "查看 {{issueRef}}", |
| "liveUpdates.issueUpdate.statusChanged": "状态 -> {{status}}", |
| "liveUpdates.issueUpdate.priorityChanged": "优先级 -> {{priority}}", |
| "status.inProgress": "进行中", |
| "priority.high": "高", |
| }); |
|
|
| const toast = __liveUpdatesTestUtils.buildActivityToastWithT( |
| t, |
| queryClient as never, |
| "company-1", |
| { |
| entityType: "issue", |
| entityId: "issue-1", |
| action: "issue.updated", |
| actorType: "user", |
| actorId: "user-1", |
| details: { |
| identifier: "PAP-759", |
| status: "in_progress", |
| priority: "high", |
| }, |
| }, |
| { userId: null, agentId: null }, |
| ); |
|
|
| expect(toast).toMatchObject({ |
| title: "董事会 更新了 PAP-759", |
| tone: "info", |
| action: { |
| label: "查看 PAP-759", |
| href: "/issues/PAP-759", |
| }, |
| }); |
| expect(toast?.body).toContain("状态 -> 进行中"); |
| expect(toast?.body).toContain("优先级 -> 高"); |
| }); |
|
|
| it("does not build start or success toasts for agent runs", () => { |
| const queryClient = { |
| getQueryData: () => [], |
| }; |
|
|
| expect( |
| __liveUpdatesTestUtils.buildAgentStatusToast( |
| { |
| agentId: "agent-1", |
| status: "running", |
| }, |
| () => "CodexCoder", |
| queryClient as never, |
| "company-1", |
| ), |
| ).toBeNull(); |
|
|
| expect( |
| __liveUpdatesTestUtils.buildRunStatusToast( |
| { |
| runId: "run-1", |
| agentId: "agent-1", |
| status: "succeeded", |
| }, |
| () => "CodexCoder", |
| ), |
| ).toBeNull(); |
| }); |
|
|
| it("still builds failure toasts for agent errors and failed runs", () => { |
| const queryClient = { |
| getQueryData: () => [ |
| { |
| id: "agent-1", |
| title: "Software Engineer", |
| }, |
| ], |
| }; |
|
|
| expect( |
| __liveUpdatesTestUtils.buildAgentStatusToast( |
| { |
| agentId: "agent-1", |
| status: "error", |
| }, |
| () => "CodexCoder", |
| queryClient as never, |
| "company-1", |
| ), |
| ).toMatchObject({ |
| title: "CodexCoder errored", |
| body: "Software Engineer", |
| tone: "error", |
| }); |
|
|
| expect( |
| __liveUpdatesTestUtils.buildRunStatusToast( |
| { |
| runId: "run-1", |
| agentId: "agent-1", |
| status: "failed", |
| error: "boom", |
| }, |
| () => "CodexCoder", |
| ), |
| ).toMatchObject({ |
| title: "CodexCoder run failed", |
| body: "boom", |
| tone: "error", |
| }); |
| }); |
|
|
| it("routes run status copy through translation helpers", () => { |
| const t = buildFakeT({ |
| "liveUpdates.runStatus.failedTitle": "{{name}} 运行失败", |
| "liveUpdates.runStatus.triggerDetail": "触发来源:{{triggerDetail}}", |
| "liveUpdates.action.viewRun": "查看运行", |
| }); |
|
|
| expect( |
| __liveUpdatesTestUtils.buildRunStatusToastWithT( |
| t, |
| { |
| runId: "run-1", |
| agentId: "agent-1", |
| status: "failed", |
| triggerDetail: "timer", |
| }, |
| () => "CodexCoder", |
| ), |
| ).toMatchObject({ |
| title: "CodexCoder 运行失败", |
| body: "触发来源:timer", |
| tone: "error", |
| action: { |
| label: "查看运行", |
| href: "/agents/agent-1/runs/run-1", |
| }, |
| }); |
| }); |
| }); |
|
|