Files
kandev/apps/web/components/linear/linear-issue-watch-form.test.ts
T

135 lines
4.2 KiB
TypeScript

import { describe, it, expect } from "vitest";
import {
buildWatchPayload,
formStateFromWatch,
isWatchFormReady,
makeEmptyForm,
maxInflightTasksString,
parseMaxInflightTasks,
type FormState,
} from "./linear-issue-watch-form";
import type { LinearIssueWatch } from "@/lib/types/linear";
describe("parseMaxInflightTasks", () => {
it("treats blank / whitespace as uncapped (null)", () => {
expect(parseMaxInflightTasks("")).toBeNull();
expect(parseMaxInflightTasks(" ")).toBeNull();
});
it("accepts positive integers", () => {
expect(parseMaxInflightTasks("5")).toBe(5);
expect(parseMaxInflightTasks(" 12 ")).toBe(12);
});
it("rejects zero, negatives, and non-integers", () => {
expect(parseMaxInflightTasks("0")).toBe("invalid");
expect(parseMaxInflightTasks("-1")).toBe("invalid");
expect(parseMaxInflightTasks("1.5")).toBe("invalid");
expect(parseMaxInflightTasks("abc")).toBe("invalid");
});
});
describe("maxInflightTasksString", () => {
it("renders null/undefined and non-positive values as blank (uncapped)", () => {
expect(maxInflightTasksString(null)).toBe("");
expect(maxInflightTasksString(undefined)).toBe("");
expect(maxInflightTasksString(0)).toBe("");
expect(maxInflightTasksString(-3)).toBe("");
});
it("renders positive values as the integer string", () => {
expect(maxInflightTasksString(5)).toBe("5");
});
});
// makeEmptyForm seeds a valid filter-less skeleton; tests add a filter field
// and a destination so isWatchFormReady's other gates pass and we isolate the
// cap behaviour.
function readyForm(overrides: Partial<FormState> = {}): FormState {
return {
...makeEmptyForm("ws-1"),
teamKey: "ENG",
workflowId: "wf-1",
workflowStepId: "step-1",
prompt: "do the thing",
...overrides,
};
}
// Builds a FormState from a minimal watch so we can assert how stored fields
// (here, sortBy) round-trip through formStateFromWatch.
function watchForm(overrides: Partial<LinearIssueWatch>): FormState {
return formStateFromWatch({
id: "w-1",
workspaceId: "ws-1",
workflowId: "wf-1",
workflowStepId: "step-1",
repositoryId: "",
baseBranch: "",
filter: {},
agentProfileId: "",
executorProfileId: "",
prompt: "do the thing",
enabled: true,
pollIntervalSeconds: 300,
createdAt: "",
updatedAt: "",
...overrides,
});
}
describe("isWatchFormReady", () => {
it("is true for a complete form with a valid cap", () => {
expect(isWatchFormReady(readyForm({ maxInflightTasks: "5" }))).toBe(true);
});
it("is true when the cap is blank (uncapped)", () => {
expect(isWatchFormReady(readyForm({ maxInflightTasks: "" }))).toBe(true);
});
it("is false when the cap is invalid", () => {
expect(isWatchFormReady(readyForm({ maxInflightTasks: "0" }))).toBe(false);
});
it("is false when the filter is empty", () => {
expect(isWatchFormReady(readyForm({ teamKey: "", maxInflightTasks: "5" }))).toBe(false);
});
});
describe("buildWatchPayload", () => {
it("maps a blank cap to null", () => {
const payload = buildWatchPayload(readyForm({ maxInflightTasks: "" }));
expect(payload).not.toBeNull();
expect(payload?.maxInflightTasks).toBeNull();
});
it("maps a positive cap to the integer", () => {
const payload = buildWatchPayload(readyForm({ maxInflightTasks: "7" }));
expect(payload?.maxInflightTasks).toBe(7);
});
it("returns null when the cap is invalid", () => {
expect(buildWatchPayload(readyForm({ maxInflightTasks: "-1" }))).toBeNull();
});
it("carries the form's sortBy through to the payload", () => {
const payload = buildWatchPayload(readyForm({ sortBy: "created_desc" }));
expect(payload?.sortBy).toBe("created_desc");
});
});
describe("sortBy form mapping", () => {
it("defaults new watches to most-important-first", () => {
expect(makeEmptyForm("ws-1").sortBy).toBe("priority");
});
it("maps a stored sortBy through from the watch", () => {
expect(watchForm({ sortBy: "updated_asc" }).sortBy).toBe("updated_asc");
});
it("falls back to the empty default for a legacy watch with no sortBy", () => {
expect(watchForm({}).sortBy).toBe("");
});
});