feat: add vitest, vite, move to tdd

This commit is contained in:
2026-07-13 07:07:44 +05:00
parent 4704a1d5a0
commit 62862beb06
18 changed files with 2043 additions and 346 deletions
+81
View File
@@ -0,0 +1,81 @@
import { describe, it, expect } from "vitest";
import { parseTaskFile, serializeTask } from "./markdown";
import { Task } from "@/model/task";
const fullTask: Task = {
id: "550e8400-e29b-41d4-a716-446655440000",
title: "Пофиксить баг логина",
description: "Ошибка валидации токена",
status: "in-progress",
priority: "high",
tags: ["bug", "frontend"],
assignee: "ivanov",
created: "2026-07-12T10:00:00.000Z",
updated: "2026-07-12T10:00:00.000Z",
};
describe("markdown", () => {
it("parses a .task.md file with full frontmatter", () => {
const content = [
"---",
'id: "550e8400-e29b-41d4-a716-446655440000"',
'title: "Пофиксить баг логина"',
"status: in-progress",
"priority: high",
"tags: [bug, frontend]",
'assignee: "ivanov"',
"created: 2026-07-12T10:00:00.000Z",
"updated: 2026-07-12T10:00:00.000Z",
"---",
"Ошибка валидации токена",
].join("\n");
const task = parseTaskFile(content);
expect(task).toEqual(fullTask);
});
it("fills defaults for missing fields", () => {
const content = [
"---",
'id: "abc"',
'title: "Minimal"',
"---",
"",
].join("\n");
const task = parseTaskFile(content);
expect(task.id).toBe("abc");
expect(task.title).toBe("Minimal");
expect(task.description).toBe("");
expect(task.status).toBe("todo");
expect(task.priority).toBe("medium");
expect(task.tags).toEqual([]);
expect(task.assignee).toBe("");
expect(task.created).toBeTruthy();
expect(task.updated).toBeTruthy();
});
it("serializeTask produces valid frontmatter", () => {
const result = serializeTask(fullTask);
expect(result).toContain("---");
expect(result).toContain("title: Пофиксить баг логина");
expect(result).toContain("status: in-progress");
expect(result).toContain("Ошибка валидации токена");
});
it("round-trip: serialize → parse produces the same task", () => {
const serialized = serializeTask(fullTask);
const parsed = parseTaskFile(serialized);
expect(parsed).toEqual(fullTask);
});
it("description with utf-8 survives round-trip", () => {
const task: Task = {
...fullTask,
description: "Тестовая задача с кириллицей\nи переносом строки",
};
const serialized = serializeTask(task);
const parsed = parseTaskFile(serialized);
expect(parsed.description).toBe(task.description);
});
});
+9 -3
View File
@@ -1,5 +1,11 @@
import matter from "gray-matter";
import { Task } from "../model/task";
import { Task } from "@/model/task";
function toISO(value: unknown): string {
if (value instanceof Date) return value.toISOString();
if (typeof value === "string") return value;
return new Date().toISOString();
}
export function parseTaskFile(content: string): Task {
const parsed = matter(content);
@@ -11,8 +17,8 @@ export function parseTaskFile(content: string): Task {
priority: parsed.data.priority ?? "medium",
tags: parsed.data.tags ?? [],
assignee: parsed.data.assignee ?? "",
created: parsed.data.created ?? new Date().toISOString(),
updated: parsed.data.updated ?? new Date().toISOString(),
created: toISO(parsed.data.created),
updated: toISO(parsed.data.updated),
};
}