perf: isolate task surface rendering (#3226)
This commit is contained in:
@@ -4,7 +4,7 @@ import { act, cleanup, fireEvent, render, screen, waitFor } from "@testing-libra
|
||||
import { TooltipProvider } from "@kandev/ui/tooltip";
|
||||
import { StateProvider } from "@/components/state-provider";
|
||||
import { TaskContributionIcons } from "@/components/task/task-contribution-icons";
|
||||
import { PRTaskIcon } from "./pr-task-icon";
|
||||
import { normalizeTaskPRs, PRTaskIcon } from "./pr-task-icon";
|
||||
import type { AppState } from "@/lib/state/store";
|
||||
import type { TaskPR } from "@/lib/types/github";
|
||||
import type { TaskCIAutomationOptions } from "@/lib/types/github";
|
||||
@@ -100,6 +100,11 @@ beforeEach(() => {
|
||||
afterEach(() => cleanup());
|
||||
|
||||
describe("PRTaskIcon corrupted store entry", () => {
|
||||
it("reuses one empty list for missing or malformed PR data", () => {
|
||||
expect(normalizeTaskPRs(undefined)).toBe(normalizeTaskPRs(null));
|
||||
expect(normalizeTaskPRs({})).toBe(normalizeTaskPRs([]));
|
||||
});
|
||||
|
||||
// Regression: an upstream payload (partial hydration, WS reorder, etc.) once
|
||||
// landed in taskPRs.byTaskId[TASK_ID] as a non-array truthy value. The
|
||||
// length-based guards then fell through into MultiPRIcon, where for-of
|
||||
|
||||
@@ -39,6 +39,7 @@ const SKY_400 = CHANGE_REQUEST_STATUS_COLORS.review;
|
||||
const EMERALD_400 = CHANGE_REQUEST_STATUS_COLORS.ready;
|
||||
const QUEUED = CHANGE_REQUEST_STATUS_COLORS.queued;
|
||||
const GREEN_500 = CHANGE_REQUEST_STATUS_COLORS.passing;
|
||||
const EMPTY_PRS: TaskPR[] = [];
|
||||
|
||||
/** Maps the task-level PR projection to the same visual language as live PRs. */
|
||||
export function getPRAggregateStatusColor(state: string | null | undefined): string {
|
||||
@@ -253,7 +254,7 @@ export function pickDefaultPR(prs: TaskPR[]): TaskPR | null {
|
||||
export function PRTaskIcon({ taskId, prInfo }: { taskId: string; prInfo?: TaskPRInfo }) {
|
||||
const prs = useAppStore((state) => getTaskPRsForCurrentWorkspace(state, taskId));
|
||||
const hydration = useTaskPRTooltipHydration(taskId, { includeAutomation: true });
|
||||
const fullPRs = Array.isArray(prs) && prs.length > 0 ? prs : [];
|
||||
const fullPRs = normalizeTaskPRs(prs);
|
||||
|
||||
// Defensive: an upstream payload may briefly seed byTaskId[taskId] with a
|
||||
// non-array value (e.g. an empty object from a partial hydration). Bail
|
||||
@@ -271,6 +272,10 @@ export function PRTaskIcon({ taskId, prInfo }: { taskId: string; prInfo?: TaskPR
|
||||
);
|
||||
}
|
||||
|
||||
export function normalizeTaskPRs(prs: unknown): TaskPR[] {
|
||||
return Array.isArray(prs) && prs.length > 0 ? prs : EMPTY_PRS;
|
||||
}
|
||||
|
||||
type TaskPRIconPresentation = {
|
||||
hasFullData: boolean;
|
||||
singlePR: TaskPR | null;
|
||||
|
||||
Reference in New Issue
Block a user