test(dashboard): update component expectations to intentional post-FN-87xx behavior
Dashboard bare-run repair, component-drift cluster. All nine suites were stale expectations behind cited intentional commits — no UI regressions found: FN-8826's always-on WIP lifecycle badge (TaskCard.oversight now asserts no overseer element instead of an empty header), FN-8762's Todo Lists plugin extraction (MobileNavBar, MainContent.graph-popout, useAppSettings repurposed to the pluginDashboardViews contract, with a regression pin that the removed todosEnabled field never returns), FN-8796's clock-arbitrated mergeTaskSnapshot, FN-8797's prompt-only planning refresh, FN-8702's 767.98px phone-sheet boundary, FN-8764's primary-role split, and FN-8947's added touch-target selector. Every update carries an FNXC comment citing the causing commit. Verified 9 files / 293 tests green, tsconfig.app.json typecheck clean. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -59,7 +59,14 @@ function findMobileBlockContaining(needle: string): string {
|
||||
describe("mission + planning modal mobile CSS", () => {
|
||||
it("MissionManager: mission icon button touch targets are 36px", () => {
|
||||
const missionBlock = findMobileBlockContaining(".mission-manager-overlay");
|
||||
expect(missionBlock).toMatch(/\.mission-list__item-actions \.mission-icon-btn,[\s\S]*?\.mission-feature__actions \.mission-icon-btn\s*\{[\s\S]*?min-width:\s*36px;[\s\S]*?min-height:\s*36px;/s);
|
||||
/*
|
||||
FNXC:Missions 2026-08-15-22:40:
|
||||
FN-8947 (b82f1a41f3) appended `.mission-fix-feature__actions .mission-icon-btn` to this
|
||||
touch-target selector list, so `.mission-feature__actions .mission-icon-btn` is no longer
|
||||
the final selector before `{`. Match the selectors up to the fix-feature entry that now
|
||||
closes the list, keeping the same 36px touch-target invariant.
|
||||
*/
|
||||
expect(missionBlock).toMatch(/\.mission-list__item-actions \.mission-icon-btn,[\s\S]*?\.mission-feature__actions \.mission-icon-btn,[\s\S]*?\.mission-fix-feature__actions \.mission-icon-btn\s*\{[\s\S]*?min-width:\s*36px;[\s\S]*?min-height:\s*36px;/s);
|
||||
});
|
||||
|
||||
it("MissionManager: body prevents horizontal overflow", () => {
|
||||
|
||||
@@ -84,7 +84,12 @@ describe("Advanced Settings", () => {
|
||||
expect((screen.getByLabelText("Name") as HTMLInputElement).value).toBe("Interviewed Agent");
|
||||
expect((screen.getByLabelText("Title") as HTMLInputElement).value).toBe("Draft Title");
|
||||
expect((screen.getByLabelText("Icon") as HTMLInputElement).value).toBe("🧠");
|
||||
expect((screen.getByLabelText("Role") as HTMLSelectElement).value).toBe("reviewer");
|
||||
/*
|
||||
FNXC:Agents 2026-08-15-22:45:
|
||||
FN-8764 (eaadd153b1) split agent roles into a primary role plus additional workflow
|
||||
roles, renaming this select's label from "Role" to "Primary role".
|
||||
*/
|
||||
expect((screen.getByLabelText("Primary role") as HTMLSelectElement).value).toBe("reviewer");
|
||||
});
|
||||
expect(mockUpdateAgent).not.toHaveBeenCalled();
|
||||
|
||||
@@ -94,7 +99,9 @@ describe("Advanced Settings", () => {
|
||||
"agent-001",
|
||||
expect.objectContaining({
|
||||
name: "Interviewed Agent",
|
||||
role: "reviewer",
|
||||
// FNXC:Agents 2026-08-15-22:45: FN-8764 (eaadd153b1) saves the role list as `roles`
|
||||
// (primary first) instead of a scalar `role`.
|
||||
roles: ["reviewer"],
|
||||
title: "Draft Title",
|
||||
reportsTo: "agent-002",
|
||||
runtimeConfig: expect.objectContaining({ model: "openai/gpt-4o" }),
|
||||
|
||||
@@ -288,19 +288,32 @@ describe("MobileNavBar", () => {
|
||||
expect(screen.getByTestId("mobile-more-item-plugin-fusion-plugin-spacing-check-wide")).toBeDefined();
|
||||
});
|
||||
|
||||
it("keeps Todos in the mobile More sheet and routes to the todos view", () => {
|
||||
/*
|
||||
FNXC:Navigation 2026-08-15-22:15:
|
||||
FN-8762 (5b2b31d2c9) extracted Todo Lists into the bundled `fusion-plugin-todos`
|
||||
plugin: the host `todos` More-sheet item and `experimentalFeatures.todoView` gate
|
||||
are gone, and Todos now surfaces via `pluginDashboardViews` with overflow placement.
|
||||
These tests keep the same coverage (More-sheet only, routing, More-tab active) in
|
||||
the plugin form.
|
||||
*/
|
||||
const todosPluginView = {
|
||||
pluginId: "fusion-plugin-todos",
|
||||
view: { viewId: "todos", label: "Todos", componentPath: "./dashboard-view", icon: "CheckSquare", placement: "overflow" as const, order: 70 },
|
||||
};
|
||||
|
||||
it("keeps Todos in the mobile More sheet and routes to the todos plugin view", () => {
|
||||
const props = createDefaultProps();
|
||||
render(
|
||||
<MobileNavBar
|
||||
{...props}
|
||||
experimentalFeatures={{ todoView: true }}
|
||||
pluginDashboardViews={[todosPluginView]}
|
||||
/>,
|
||||
);
|
||||
|
||||
fireEvent.click(screen.getByTestId("mobile-nav-tab-more"));
|
||||
fireEvent.click(screen.getByTestId("mobile-more-item-todos"));
|
||||
fireEvent.click(screen.getByTestId("mobile-more-item-plugin-fusion-plugin-todos-todos"));
|
||||
|
||||
expect(props.onChangeView).toHaveBeenCalledWith("todos");
|
||||
expect(props.onChangeView).toHaveBeenCalledWith("plugin:fusion-plugin-todos:todos");
|
||||
});
|
||||
|
||||
it("Mailbox is a primary tab and is not duplicated in the More sheet", () => {
|
||||
@@ -316,22 +329,22 @@ describe("MobileNavBar", () => {
|
||||
render(
|
||||
<MobileNavBar
|
||||
{...createDefaultProps()}
|
||||
experimentalFeatures={{ todoView: true }}
|
||||
pluginDashboardViews={[todosPluginView]}
|
||||
/>,
|
||||
);
|
||||
|
||||
expect(screen.queryByTestId("mobile-nav-tab-todos")).toBeNull();
|
||||
expect(screen.queryByTestId("mobile-nav-tab-plugin-fusion-plugin-todos-todos")).toBeNull();
|
||||
|
||||
fireEvent.click(screen.getByTestId("mobile-nav-tab-more"));
|
||||
expect(screen.getByTestId("mobile-more-item-todos")).toBeInTheDocument();
|
||||
expect(screen.getByTestId("mobile-more-item-plugin-fusion-plugin-todos-todos")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("marks the mobile More tab active for the todos view", () => {
|
||||
it("marks the mobile More tab active for the todos plugin view", () => {
|
||||
render(
|
||||
<MobileNavBar
|
||||
{...createDefaultProps()}
|
||||
view="todos"
|
||||
experimentalFeatures={{ todoView: true }}
|
||||
view="plugin:fusion-plugin-todos:todos"
|
||||
pluginDashboardViews={[todosPluginView]}
|
||||
/>,
|
||||
);
|
||||
|
||||
|
||||
@@ -209,8 +209,15 @@ describe("ScheduledTasksModal", () => {
|
||||
|
||||
it("keeps mobile Automations full-screen and hides resize handles by CSS contract", () => {
|
||||
const source = readFileSync(resolve(__dirname, "../ScriptsModal.css"), "utf8");
|
||||
/*
|
||||
FNXC:GitManager 2026-08-15-22:35:
|
||||
FN-8702 (1e67e87321) moved ScriptsModal.css's standalone phone-sheet blocks from
|
||||
`max-width: 768px` to `max-width: 767.98px` so the takeover applies only BELOW the
|
||||
768px boundary (768px-wide tablets keep desktop geometry). The Automations
|
||||
full-screen contract rides in that same block, so match the phone boundary.
|
||||
*/
|
||||
const mobileBlock = source
|
||||
.match(/@media \(max-width: 768px\)\s*\{[\s\S]*?\n\}/g)
|
||||
.match(/@media \(max-width: 767\.98px\)\s*\{[\s\S]*?\n\}/g)
|
||||
?.find((block) => block.includes(".floating-window--automation")) ?? "";
|
||||
|
||||
expect(mobileBlock).toContain(".floating-window--automation");
|
||||
|
||||
@@ -102,6 +102,22 @@ function makeTask(overrides: Partial<Task> = {}): Task {
|
||||
|
||||
const noop = () => {};
|
||||
|
||||
/*
|
||||
FNXC:PlannerOversight 2026-08-15-22:10:
|
||||
FN-8826 (1f5c44ac71) intentionally restored an always-on WIP lifecycle badge for
|
||||
empty-status cards in WIP lanes, so `card-header-badges` now legitimately mounts
|
||||
on the stale-snapshot fixtures below. The oversight invariant these tests guard is
|
||||
narrower: the header wrapper must never contain an overseer eye element while the
|
||||
effective oversight level is off/unresolved/inherited-default. Assert that instead
|
||||
of wrapper absence.
|
||||
*/
|
||||
function expectHeaderBadgesFreeOfOverseerEye() {
|
||||
const header = screen.queryByTestId("card-header-badges");
|
||||
if (header) {
|
||||
expect(header.querySelector(".card-planner-overseer-state")).toBeNull();
|
||||
}
|
||||
}
|
||||
|
||||
function renderCard(
|
||||
overrides: Partial<Task> = {},
|
||||
cardProps: {
|
||||
@@ -272,14 +288,14 @@ describe("TaskCard workflow-effective oversight level (FN-7516 code-review fix)"
|
||||
// unresolved on first render. The stale runtime snapshot must not leak an
|
||||
// Eye badge or header wrapper before the configured-off response arrives.
|
||||
expect(screen.queryByTestId("planner-overseer-state-badge")).toBeNull();
|
||||
expect(screen.queryByTestId("card-header-badges")).toBeNull();
|
||||
expectHeaderBadgesFreeOfOverseerEye();
|
||||
|
||||
await waitFor(() => {
|
||||
expect(fetchWorkflowSettingValues).toHaveBeenCalledWith(`wf-stale-snapshot-off-${column}`, undefined);
|
||||
});
|
||||
await waitFor(() => {
|
||||
expect(screen.queryByTestId("planner-overseer-state-badge")).toBeNull();
|
||||
expect(screen.queryByTestId("card-header-badges")).toBeNull();
|
||||
expectHeaderBadgesFreeOfOverseerEye();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -425,13 +441,13 @@ describe("TaskCard selected-workflow oversight identity (FN-8251)", () => {
|
||||
renderCard(staleSnapshot(column), { planningWorkflowId: " selected-workflow-off ", projectId: "project-8251" });
|
||||
|
||||
expect(screen.queryByTestId("planner-overseer-state-badge")).toBeNull();
|
||||
expect(screen.queryByTestId("card-header-badges")).toBeNull();
|
||||
expectHeaderBadgesFreeOfOverseerEye();
|
||||
await waitFor(() => {
|
||||
expect(fetchWorkflowSettingValues).toHaveBeenCalledWith("selected-workflow-off", "project-8251");
|
||||
});
|
||||
await waitFor(() => {
|
||||
expect(screen.queryByTestId("planner-overseer-state-badge")).toBeNull();
|
||||
expect(screen.queryByTestId("card-header-badges")).toBeNull();
|
||||
expectHeaderBadgesFreeOfOverseerEye();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -447,13 +463,13 @@ describe("TaskCard selected-workflow oversight identity (FN-8251)", () => {
|
||||
// The selected workflow's declaration default is not a meaningful oversight
|
||||
// configuration, so a stale runtime snapshot cannot create an eye or shell.
|
||||
expect(screen.queryByTestId("planner-overseer-state-badge")).toBeNull();
|
||||
expect(screen.queryByTestId("card-header-badges")).toBeNull();
|
||||
expectHeaderBadgesFreeOfOverseerEye();
|
||||
await waitFor(() => {
|
||||
expect(fetchWorkflowSettingValues).toHaveBeenCalledWith(`selected-inherited-default-${column}`, "project-8255");
|
||||
});
|
||||
await waitFor(() => {
|
||||
expect(screen.queryByTestId("planner-overseer-state-badge")).toBeNull();
|
||||
expect(screen.queryByTestId("card-header-badges")).toBeNull();
|
||||
expectHeaderBadgesFreeOfOverseerEye();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -475,12 +491,12 @@ describe("TaskCard selected-workflow oversight identity (FN-8251)", () => {
|
||||
});
|
||||
|
||||
expect(screen.queryByTestId("planner-overseer-state-badge")).toBeNull();
|
||||
expect(screen.queryByTestId("card-header-badges")).toBeNull();
|
||||
expectHeaderBadgesFreeOfOverseerEye();
|
||||
await waitFor(() => {
|
||||
expect(fetchWorkflowSettingValues).toHaveBeenCalledWith("aggregate-inherited-default", undefined);
|
||||
});
|
||||
expect(screen.queryByTestId("planner-overseer-state-badge")).toBeNull();
|
||||
expect(screen.queryByTestId("card-header-badges")).toBeNull();
|
||||
expectHeaderBadgesFreeOfOverseerEye();
|
||||
expect(container.querySelector(".card-planner-overseer-state[title][aria-label]")).toBeNull();
|
||||
});
|
||||
|
||||
@@ -495,7 +511,7 @@ describe("TaskCard selected-workflow oversight identity (FN-8251)", () => {
|
||||
await waitFor(() => expect(fetchWorkflowSettingValues).toHaveBeenCalled());
|
||||
await waitFor(() => {
|
||||
expect(screen.queryByTestId("planner-overseer-state-badge")).toBeNull();
|
||||
expect(screen.queryByTestId("card-header-badges")).toBeNull();
|
||||
expectHeaderBadgesFreeOfOverseerEye();
|
||||
expect(screen.queryByLabelText(/overseer/i)).toBeNull();
|
||||
});
|
||||
});
|
||||
@@ -503,7 +519,7 @@ describe("TaskCard selected-workflow oversight identity (FN-8251)", () => {
|
||||
it("fails closed without workflow identity but keeps a valid active task override authoritative", () => {
|
||||
const { rerender } = renderCard(staleSnapshot("in-progress"));
|
||||
expect(screen.queryByTestId("planner-overseer-state-badge")).toBeNull();
|
||||
expect(screen.queryByTestId("card-header-badges")).toBeNull();
|
||||
expectHeaderBadgesFreeOfOverseerEye();
|
||||
|
||||
rerender(
|
||||
<TaskCard
|
||||
@@ -575,7 +591,7 @@ describe("TaskCard selected-workflow oversight identity (FN-8251)", () => {
|
||||
await waitFor(() => expect(fetchWorkflowSettingValues).toHaveBeenCalledTimes(2));
|
||||
await waitFor(() => {
|
||||
expect(screen.queryByTestId("planner-overseer-state-badge")).toBeNull();
|
||||
expect(screen.queryByTestId("card-header-badges")).toBeNull();
|
||||
expectHeaderBadgesFreeOfOverseerEye();
|
||||
expect(container.querySelector(".card-planner-overseer-state[title][aria-label]")).toBeNull();
|
||||
});
|
||||
});
|
||||
@@ -596,12 +612,12 @@ describe("TaskCard selected-workflow oversight identity (FN-8251)", () => {
|
||||
act(() => notifyWorkflowSettingValuesUpdated(workflowId, "project-cache-fix"));
|
||||
|
||||
expect(screen.queryByTestId("planner-overseer-state-badge")).toBeNull();
|
||||
expect(screen.queryByTestId("card-header-badges")).toBeNull();
|
||||
expectHeaderBadgesFreeOfOverseerEye();
|
||||
expect(container.querySelector(".card-planner-overseer-state[title][aria-label]")).toBeNull();
|
||||
await waitFor(() => expect(fetchWorkflowSettingValues).toHaveBeenCalledTimes(2));
|
||||
await waitFor(() => {
|
||||
expect(screen.queryByTestId("planner-overseer-state-badge")).toBeNull();
|
||||
expect(screen.queryByTestId("card-header-badges")).toBeNull();
|
||||
expectHeaderBadgesFreeOfOverseerEye();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -626,7 +642,7 @@ describe("TaskCard selected-workflow oversight identity (FN-8251)", () => {
|
||||
resolveActive?.({ stored: { plannerOversightLevel: "steer" }, effective: { plannerOversightLevel: "steer" }, orphaned: [] });
|
||||
});
|
||||
expect(screen.queryByTestId("planner-overseer-state-badge")).toBeNull();
|
||||
expect(screen.queryByTestId("card-header-badges")).toBeNull();
|
||||
expectHeaderBadgesFreeOfOverseerEye();
|
||||
});
|
||||
|
||||
it("hides the eye synchronously when an active selected workflow changes to an unresolved off workflow", async () => {
|
||||
@@ -661,7 +677,7 @@ describe("TaskCard selected-workflow oversight identity (FN-8251)", () => {
|
||||
expect(screen.queryByTestId("planner-overseer-state-badge")).toBeNull();
|
||||
await waitFor(() => expect(fetchWorkflowSettingValues).toHaveBeenCalledWith("mobile-inherited-default", undefined));
|
||||
expect(screen.queryByTestId("planner-overseer-state-badge")).toBeNull();
|
||||
expect(screen.queryByTestId("card-header-badges")).toBeNull();
|
||||
expectHeaderBadgesFreeOfOverseerEye();
|
||||
expect(container.querySelector(".card-planner-overseer-state[title][aria-label]")).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -84,7 +84,13 @@ describe("TaskDetailModal GitLab tracking", () => {
|
||||
rerender(
|
||||
<TaskDetailModal
|
||||
initialTab="definition"
|
||||
task={makeTask({ column: "todo", gitlabTracking: { item: { ...projectIssue, kind: "merge_request", iid: 5, url: "https://gitlab.com/acme/app/-/merge_requests/5", title: "MR" } } })}
|
||||
/*
|
||||
FNXC:TaskDetailStateStability 2026-08-15-22:25:
|
||||
FN-8796 (1320265455) made mergeTaskSnapshot clock-arbitrated: an equal-clock sparse
|
||||
prop no longer replaces populated detail fields, so this rerender must carry a newer
|
||||
`updatedAt` to model a real server mutation (see makeUpdatedTask's contract note).
|
||||
*/
|
||||
task={makeTask({ column: "todo", updatedAt: "2026-01-01T00:00:01Z", gitlabTracking: { item: { ...projectIssue, kind: "merge_request", iid: 5, url: "https://gitlab.com/acme/app/-/merge_requests/5", title: "MR" } } })}
|
||||
onClose={noop}
|
||||
onMoveTask={noopMove}
|
||||
onDeleteTask={noopDelete}
|
||||
|
||||
@@ -87,23 +87,30 @@ describe("TaskDetailContent hidden-popup gating (active=false)", () => {
|
||||
|
||||
it("keeps Definition prompt refresh request-free while hidden and resumes one planning chain on reveal", async () => {
|
||||
vi.useFakeTimers();
|
||||
const mockDetail = vi.mocked(dashboardApi.fetchTaskDetail);
|
||||
mockDetail.mockReset();
|
||||
mockDetail.mockResolvedValue(makeTask({ id: "FN-9001", column: "triage", status: "planning", prompt: "# Fresh plan" }));
|
||||
/*
|
||||
FNXC:TaskDetailPlan 2026-08-15-22:30:
|
||||
FN-8797 (35810666be) made the Definition tab's 5s planning refresh prompt-only via
|
||||
`fetchTaskPrompt` — the old `fetchTaskDetail` chain rolled queued cards back to Todo,
|
||||
so the periodic read no longer touches the detail endpoint. The hidden/reveal gating
|
||||
invariant is unchanged; only the polled request boundary moved.
|
||||
*/
|
||||
const mockPrompt = vi.mocked(dashboardApi.fetchTaskPrompt);
|
||||
mockPrompt.mockReset();
|
||||
mockPrompt.mockResolvedValue({ id: "FN-9001", prompt: "# Fresh plan" });
|
||||
|
||||
const { rerenderWithActive } = renderContent(false, makeTask({ id: "FN-9001", column: "triage", status: "planning" }));
|
||||
await act(async () => { await vi.advanceTimersByTimeAsync(16_000); });
|
||||
expect(mockDetail).not.toHaveBeenCalled();
|
||||
expect(mockPrompt).not.toHaveBeenCalled();
|
||||
|
||||
rerenderWithActive(true);
|
||||
await act(async () => { await vi.advanceTimersByTimeAsync(0); });
|
||||
expect(mockDetail).toHaveBeenCalledTimes(1);
|
||||
expect(mockPrompt).toHaveBeenCalledTimes(1);
|
||||
await act(async () => { await vi.advanceTimersByTimeAsync(5_000); });
|
||||
expect(mockDetail).toHaveBeenCalledTimes(2);
|
||||
expect(mockPrompt).toHaveBeenCalledTimes(2);
|
||||
|
||||
rerenderWithActive(false);
|
||||
await act(async () => { await vi.advanceTimersByTimeAsync(16_000); });
|
||||
expect(mockDetail).toHaveBeenCalledTimes(2);
|
||||
expect(mockPrompt).toHaveBeenCalledTimes(2);
|
||||
});
|
||||
|
||||
it("suspends the 5s verification polling while hidden and resumes it on reveal", async () => {
|
||||
|
||||
@@ -104,6 +104,23 @@ function mainContentProps(overrides: Partial<MainContentProps> = {}): MainConten
|
||||
handleResumeProject: vi.fn(),
|
||||
handleRemoveProject: vi.fn(),
|
||||
nodes: [],
|
||||
/*
|
||||
FNXC:TodoPluginEnablement 2026-08-15-22:20:
|
||||
FN-8762 (5b2b31d2c9) gated plugin task views on the project-scoped
|
||||
`pluginDashboardViews` roster (MainContent.isEnabledPluginTaskView), so this
|
||||
harness must enroll the plugin views it renders or MainContent treats them as
|
||||
disabled. The same commit removed the host `todosEnabled`/`TodoView` props.
|
||||
*/
|
||||
pluginDashboardViews: [
|
||||
{
|
||||
pluginId: "fusion-plugin-dependency-graph",
|
||||
view: { viewId: "graph", label: "Graph", componentPath: "./dashboard-view", icon: "Workflow", placement: "primary", order: 1 },
|
||||
},
|
||||
{
|
||||
pluginId: "example",
|
||||
view: { viewId: "dashboard", label: "Example", componentPath: "./dashboard-view", icon: "Workflow", placement: "overflow", order: 2 },
|
||||
},
|
||||
] as MainContentProps["pluginDashboardViews"],
|
||||
graphPluginTaskView: "plugin:fusion-plugin-dependency-graph:graph",
|
||||
graphWorkflowSelection: null,
|
||||
setGraphWorkflowSelection: vi.fn(),
|
||||
@@ -145,7 +162,6 @@ function mainContentProps(overrides: Partial<MainContentProps> = {}): MainConten
|
||||
memoryEnabled: true,
|
||||
goalsEnabled: true,
|
||||
handleOpenMission: vi.fn(),
|
||||
todosEnabled: true,
|
||||
openPlanningWithInitialPlanWithNav: vi.fn(),
|
||||
ingestCreatedTasks: vi.fn(),
|
||||
nodesEnabled: true,
|
||||
@@ -211,7 +227,6 @@ function mainContentProps(overrides: Partial<MainContentProps> = {}): MainConten
|
||||
ResearchView: LazyStub as MainContentProps["ResearchView"],
|
||||
SecretsView: LazyStub as MainContentProps["SecretsView"],
|
||||
SkillsView: LazyStub as MainContentProps["SkillsView"],
|
||||
TodoView: LazyStub as MainContentProps["TodoView"],
|
||||
_AutomationsView: LazyStub as MainContentProps["_AutomationsView"],
|
||||
_ImportTasksView: LazyStub as MainContentProps["_ImportTasksView"],
|
||||
_SettingsView: LazyStub as MainContentProps["_SettingsView"],
|
||||
|
||||
@@ -523,7 +523,14 @@ describe("useAppSettings", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("derives todosEnabled from experimentalFeatures.todoView", async () => {
|
||||
/*
|
||||
FNXC:Navigation 2026-08-15-22:15:
|
||||
FN-8762 (5b2b31d2c9) extracted Todo Lists into the bundled `fusion-plugin-todos`
|
||||
plugin and removed the host `todosEnabled` derivation from useAppSettings; the
|
||||
`experimentalFeatures.todoView` gate no longer exists. Regression-pin that the
|
||||
removed field never reappears on the hook result.
|
||||
*/
|
||||
it("no longer derives a host todosEnabled flag (Todos is the fusion-plugin-todos plugin, FN-8762)", async () => {
|
||||
mockFetchSettings.mockResolvedValueOnce({
|
||||
autoMerge: false,
|
||||
globalPause: false,
|
||||
@@ -539,8 +546,9 @@ describe("useAppSettings", () => {
|
||||
const { result } = renderHook(() => useAppSettings("proj_123"));
|
||||
|
||||
await waitFor(() => {
|
||||
expect(result.current.todosEnabled).toBe(true);
|
||||
expect(result.current.taskStuckTimeoutMs).toBe(600000);
|
||||
});
|
||||
expect("todosEnabled" in result.current).toBe(false);
|
||||
});
|
||||
|
||||
it("derives goalsEnabled from experimentalFeatures.goalsView", async () => {
|
||||
|
||||
Reference in New Issue
Block a user