refactor(app): assert store state rather than mock call counts in store tests (#715)

Rewrites two store tests to verify observable state instead of implementation-level call counts.

navigation-active-workspace-store: the "persists" test now uses a functional in-memory AsyncStorage mock that retains written values across vi.resetModules(), then rehydrates a fresh store instance and asserts the resulting selection state matches what was written.

checkout-git-actions-store: removes toHaveBeenCalledWith/toHaveBeenCalledTimes assertions on client methods; replaces with getStatus() assertions that reflect the store's actual state after each action completes or fails.
This commit is contained in:
Mohamed Boudra
2026-05-05 19:58:55 +07:00
parent fd30eafafc
commit 90c7e591e8
2 changed files with 38 additions and 18 deletions
@@ -91,7 +91,6 @@ describe("checkout-git-actions-store", () => {
const first = store.commit({ serverId, cwd }); const first = store.commit({ serverId, cwd });
const second = store.commit({ serverId, cwd }); const second = store.commit({ serverId, cwd });
expect(client.checkoutCommit).toHaveBeenCalledTimes(1);
expect(store.getStatus({ serverId, cwd, actionId: "commit" })).toBe("pending"); expect(store.getStatus({ serverId, cwd, actionId: "commit" })).toBe("pending");
deferred.resolve({}); deferred.resolve({});
@@ -126,8 +125,9 @@ describe("checkout-git-actions-store", () => {
await useCheckoutGitActionsStore.getState().pullAndPush({ serverId, cwd }); await useCheckoutGitActionsStore.getState().pullAndPush({ serverId, cwd });
expect(order).toEqual(["pull", "push"]); expect(order).toEqual(["pull", "push"]);
expect(client.checkoutPull).toHaveBeenCalledWith(cwd); expect(
expect(client.checkoutPush).toHaveBeenCalledWith(cwd); useCheckoutGitActionsStore.getState().getStatus({ serverId, cwd, actionId: "pull-and-push" }),
).toBe("success");
}); });
it("does not push when pull fails for pull-and-push", async () => { it("does not push when pull fails for pull-and-push", async () => {
@@ -146,7 +146,9 @@ describe("checkout-git-actions-store", () => {
await expect( await expect(
useCheckoutGitActionsStore.getState().pullAndPush({ serverId, cwd }), useCheckoutGitActionsStore.getState().pullAndPush({ serverId, cwd }),
).rejects.toThrow("pull conflict"); ).rejects.toThrow("pull conflict");
expect(client.checkoutPush).not.toHaveBeenCalled(); expect(
useCheckoutGitActionsStore.getState().getStatus({ serverId, cwd, actionId: "pull-and-push" }),
).toBe("idle");
}); });
it("surfaces push errors from pull-and-push after a successful pull", async () => { it("surfaces push errors from pull-and-push after a successful pull", async () => {
@@ -165,8 +167,9 @@ describe("checkout-git-actions-store", () => {
await expect( await expect(
useCheckoutGitActionsStore.getState().pullAndPush({ serverId, cwd }), useCheckoutGitActionsStore.getState().pullAndPush({ serverId, cwd }),
).rejects.toThrow("push rejected"); ).rejects.toThrow("push rejected");
expect(client.checkoutPull).toHaveBeenCalledTimes(1); expect(
expect(client.checkoutPush).toHaveBeenCalledTimes(1); useCheckoutGitActionsStore.getState().getStatus({ serverId, cwd, actionId: "pull-and-push" }),
).toBe("idle");
}); });
it("invalidates checkout PR status and every PR pane timeline for a checkout", async () => { it("invalidates checkout PR status and every PR pane timeline for a checkout", async () => {
@@ -212,7 +215,6 @@ describe("checkout-git-actions-store", () => {
.getState() .getState()
.archiveWorktree({ serverId, cwd, worktreePath: cwd }); .archiveWorktree({ serverId, cwd, worktreePath: cwd });
expect(client.archivePaseoWorktree).toHaveBeenCalledWith({ worktreePath: cwd });
expect(useSessionStore.getState().sessions[serverId]?.workspaces.has(cwd)).toBe(false); expect(useSessionStore.getState().sessions[serverId]?.workspaces.has(cwd)).toBe(false);
expect(appQueryClient.getQueryData(["sidebarPaseoWorktreeList", serverId, "/tmp"])).toEqual([ expect(appQueryClient.getQueryData(["sidebarPaseoWorktreeList", serverId, "/tmp"])).toEqual([
{ worktreePath: "/tmp/other" }, { worktreePath: "/tmp/other" },
@@ -1,9 +1,15 @@
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
const asyncStorageMock = vi.hoisted(() => ({ const asyncStorageMock = vi.hoisted(() => {
getItem: vi.fn<(_: string) => Promise<string | null>>(), const storage = new Map<string, string>();
setItem: vi.fn<(_: string, __: string) => Promise<void>>(), return {
})); storage,
getItem: vi.fn(async (key: string): Promise<string | null> => storage.get(key) ?? null),
setItem: vi.fn(async (key: string, value: string): Promise<void> => {
storage.set(key, value);
}),
};
});
vi.mock("@react-native-async-storage/async-storage", () => ({ vi.mock("@react-native-async-storage/async-storage", () => ({
default: asyncStorageMock, default: asyncStorageMock,
@@ -94,10 +100,17 @@ function createNavigationPathWithParamsRef(path: string, serverId: string, works
describe("navigation active workspace store", () => { describe("navigation active workspace store", () => {
beforeEach(() => { beforeEach(() => {
vi.resetModules(); vi.resetModules();
asyncStorageMock.storage.clear();
asyncStorageMock.getItem.mockReset(); asyncStorageMock.getItem.mockReset();
asyncStorageMock.setItem.mockReset(); asyncStorageMock.setItem.mockReset();
asyncStorageMock.getItem.mockResolvedValue(null); asyncStorageMock.getItem.mockImplementation(
asyncStorageMock.setItem.mockResolvedValue(); async (key: string): Promise<string | null> => asyncStorageMock.storage.get(key) ?? null,
);
asyncStorageMock.setItem.mockImplementation(
async (key: string, value: string): Promise<void> => {
asyncStorageMock.storage.set(key, value);
},
);
}); });
afterEach(() => { afterEach(() => {
@@ -222,7 +235,6 @@ describe("navigation active workspace store", () => {
it("hydrates empty and corrupt workspace route storage as null", async () => { it("hydrates empty and corrupt workspace route storage as null", async () => {
installWindowStub("/open-project"); installWindowStub("/open-project");
asyncStorageMock.getItem.mockResolvedValueOnce(null);
let store = await import("@/stores/navigation-active-workspace-store"); let store = await import("@/stores/navigation-active-workspace-store");
await store.hydrateLastNavigationWorkspaceRouteSelection(); await store.hydrateLastNavigationWorkspaceRouteSelection();
@@ -244,10 +256,16 @@ describe("navigation active workspace store", () => {
await store.hydrateLastNavigationWorkspaceRouteSelection(); await store.hydrateLastNavigationWorkspaceRouteSelection();
store.syncNavigationActiveWorkspace(createNavigationRef("server-1", "workspace-a")); store.syncNavigationActiveWorkspace(createNavigationRef("server-1", "workspace-a"));
await Promise.resolve(); // flush the fire-and-forget setItem microtask
expect(asyncStorageMock.setItem).toHaveBeenCalledWith( vi.resetModules();
LAST_WORKSPACE_ROUTE_SELECTION_STORAGE_KEY, const freshStore = await import("@/stores/navigation-active-workspace-store");
JSON.stringify({ serverId: "server-1", workspaceId: "workspace-a" }), await freshStore.hydrateLastNavigationWorkspaceRouteSelection();
);
expect(freshStore.getLastNavigationWorkspaceRouteSelection()).toEqual({
serverId: "server-1",
workspaceId: "workspace-a",
});
expect(freshStore.getIsLastNavigationWorkspaceRouteSelectionLoaded()).toBe(true);
}); });
}); });