150 lines
4.7 KiB
TypeScript
150 lines
4.7 KiB
TypeScript
import { describe, expect, it } from "vitest";
|
|
|
|
import {
|
|
getImproveKandevStepDescription,
|
|
getImproveKandevBrowserStorage,
|
|
getImproveKandevForkBlockedReason,
|
|
initialImproveKandevMode,
|
|
readImproveKandevSkipIntro,
|
|
contributorAccessMessage,
|
|
resolveImproveKandevWorkflow,
|
|
writeImproveKandevSkipIntro,
|
|
} from "./improve-kandev-dialog-model";
|
|
import type { WorkflowStep } from "@/lib/types/http";
|
|
|
|
class MemoryStorage {
|
|
private values = new Map<string, string>();
|
|
|
|
getItem(key: string) {
|
|
return this.values.get(key) ?? null;
|
|
}
|
|
|
|
setItem(key: string, value: string) {
|
|
this.values.set(key, value);
|
|
}
|
|
|
|
removeItem(key: string) {
|
|
this.values.delete(key);
|
|
}
|
|
}
|
|
|
|
describe("improve kandev dialog model", () => {
|
|
it("reads and writes the intro preference without requiring browser storage", () => {
|
|
const storage = new MemoryStorage();
|
|
expect(readImproveKandevSkipIntro(undefined)).toBe(false);
|
|
expect(readImproveKandevSkipIntro(storage)).toBe(false);
|
|
|
|
writeImproveKandevSkipIntro(storage, true);
|
|
expect(readImproveKandevSkipIntro(storage)).toBe(true);
|
|
|
|
writeImproveKandevSkipIntro(storage, false);
|
|
expect(readImproveKandevSkipIntro(storage)).toBe(false);
|
|
});
|
|
|
|
it("falls back safely when storage throws", () => {
|
|
const storage = {
|
|
getItem: () => {
|
|
throw new Error("blocked");
|
|
},
|
|
setItem: () => {
|
|
throw new Error("blocked");
|
|
},
|
|
removeItem: () => {
|
|
throw new Error("blocked");
|
|
},
|
|
};
|
|
expect(readImproveKandevSkipIntro(storage)).toBe(false);
|
|
expect(() => writeImproveKandevSkipIntro(storage, true)).not.toThrow();
|
|
});
|
|
|
|
it("falls back safely when the browser localStorage property throws", () => {
|
|
const original = Object.getOwnPropertyDescriptor(globalThis, "localStorage");
|
|
Object.defineProperty(globalThis, "localStorage", {
|
|
configurable: true,
|
|
get() {
|
|
throw new Error("blocked");
|
|
},
|
|
});
|
|
try {
|
|
expect(getImproveKandevBrowserStorage()).toBeUndefined();
|
|
} finally {
|
|
if (original) Object.defineProperty(globalThis, "localStorage", original);
|
|
else delete (globalThis as { localStorage?: unknown }).localStorage;
|
|
}
|
|
});
|
|
|
|
it("chooses create mode only after the intro has been dismissed", () => {
|
|
expect(initialImproveKandevMode(false)).toBe("intro");
|
|
expect(initialImproveKandevMode(true)).toBe("create");
|
|
});
|
|
|
|
it("selects the issue workflow and its start step for Open issue", () => {
|
|
const implementStep = {
|
|
id: "implement-step",
|
|
name: "Improve",
|
|
position: 0,
|
|
is_start_step: true,
|
|
} as WorkflowStep;
|
|
const issueStep = {
|
|
id: "issue-step",
|
|
name: "Open issue",
|
|
position: 0,
|
|
is_start_step: true,
|
|
} as WorkflowStep;
|
|
const ready = {
|
|
data: {
|
|
workflow_id: "implementation",
|
|
issue_workflow_id: "issue-report",
|
|
repository_id: "repo",
|
|
branch: "main",
|
|
fork_status: "unknown" as const,
|
|
},
|
|
steps: [implementStep],
|
|
issueSteps: [issueStep],
|
|
};
|
|
|
|
expect(resolveImproveKandevWorkflow(ready, "issue")).toEqual({
|
|
workflowId: "issue-report",
|
|
steps: ready.issueSteps,
|
|
startStep: ready.issueSteps[0],
|
|
});
|
|
expect(resolveImproveKandevWorkflow(ready, "bug")).toEqual({
|
|
workflowId: "implementation",
|
|
steps: ready.steps,
|
|
startStep: ready.steps[0],
|
|
});
|
|
});
|
|
|
|
it("blocks only implementation kinds for managed fork restrictions", () => {
|
|
expect(getImproveKandevForkBlockedReason("bug", "blocked_emu", "account_cannot_fork")).toBe(
|
|
"common:thisGithubAccountCannotForkKdlbs",
|
|
);
|
|
expect(getImproveKandevForkBlockedReason("feature", "blocked_managed", "fork_conflict")).toBe(
|
|
"common:managedGithubForkUnavailable",
|
|
);
|
|
expect(
|
|
getImproveKandevForkBlockedReason("issue", "blocked_emu", "account_cannot_fork"),
|
|
).toBeNull();
|
|
expect(getImproveKandevForkBlockedReason("bug", "unknown", undefined)).toBeNull();
|
|
});
|
|
|
|
it("prefers stable workflow step ids for descriptions", () => {
|
|
expect(
|
|
getImproveKandevStepDescription({ id: "improve", name: "Renamed phase" } as WorkflowStep),
|
|
).toContain("implements the change");
|
|
});
|
|
});
|
|
|
|
describe("improve kandev contributor access", () => {
|
|
it("describes each workflow kind with a translation key", () => {
|
|
expect(contributorAccessMessage("issue", false)).toBe("common:improveKandevIssueNoForkAccess");
|
|
expect(contributorAccessMessage("bug", true)).toBe("common:improveKandevDirectPushAccess");
|
|
expect(contributorAccessMessage("feature", false, "creatable")).toBe(
|
|
"common:improveKandevManagedForkPrepared",
|
|
);
|
|
expect(contributorAccessMessage("feature", false, "ready")).toBe(
|
|
"common:improveKandevExistingForkPrepared",
|
|
);
|
|
});
|
|
});
|