Files
kandev/apps/web/components/task-create-dialog-repository-autopick.test.ts
T
Carlos Florêncio b9eb6ae058 refactor(web): remove legacy settings migration (#1704)
* refactor(web): remove legacy settings migration

* fix(web): hydrate portable settings from boot state

* fix(web): preserve sidebar sync contracts

* fix(web): retain portable settings filters

* fix(web): retry sidebar settings writes

* fix(web): retain sidebar boot settings

* fix(websocket): avoid shutdown teardown leaks

* fix(boot): normalize sidebar task preferences

* fix(settings): preserve saved views during hydration

* fix(jira): avoid duplicate saved-view syncs

* fix(jira): preserve saved views on fetch failure

* fix(jira): retry saved-view hydration failures

* refactor(jira): simplify hydration request cleanup

* fix(websocket): close late clients after hub shutdown
2026-07-16 00:06:41 +01:00

159 lines
5.2 KiB
TypeScript

import { renderHook, waitFor } from "@testing-library/react";
import { beforeEach, describe, expect, it, vi } from "vitest";
import { useRepositoryAutoSelectEffect } from "./task-create-dialog-repository-autopick";
import type { DialogFormState, TaskRepoRow } from "@/components/task-create-dialog-types";
import type { Repository } from "@/lib/types/http";
const STORAGE_KEYS = { LAST_REPOSITORY_ID: "kandev.dialog.lastRepositoryId" } as const;
import {
readQueuedTaskCreateLastUsedState,
resetTaskCreateLastUsedSync,
} from "./task-create-dialog-handlers";
beforeEach(() => {
localStorage.clear();
resetTaskCreateLastUsedSync({ clearQueued: true });
});
function makeRepository(id: string): Repository {
return {
id,
workspace_id: "ws-1",
name: "repo",
source_type: "local",
local_path: "/repo",
default_branch: "main",
created_at: new Date().toISOString(),
updated_at: new Date().toISOString(),
} as Repository;
}
function makeRepoAutoSelectFs(
rows: TaskRepoRow[],
setRepositories: DialogFormState["setRepositories"],
): DialogFormState {
return {
repositories: rows,
useRemote: false,
setRepositories,
} as unknown as DialogFormState;
}
describe("useRepositoryAutoSelectEffect loading gates", () => {
it("waits for store-backed settings before falling back to an empty row", async () => {
const setRepositories = vi.fn();
const fs = makeRepoAutoSelectFs([], setRepositories);
const { rerender } = renderHook(
({ loaded, lastUsedRepositoryId }) =>
useRepositoryAutoSelectEffect(
fs,
true,
"ws-1",
[makeRepository("repo-1"), makeRepository("repo-2")],
{
lastUsedRepositoryId,
userSettingsLoaded: loaded,
},
),
{ initialProps: { loaded: false, lastUsedRepositoryId: null as string | null } },
);
await new Promise((resolve) => setTimeout(resolve, 10));
expect(setRepositories).not.toHaveBeenCalled();
rerender({ loaded: true, lastUsedRepositoryId: "repo-2" });
await waitFor(() => expect(setRepositories).toHaveBeenCalled());
const updater = setRepositories.mock.calls[0]![0] as (prev: TaskRepoRow[]) => TaskRepoRow[];
expect(updater([])).toEqual([{ key: "row-0", repositoryId: "repo-2", branch: "" }]);
});
it("ignores a stale cached repository while waiting for backend settings", async () => {
window.localStorage.setItem(STORAGE_KEYS.LAST_REPOSITORY_ID, JSON.stringify("repo-1"));
const setRepositories = vi.fn();
const fs = makeRepoAutoSelectFs([], setRepositories);
renderHook(() =>
useRepositoryAutoSelectEffect(
fs,
true,
"ws-1",
[makeRepository("repo-1"), makeRepository("repo-2")],
{
lastUsedRepositoryId: null,
userSettingsLoaded: false,
},
),
);
await new Promise((resolve) => setTimeout(resolve, 10));
expect(setRepositories).not.toHaveBeenCalled();
expect(readQueuedTaskCreateLastUsedState()).toEqual({});
});
it("ignores a stale cached repository after user settings have loaded", async () => {
window.localStorage.setItem(STORAGE_KEYS.LAST_REPOSITORY_ID, JSON.stringify("repo-1"));
const setRepositories = vi.fn();
const fs = makeRepoAutoSelectFs([], setRepositories);
renderHook(() =>
useRepositoryAutoSelectEffect(
fs,
true,
"ws-1",
[makeRepository("repo-1"), makeRepository("repo-2")],
{ userSettingsLoaded: true },
),
);
await waitFor(() => expect(setRepositories).toHaveBeenCalled());
const updater = setRepositories.mock.calls[0]![0] as (prev: TaskRepoRow[]) => TaskRepoRow[];
expect(updater([])).toEqual([{ key: "row-0", branch: "" }]);
expect(readQueuedTaskCreateLastUsedState()).toEqual({});
});
});
describe("useRepositoryAutoSelectEffect defaults", () => {
it("fills an untouched placeholder row from backend settings", async () => {
const setRepositories = vi.fn();
const fs = makeRepoAutoSelectFs([{ key: "row-0", branch: "" }], setRepositories);
renderHook(() =>
useRepositoryAutoSelectEffect(
fs,
true,
"ws-1",
[makeRepository("repo-1"), makeRepository("repo-2")],
{ lastUsedRepositoryId: "repo-2" },
),
);
await waitFor(() => expect(setRepositories).toHaveBeenCalled());
const updater = setRepositories.mock.calls[0]![0] as (prev: TaskRepoRow[]) => TaskRepoRow[];
expect(updater([{ key: "row-0", branch: "" }])).toEqual([
{ key: "row-0", repositoryId: "repo-2", branch: "" },
]);
});
it("fills an empty repo row list from backend settings", async () => {
const setRepositories = vi.fn();
const fs = makeRepoAutoSelectFs([], setRepositories);
renderHook(() =>
useRepositoryAutoSelectEffect(
fs,
true,
"ws-1",
[makeRepository("repo-1"), makeRepository("repo-2")],
{ lastUsedRepositoryId: "repo-1" },
),
);
await waitFor(() => expect(setRepositories).toHaveBeenCalled());
const updater = setRepositories.mock.calls[0]![0] as (prev: TaskRepoRow[]) => TaskRepoRow[];
expect(updater([])).toEqual([{ key: "row-0", repositoryId: "repo-1", branch: "" }]);
});
});