fix(dashboard-tests): finish the FN-8491 mock backlog; contain FN-8557 innerWidth leakage

Nine more api-lane files get the established getProjectScopedPluginMcpServers
stub (includes shared-branch-group-entry-points, whose two current failures
were FN-8491 500s — the historical per-task-derived pair is gone). App lane:
FN-8557 made window.innerWidth<=768 a mobile signal, so leaked innerWidth=375
defineProperty stamps flipped later ListView/settings tests into mobile
layout (reset in beforeEach), and useModalResizePersist's 700px "mobile"
fixture became tablet-class (now a phone-class 375px viewport).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
gsxdsm
2026-07-24 02:14:26 -07:00
parent 16d30ee7b0
commit 12fa85eefb
12 changed files with 102 additions and 1 deletions

View File

@@ -420,6 +420,16 @@ describe("ListView", () => {
localStorage.clear();
showAllColumnsByDefault();
ensureMatchMedia();
/*
FNXC:ViewportMode 2026-07-24-02:30:
FN-8557 (973c978f9) made isMobileViewport treat `window.innerWidth <= 768`
as a mobile signal alongside matchMedia. mockMobileViewport stamps
innerWidth=375 via defineProperty and its spy restore only resets
matchMedia, so the leaked width flipped every later test into the mobile
card layout (no <tr> rows). Reset innerWidth to a desktop width here so the
per-test viewport helpers stay authoritative.
*/
Object.defineProperty(window, "innerWidth", { value: 1280, configurable: true });
vi.spyOn(window, "matchMedia").mockImplementation((query: string) => ({
matches: false,
media: query,
@@ -4221,6 +4231,8 @@ describe("ListView - Bulk Selection", () => {
for (const key of Object.keys(listViewSseHandlers)) delete listViewSseHandlers[key];
localStorage.clear();
ensureMatchMedia();
// FNXC:ViewportMode 2026-07-24-02:30: FN-8557 innerWidth leak reset (see the main ListView beforeEach comment).
Object.defineProperty(window, "innerWidth", { value: 1280, configurable: true });
vi.spyOn(window, "matchMedia").mockImplementation((query: string) => ({
matches: false,
media: query,

View File

@@ -163,6 +163,15 @@ function setDocumentHidden(hidden: boolean): void {
}
function mockSettingsViewport(matches: boolean): void {
/*
FNXC:ViewportMode 2026-07-24-02:20:
FN-8557 (973c978f9) made isMobileViewport treat `window.innerWidth <= 768` as a
mobile signal alongside matchMedia. Individual mobile tests here stamp
innerWidth=375 via defineProperty without restoring it, which leaked mobile
mode into later desktop assertions. The viewport mock now owns innerWidth in
both directions so each test's declared viewport is authoritative.
*/
Object.defineProperty(window, "innerWidth", { configurable: true, value: matches ? 375 : 1280 });
Object.defineProperty(window, "matchMedia", {
writable: true,
value: vi.fn().mockImplementation((query: string) => ({

View File

@@ -181,7 +181,15 @@ describe("useModalResizePersist", () => {
});
it("clears inline size and does not inject a grip on mobile", () => {
setViewport(700);
/*
FNXC:ViewportMode 2026-07-24-02:25:
FN-8557 (973c978f9) classifies a narrow touch viewport with a tablet-class
physical screen (min edge > 480px) as tablet, which keeps the FN-6377 touch
grip. jsdom reports `"ontouchstart" in window === true`, so the old 700x800
fixture became a tablet. Use a phone-class screen (375px min edge) so this
test still exercises the true mobile contract: no grip, inline size cleared.
*/
setViewport(375);
localStorage.setItem(STORAGE_KEY, JSON.stringify({ width: 610, height: 480 }));
render(<Harness initialWidth="610px" initialHeight="480px" />);

View File

@@ -20,6 +20,14 @@ class MockStore extends EventEmitter {
addTask(task: Task): void { this.tasks.set(task.id, task); }
getMissionStore() { return new EventEmitter(); }
async listTasks(): Promise<Task[]> { return []; }
/*
FNXC:PluginMcpServers 2026-07-24-02:05:
FN-8491 (3cd023fa4) made resolveProjectContext bind a project-scoped plugin
MCP provider on every getProjectContext call; a store exposing
getProjectScopedPluginMcpServers is treated as runtime-owned and skips the
binder (which would otherwise 500 on getPluginStore()).
*/
getProjectScopedPluginMcpServers = vi.fn().mockResolvedValue([]);
}
function createTask(overrides: Partial<Task> = {}): Task {

View File

@@ -68,6 +68,14 @@ function createMockStore(overrides: Partial<TaskStore> = {}): TaskStore {
updateWorkflowStep: vi.fn(),
deleteWorkflowStep: vi.fn(),
getMissionStore: vi.fn().mockReturnValue(createMockMissionStore()),
/*
FNXC:PluginMcpServers 2026-07-24-02:05:
FN-8491 (3cd023fa4) made resolveProjectContext bind a project-scoped plugin
MCP provider on every getProjectContext call; a store exposing
getProjectScopedPluginMcpServers is treated as runtime-owned and skips the
binder (which would otherwise 500 on getPluginStore()).
*/
getProjectScopedPluginMcpServers: vi.fn().mockResolvedValue([]),
...overrides,
} as unknown as TaskStore;
}

View File

@@ -237,6 +237,14 @@ function createMockStore(): TaskStore {
getMissionTask: vi.fn(),
deleteMissionTask: vi.fn(),
}),
/*
FNXC:PluginMcpServers 2026-07-24-02:05:
FN-8491 (3cd023fa4) made resolveProjectContext bind a project-scoped plugin
MCP provider on every getProjectContext call; a store exposing
getProjectScopedPluginMcpServers is treated as runtime-owned and skips the
binder (which would otherwise 500 on getPluginStore()).
*/
getProjectScopedPluginMcpServers: vi.fn().mockResolvedValue([]),
};
return store as unknown as TaskStore;

View File

@@ -20,6 +20,14 @@ function createMockStore(overrides: Partial<TaskStore> = {}): TaskStore {
logEntry: vi.fn().mockResolvedValue(undefined),
on: vi.fn(),
off: vi.fn(),
/*
FNXC:PluginMcpServers 2026-07-24-02:05:
FN-8491 (3cd023fa4) made resolveProjectContext bind a project-scoped plugin
MCP provider on every getProjectContext call; a store exposing
getProjectScopedPluginMcpServers is treated as runtime-owned and skips the
binder (which would otherwise 500 on getPluginStore()).
*/
getProjectScopedPluginMcpServers: vi.fn().mockResolvedValue([]),
...overrides,
} as unknown as TaskStore;
}

View File

@@ -34,6 +34,14 @@ class MockStore extends EventEmitter {
getWorkflowDefinition = vi.fn(async () => undefined);
getWorkflowSettingValues = vi.fn((workflowId: string, projectId: string) => this.workflowValues.get(`${workflowId}::${projectId}`) ?? {});
getWorkflowSettingsProjectId = vi.fn(() => "default");
/*
FNXC:PluginMcpServers 2026-07-24-02:05:
FN-8491 (3cd023fa4) made resolveProjectContext bind a project-scoped plugin
MCP provider on every getProjectContext call; a store exposing
getProjectScopedPluginMcpServers is treated as runtime-owned and skips the
binder (which would otherwise 500 on getPluginStore()).
*/
getProjectScopedPluginMcpServers = vi.fn().mockResolvedValue([]);
setSelection(taskId: string, workflowId: string): void {
this.workflowSelections.set(taskId, { workflowId, stepIds: [] });

View File

@@ -99,6 +99,14 @@ function createMockStore(fusionDir: string) {
getAllDocuments: vi.fn().mockResolvedValue([]),
listWorkflowSteps: vi.fn().mockResolvedValue([]),
getMissionStore: vi.fn(),
/*
FNXC:PluginMcpServers 2026-07-24-02:05:
FN-8491 (3cd023fa4) made resolveProjectContext bind a project-scoped plugin
MCP provider on every getProjectContext call; a store exposing
getProjectScopedPluginMcpServers is treated as runtime-owned and skips the
binder (which would otherwise 500 on getPluginStore()).
*/
getProjectScopedPluginMcpServers: vi.fn().mockResolvedValue([]),
} as any;
}

View File

@@ -60,6 +60,14 @@ function createMockStore(): TaskStore {
updateWorkflowStep: vi.fn(),
deleteWorkflowStep: vi.fn(),
getMissionStore: vi.fn(),
/*
FNXC:PluginMcpServers 2026-07-24-02:05:
FN-8491 (3cd023fa4) made resolveProjectContext bind a project-scoped plugin
MCP provider on every getProjectContext call; a store exposing
getProjectScopedPluginMcpServers is treated as runtime-owned and skips the
binder (which would otherwise 500 on getPluginStore()).
*/
getProjectScopedPluginMcpServers: vi.fn().mockResolvedValue([]),
} as unknown as TaskStore;
}

View File

@@ -11,6 +11,14 @@ describe("task workflow merge route", () => {
const store: TaskStore = {
getRootDir: vi.fn(() => process.cwd()),
mergeTask: vi.fn(),
/*
FNXC:PluginMcpServers 2026-07-24-02:05:
FN-8491 (3cd023fa4) made resolveProjectContext bind a project-scoped plugin
MCP provider on every getProjectContext call; a store exposing
getProjectScopedPluginMcpServers is treated as runtime-owned and skips the
binder (which would otherwise 500 on getPluginStore()).
*/
getProjectScopedPluginMcpServers: vi.fn().mockResolvedValue([]),
} as unknown as TaskStore;
const onMerge = vi.fn(async (id: string) => ({

View File

@@ -58,6 +58,14 @@ const createHarness = (taskState: any, events: RunAuditEvent[]) => {
}
return filtered;
}),
/*
FNXC:PluginMcpServers 2026-07-24-02:05:
FN-8491 (3cd023fa4) made resolveProjectContext bind a project-scoped plugin
MCP provider on every getProjectContext call; a store exposing
getProjectScopedPluginMcpServers is treated as runtime-owned and skips the
binder (which would otherwise 500 on getPluginStore()).
*/
getProjectScopedPluginMcpServers: vi.fn().mockResolvedValue([]),
} as unknown as TaskStore;
const app = express();