fix(dashboard-tests): align remote-access mock store with FN-8491, ChatView mobile with FN-8502, and de-race planning claim tests

- remote-access-routes: FN-8491 made every project-context resolution call the
  plugin-MCP binder; the mock store now satisfies the runtime-owned-store guard.
- ChatView.mobile: sendMessage gained the FN-8502 {onDelivered,onFailed} arg.
- PlanningModeModal.planning-flow: settle-then-requery before clicking Proceed
  (detached-node race under CI load, same class 5a5796bca fixed for Stop/Refine)
  and await the create-dispatch mock signal instead of the DOM alone.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
gsxdsm
2026-07-23 23:09:59 -07:00
parent da0d73c853
commit d7752931b0
3 changed files with 40 additions and 5 deletions

View File

@@ -881,7 +881,17 @@ describe("ChatView mobile behavior", () => {
fireEvent.click(sendButton);
expect(sendMessage).toHaveBeenCalledTimes(1);
expect(sendMessage).toHaveBeenCalledWith("Hello mobile", []);
/*
FNXC:ChatAttachments 2026-07-23-23:00:
FN-8502 made ChatView pass attachment delivery callbacks as a third
sendMessage argument ({ onDelivered, onFailed }); assert them
structurally so the iOS first-tap send contract (text + empty
attachments) stays the protected invariant.
*/
expect(sendMessage).toHaveBeenCalledWith("Hello mobile", [], expect.objectContaining({
onDelivered: expect.any(Function),
onFailed: expect.any(Function),
}));
expect(document.activeElement).toBe(input);
} finally {
isIOSSpy.mockRestore();
@@ -911,7 +921,11 @@ describe("ChatView mobile behavior", () => {
fireEvent.click(sendButton);
expect(sendMessage).toHaveBeenCalledTimes(1);
expect(sendMessage).toHaveBeenCalledWith("Hello mobile", []);
// FNXC:ChatAttachments 2026-07-23-23:00: FN-8502 delivery-callback third arg (see above).
expect(sendMessage).toHaveBeenCalledWith("Hello mobile", [], expect.objectContaining({
onDelivered: expect.any(Function),
onFailed: expect.any(Function),
}));
expect(document.activeElement).toBe(input);
} finally {
restoreMatchMedia.mockRestore();

View File

@@ -589,10 +589,19 @@ describe("PlanningModeModal sequential flow", () => {
.mockResolvedValueOnce({ id: "FN-8442" });
renderSession();
fireEvent.click(await screen.findByRole("button", { name: "Proceed with plan" }));
/*
FNXC:PlanningMode 2026-07-23-23:30:
Settle pending hydration commits and click a freshly-queried node: clicking the button
reference returned by findByRole raced late hydration re-renders on loaded CI shards
(full-suite run 30069944059), dispatching on a detached node so the create never fired and
the view stayed on plan review. Same detached-node class as the Stop/Refine race (5a5796bca).
*/
await screen.findByRole("button", { name: "Proceed with plan" });
await act(async () => {});
fireEvent.click(screen.getByRole("button", { name: "Proceed with plan" }));
await waitFor(() => expect(mockCreateTaskFromPlanning).toHaveBeenCalledTimes(2));
expect(await screen.findByTestId("planning-task-created")).toHaveTextContent("FN-8442");
expect(mockCreateTaskFromPlanning).toHaveBeenCalledTimes(2);
expect(screen.queryByTestId("planning-create-retry")).toBeNull();
});
@@ -610,7 +619,10 @@ describe("PlanningModeModal sequential flow", () => {
.mockResolvedValueOnce({ id: "FN-8442" });
const { rerender } = renderSession();
fireEvent.click(await screen.findByRole("button", { name: "Proceed with plan" }));
// FNXC:PlanningMode 2026-07-23-23:30: settle hydration then click a fresh node (see detached-node note above).
await screen.findByRole("button", { name: "Proceed with plan" });
await act(async () => {});
fireEvent.click(screen.getByRole("button", { name: "Proceed with plan" }));
vi.useFakeTimers();
await act(async () => {

View File

@@ -87,6 +87,15 @@ function createMockStore(overrides: Partial<TaskStore> = {}): TaskStore {
moveTask: vi.fn(),
logEntry: vi.fn(),
getAgentLogs: vi.fn().mockResolvedValue([]),
/*
FNXC:PluginMcpServers 2026-07-23-00:00:
FN-8491 (3cd023fa4) made resolveProjectContext bind a project-scoped plugin
MCP provider on every getProjectContext call. A store that already exposes
getProjectScopedPluginMcpServers is treated as runtime-owned and skips the
binder (which would otherwise call getPluginStore()); declare it here so the
remote-access route contracts stay isolated from plugin-loader bootstrapping.
*/
getProjectScopedPluginMcpServers: vi.fn().mockResolvedValue([]),
on: vi.fn(),
off: vi.fn(),
...overrides,