133 lines
4.1 KiB
TypeScript
133 lines
4.1 KiB
TypeScript
import { describe, expect, it } from "vitest";
|
|
import { MAX_TASK_TITLE_LENGTH } from "@/model/taskFile";
|
|
import { validateFrontmatter } from "./validateFrontmatter";
|
|
|
|
const valid = `---
|
|
id: "550e8400-e29b-41d4-a716-446655440000"
|
|
title: Fix login
|
|
status: todo
|
|
priority: high
|
|
tags: [bug]
|
|
assignee: ""
|
|
created: 2026-07-12T10:00:00.000Z
|
|
updated: 2026-07-12T10:00:00.000Z
|
|
---
|
|
|
|
Body
|
|
`;
|
|
|
|
describe("validateFrontmatter", () => {
|
|
it("accepts a valid task file", () => {
|
|
expect(validateFrontmatter(valid)).toEqual([]);
|
|
});
|
|
|
|
it("errors when frontmatter is missing", () => {
|
|
const issues = validateFrontmatter("no frontmatter\n");
|
|
expect(issues).toHaveLength(1);
|
|
expect(issues[0].severity).toBe("error");
|
|
expect(issues[0].message).toMatch(/frontmatter/i);
|
|
});
|
|
|
|
it("errors when frontmatter is unclosed", () => {
|
|
const issues = validateFrontmatter("---\ntitle: x\n");
|
|
expect(issues.some((i) => /not closed/i.test(i.message))).toBe(true);
|
|
});
|
|
|
|
it("errors on invalid status", () => {
|
|
const content = valid.replace("status: todo", "status: nope");
|
|
const issues = validateFrontmatter(content);
|
|
const statusIssue = issues.find((i) => i.field === "status");
|
|
expect(statusIssue).toBeDefined();
|
|
expect(statusIssue!.severity).toBe("error");
|
|
expect(statusIssue!.range.startLine).toBeGreaterThan(0);
|
|
});
|
|
|
|
it("errors on invalid priority", () => {
|
|
const content = valid.replace("priority: high", "priority: urgent");
|
|
const issues = validateFrontmatter(content);
|
|
expect(issues.some((i) => i.field === "priority")).toBe(true);
|
|
});
|
|
|
|
it("errors when id is missing", () => {
|
|
const content = valid.replace(
|
|
'id: "550e8400-e29b-41d4-a716-446655440000"\n',
|
|
"",
|
|
);
|
|
const issues = validateFrontmatter(content);
|
|
expect(
|
|
issues.some((i) => i.field === "id" && /missing/i.test(i.message)),
|
|
).toBe(true);
|
|
});
|
|
|
|
it("errors when title exceeds max length", () => {
|
|
const long = "x".repeat(MAX_TASK_TITLE_LENGTH + 1);
|
|
const content = valid.replace("title: Fix login", `title: ${long}`);
|
|
const issues = validateFrontmatter(content);
|
|
const titleIssue = issues.find((i) => i.field === "title");
|
|
expect(titleIssue).toBeDefined();
|
|
expect(titleIssue!.message).toMatch(String(MAX_TASK_TITLE_LENGTH));
|
|
});
|
|
|
|
it("errors when tags is not an array", () => {
|
|
const content = valid.replace("tags: [bug]", "tags: bug");
|
|
const issues = validateFrontmatter(content);
|
|
expect(issues.some((i) => i.field === "tags")).toBe(true);
|
|
});
|
|
|
|
it("warns on unknown fields", () => {
|
|
const content = valid.replace(
|
|
"updated: 2026-07-12T10:00:00.000Z",
|
|
"updated: 2026-07-12T10:00:00.000Z\nfoo: bar",
|
|
);
|
|
const issues = validateFrontmatter(content);
|
|
const unknown = issues.find((i) => i.field === "foo");
|
|
expect(unknown).toBeDefined();
|
|
expect(unknown!.severity).toBe("warning");
|
|
});
|
|
|
|
it("errors on broken YAML", () => {
|
|
const content = `---
|
|
title: [unterminated
|
|
---
|
|
`;
|
|
const issues = validateFrontmatter(content);
|
|
expect(issues.some((i) => /yaml/i.test(i.message))).toBe(true);
|
|
});
|
|
|
|
it("does not warn about unknown tags when knownTags is omitted", () => {
|
|
const issues = validateFrontmatter(valid);
|
|
expect(issues.filter((i) => i.field === "tags")).toEqual([]);
|
|
});
|
|
|
|
it("warns when a tag is not in knownTags", () => {
|
|
const content = valid.replace("tags: [bug]", "tags: [bug, nope]");
|
|
const issues = validateFrontmatter(content, { knownTags: ["bug"] });
|
|
const unknown = issues.filter(
|
|
(i) => i.field === "tags" && i.severity === "warning",
|
|
);
|
|
expect(unknown).toHaveLength(1);
|
|
expect(unknown[0].message).toMatch(/nope/i);
|
|
expect(unknown[0].code).toBe("unknown-tag");
|
|
expect(unknown[0].tag).toBe("nope");
|
|
});
|
|
|
|
it("does not warn when all tags are known", () => {
|
|
const issues = validateFrontmatter(valid, {
|
|
knownTags: ["bug", "frontend"],
|
|
});
|
|
expect(issues.filter((i) => i.severity === "warning")).toEqual([]);
|
|
});
|
|
|
|
it("warns for every tag when knownTags is empty array (config exists)", () => {
|
|
const issues = validateFrontmatter(valid, { knownTags: [] });
|
|
expect(
|
|
issues.some(
|
|
(i) =>
|
|
i.field === "tags" &&
|
|
i.severity === "warning" &&
|
|
/bug/i.test(i.message),
|
|
),
|
|
).toBe(true);
|
|
});
|
|
});
|