test(dashboard): align app tests with recent product changes (FN-7057/7340/7156/7342/6825/7265)
Update five dashboard app test files whose assertions drifted from intentional product changes that landed on main without updating them: - graph-workflow-header: FN-7057 treats stale/missing workflow ids as the default workflow, so FN-unknown now shows under the default selection. - EngineControlMenu.css: FN-7340 added a 768px range-thumb touch-target block; narrow the popover-breakpoint assertion to that selector. - MissionManager.delete-confirm: FN-7156 removed first-mission auto-select; explicitly select the mission before the detail-delete flow. - board-mobile-initial-render: FN-7342 preserves board column scroll during stabilization; FN-6825 renders the workflow toolbar on options, not callbacks. - workflow-auto-layout: FN-7265 removed the stepwise review node (per-step review lives in the foreach); the connected run ends at completion-summary. No assertion was loosened or deleted to force a pass; each change cites the breaking commit via an FNXC comment. packages/dashboard is private (no changeset).
This commit is contained in:
@@ -89,11 +89,15 @@ describe("Graph workflow header integration", () => {
|
||||
expect(headerSlot.contains(selector)).toBe(true);
|
||||
|
||||
const contextTasks = screen.getByTestId("graph-plugin-context-tasks");
|
||||
/*
|
||||
FNXC:GraphWorkflowSelection 2026-07-07-08:15:
|
||||
FN-7057 changed filterTasksByGraphWorkflowSelection to treat stale taskWorkflowIds entries that reference deleted/missing workflows as DEFAULT-workflow assignments (so tasks never vanish from every view). FN-unknown carries wf-missing, so under the default Coding selection it now resolves to the default workflow and is SHOWN, not filtered out.
|
||||
*/
|
||||
await waitFor(() => {
|
||||
expect(within(contextTasks).getByTestId("graph-context-task-FN-default")).toBeInTheDocument();
|
||||
expect(within(contextTasks).getByTestId("graph-context-task-FN-unassigned")).toBeInTheDocument();
|
||||
expect(within(contextTasks).queryByTestId("graph-context-task-FN-review")).toBeNull();
|
||||
expect(within(contextTasks).queryByTestId("graph-context-task-FN-unknown")).toBeNull();
|
||||
expect(within(contextTasks).getByTestId("graph-context-task-FN-unknown")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
fireEvent.click(selector);
|
||||
|
||||
@@ -90,6 +90,28 @@ function extractMobileMediaBlocks(css: string): string {
|
||||
return blocks.join("\n");
|
||||
}
|
||||
|
||||
function extractMediaBlocksForWidth(css: string, width: string): string {
|
||||
const blocks: string[] = [];
|
||||
const regex = new RegExp(`@media[^{}]*\\(max-width:\\s*${width}\\)[^{]*\\{`, "g");
|
||||
let match: RegExpExecArray | null;
|
||||
|
||||
while ((match = regex.exec(css)) !== null) {
|
||||
const startIdx = match.index + match[0].length;
|
||||
let braceCount = 1;
|
||||
let endIdx = startIdx;
|
||||
while (braceCount > 0 && endIdx < css.length) {
|
||||
if (css[endIdx] === "{") braceCount += 1;
|
||||
if (css[endIdx] === "}") braceCount -= 1;
|
||||
endIdx += 1;
|
||||
}
|
||||
if (braceCount === 0) {
|
||||
blocks.push(css.slice(startIdx, endIdx - 1));
|
||||
}
|
||||
}
|
||||
|
||||
return blocks.join("\n");
|
||||
}
|
||||
|
||||
function normalizeCss(css: string): string {
|
||||
return css.replace(/\s+/g, " ").trim();
|
||||
}
|
||||
@@ -132,7 +154,11 @@ describe("EngineControlMenu CSS token validity (FN-6862)", () => {
|
||||
|
||||
it("renders the mobile and narrow-tablet footer popover as a viewport-safe bottom panel", () => {
|
||||
expect(componentCss).toContain("@media (max-width: 1024px)");
|
||||
expect(componentCss).not.toContain("@media (max-width: 768px)");
|
||||
/*
|
||||
FNXC:EngineControls 2026-07-07-08:20:
|
||||
FN-7340 added a @media (max-width: 768px) block for range-slider thumb touch-target sizing — a separate concern from popover placement, which stays on the 1024px breakpoint (extractMobileMediaBlocks). The earlier whole-file ban on "@media (max-width: 768px)" was too broad; assert instead that the 768px block never repositions the popover.
|
||||
*/
|
||||
expect(extractMediaBlocksForWidth(componentCss, "768px")).not.toContain(".engine-control-menu__popover");
|
||||
|
||||
const desktopPopoverBlock = normalizeCss(extractRuleBlock(componentCss, ".engine-control-menu__popover"));
|
||||
const footerDesktopPopoverBlock = normalizeCss(
|
||||
|
||||
@@ -227,6 +227,12 @@ describe("MissionManager mission delete confirmation", () => {
|
||||
const addToast = vi.fn();
|
||||
renderMissionManager(addToast);
|
||||
|
||||
/*
|
||||
FNXC:MissionManager 2026-07-07-08:25:
|
||||
FN-7156 made inline Missions open on the overview (no first-mission auto-selection), so a selected-detail delete must first click the mission in the list to load its detail before the fetchMission/delete-modal flow runs.
|
||||
*/
|
||||
fireEvent.click(await findMissionListItem("Build Auth System"));
|
||||
|
||||
await waitFor(() => {
|
||||
expect(mockFetchMission).toHaveBeenCalledWith("M-001", projectId);
|
||||
});
|
||||
|
||||
@@ -127,7 +127,7 @@ describe("Board mobile initial render stabilization (FN-4574)", () => {
|
||||
vi.unstubAllGlobals();
|
||||
});
|
||||
|
||||
it("normalizes scrollLeft to 0 on initial mobile render and keeps snap style in CSS, not inline", () => {
|
||||
it("preserves board column scroll during initial mobile stabilization while keeping snap style in CSS, not inline", () => {
|
||||
const viewportSpy = mockViewport(375);
|
||||
const raf = vi.fn<(cb: FrameRequestCallback) => number>((cb) => {
|
||||
setTimeout(() => cb(0), 0);
|
||||
@@ -145,14 +145,18 @@ describe("Board mobile initial render stabilization (FN-4574)", () => {
|
||||
act(() => {
|
||||
vi.runOnlyPendingTimers();
|
||||
});
|
||||
expect(board.scrollLeft).toBe(0);
|
||||
/*
|
||||
FNXC:BoardMobile 2026-07-07-08:30:
|
||||
FN-7342 (preserve board scroll during refresh stabilization) removed the `boardEl.scrollLeft = 0` reset from mobile stabilization — #board is the user's horizontal scroller, so stabilization now only normalizes document-level horizontal drift and must not force the board back to triage. The board's column scroll position is therefore preserved (500) instead of reset to 0; rAF scheduling and the CSS-not-inline snap invariant still hold.
|
||||
*/
|
||||
expect(board.scrollLeft).toBe(500);
|
||||
expect(raf).toHaveBeenCalled();
|
||||
expect(board.style.scrollSnapType).toBe("");
|
||||
|
||||
viewportSpy.mockRestore();
|
||||
});
|
||||
|
||||
it("re-anchors on pageshow persisted restore for mobile", () => {
|
||||
it("preserves board column scroll on pageshow persisted restore for mobile", () => {
|
||||
const viewportSpy = mockViewport(375);
|
||||
vi.stubGlobal("requestAnimationFrame", (cb: FrameRequestCallback) => {
|
||||
setTimeout(() => cb(0), 0);
|
||||
@@ -176,7 +180,11 @@ describe("Board mobile initial render stabilization (FN-4574)", () => {
|
||||
act(() => {
|
||||
vi.runOnlyPendingTimers();
|
||||
});
|
||||
expect(board.scrollLeft).toBe(0);
|
||||
/*
|
||||
FNXC:BoardMobile 2026-07-07-08:32:
|
||||
FN-7342 keeps pageshow/bfcache stabilization scoped to document-level drift, so the board column scroll set before the restore (500) is preserved rather than re-anchored to 0.
|
||||
*/
|
||||
expect(board.scrollLeft).toBe(500);
|
||||
|
||||
viewportSpy.mockRestore();
|
||||
});
|
||||
@@ -468,7 +476,11 @@ describe("Board mobile initial render stabilization (FN-4574)", () => {
|
||||
expect(document.querySelector("main.board.board-workflow-columns")).not.toBeNull();
|
||||
});
|
||||
|
||||
expect(document.querySelector(".board-workflow-toolbar")).toBeNull();
|
||||
/*
|
||||
FNXC:BoardMobile 2026-07-07-08:35:
|
||||
FN-6825 (combine workflow actions in switcher) moved edit/create into the WorkflowSwitcher, so shouldRenderWorkflowControls is now `workflowOptions.length > 0` rather than gated on onCreateWorkflow/onOpenWorkflowEditor. A Board rendered without those callbacks still shows the switcher toolbar whenever workflow options exist, so the no-callbacks toolbar shell no longer disappears.
|
||||
*/
|
||||
expect(document.querySelector(".board-workflow-toolbar")).not.toBeNull();
|
||||
expect(document.querySelectorAll(".board-workflow-columns [data-testid^='column-']")).toHaveLength(6);
|
||||
|
||||
viewportSpy.mockRestore();
|
||||
|
||||
@@ -236,11 +236,15 @@ describe("autoLayout — foreach / unreachable / cycles", () => {
|
||||
"code-review",
|
||||
"review",
|
||||
]);
|
||||
/*
|
||||
FNXC:WorkflowAutoLayout 2026-07-07-08:10:
|
||||
FN-7265 (align per-step review workflow) removed the standalone `review` prompt node from the stepwise IR — per-step review now happens inside the foreach (step-review), so the post-foreach success path is steps → browser-verification → code-review → completion-summary → merge-gate. The connected editor run for the stepwise built-in therefore ends at `completion-summary`, not `review`.
|
||||
*/
|
||||
assertAutoLayoutRunConnected("stepwise", workflowDef(BUILTIN_STEPWISE_CODING_WORKFLOW_IR), [
|
||||
"steps",
|
||||
"browser-verification",
|
||||
"code-review",
|
||||
"review",
|
||||
"completion-summary",
|
||||
]);
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user