tests: more tests for remaining parser, queries, and update logic

This commit is contained in:
Jacobtread 2026-04-14 20:09:43 +12:00
parent 632bd67a4f
commit aaa93c3371
14 changed files with 506 additions and 4 deletions

View file

@ -0,0 +1,32 @@
import moment from "moment";
import { TimeEntry } from "@/timekeep/schema";
export const currentTime = moment();
export const targetEntryId = "9054dee3-8c15-493b-ad31-f070e08c2699";
export const input: TimeEntry[] = [
{
id: "7054dee3-8c15-493b-ad31-f070e08c2699",
name: "Part 1",
startTime: null,
endTime: null,
subEntries: [
{
id: "7054dee3-8c15-493b-ad31-f070e08c2699",
name: "Part 1",
startTime: currentTime,
endTime: currentTime,
subEntries: null,
},
{
id: "7054dee3-8c15-493b-ad31-f070e08c269a",
name: "Part 2",
startTime: null,
endTime: null,
subEntries: [],
},
],
},
];

View file

@ -0,0 +1,49 @@
import moment from "moment";
import { TimeEntry } from "@/timekeep/schema";
export const currentTime = moment();
export const targetEntryId = "9054dee3-8c15-493b-ad31-f070e08c2699";
export const targetEntry: TimeEntry = {
id: "9054dee3-8c15-493b-ad31-f070e08c2699",
name: "Running Entry",
startTime: currentTime,
endTime: null,
subEntries: null,
};
export const input: TimeEntry[] = [
{
id: "8054dee3-8c15-493b-ad31-f070e08c2699",
name: "Block 1",
startTime: null,
endTime: null,
subEntries: [
{
id: "7054dee3-8c15-493b-ad31-f070e08c2699",
name: "Part 1",
startTime: currentTime,
endTime: currentTime,
subEntries: null,
},
{
id: "7054dee3-8c15-493b-ad31-f070e08c269a",
name: "Part 2",
startTime: null,
endTime: null,
subEntries: [
{
id: "dcd7dca3-2471-4855-a49b-397e388c3204",
name: "Part 1",
startTime: currentTime,
endTime: currentTime,
subEntries: null,
},
targetEntry,
],
},
],
},
];

View file

@ -0,0 +1,47 @@
import moment from "moment";
import { createCodeBlock } from "@/utils/codeblock";
import { Timekeep } from "@/timekeep/schema";
// Input data to find
export const input1 = createCodeBlock(
`{"entries":[{"name":"Block 1","startTime":"2024-03-17T01:33:51.630Z","endTime":"2024-03-17T01:33:55.151Z","subEntries":null}]}`,
4,
4
);
// Input data to find
export const input2 = createCodeBlock(
`{"entries":[{"name":"Block 2","startTime":"2024-03-17T01:33:51.630Z","endTime":"2024-03-17T01:33:55.151Z","subEntries":null}]}`,
4,
4
);
// Timekeep with a renamed block
export const inputTimekeep1: Timekeep = {
entries: [
{
id: "9054dee3-8c15-493b-ad31-f070e08c2699",
name: "Block 1",
startTime: moment("2024-03-17T01:33:51.630Z"),
endTime: moment("2024-03-17T01:33:55.151Z"),
subEntries: null,
},
],
};
// Timekeep with a renamed block
export const inputTimekeep2: Timekeep = {
entries: [
{
id: "9054dee3-8c15-493b-ad31-f070e08c2699",
name: "Block 2",
startTime: moment("2024-03-17T01:33:51.630Z"),
endTime: moment("2024-03-17T01:33:55.151Z"),
subEntries: null,
},
],
};
export const text = "\n\n\n" + input1 + "\n\n\n\n" + input2;

View file

@ -0,0 +1,6 @@
import { createCodeBlock } from "@/utils/codeblock";
// Input data to find
export const input1 = createCodeBlock(`{"invalid_json}]}`, 4, 4);
export const text = "\n\n\n" + input1 + "\n\n\n\n";

View file

@ -0,0 +1,27 @@
import moment from "moment";
import { createCodeBlock } from "@/utils/codeblock";
import { Timekeep } from "@/timekeep/schema";
// Input data to find
export const input1 = createCodeBlock(
`{"entries":[{"name":"Block 1","startTime":"2024-03-17T01:33:51.630Z","endTime":"2024-03-17T01:33:55.151Z","subEntries":null}]}`,
4,
4
);
// Timekeep with a renamed block
export const inputTimekeep1: Timekeep = {
entries: [
{
id: "9054dee3-8c15-493b-ad31-f070e08c2699",
name: "Block 1",
startTime: moment("2024-03-17T01:33:51.630Z"),
endTime: moment("2024-03-17T01:33:55.151Z"),
subEntries: null,
},
],
};
export const text = "\n\n\n" + input1 + "\n\n\n\n```timekeep\n\n";

View file

@ -19,6 +19,44 @@ export const entries: TimeEntry[] = [
endTime: currentTime,
subEntries: null,
},
{
id: "1f683adc-0205-4a18-9b1b-ada64a4b0553",
name: "Block Should be removed",
startTime: null,
endTime: null,
subEntries: [],
},
{
id: "36c3a34a-208c-4975-bae7-ff833e8017ac",
name: "Block 4",
startTime: null,
endTime: null,
subEntries: [
{
id: "cbfb3b78-dd7f-4ee2-99ea-34d6b0626e23",
name: "Block 5",
startTime: currentTime,
endTime: currentTime,
subEntries: null,
},
{
id: "35ff21e3-af83-45b6-86a2-7e141a12c37f",
name: "Block 12",
startTime: currentTime,
endTime: currentTime,
subEntries: null,
},
],
},
{
id: "a9bda6f2-568c-4d11-a3be-e8586bde8b1c",
name: "Block 6",
startTime: null,
endTime: null,
subEntries: [],
folder: true,
},
entryToRemove,
{
id: "b8fbcb98-f1e9-4d80-8867-994f58191046",
@ -37,6 +75,36 @@ export const expectedEntries: TimeEntry[] = [
endTime: currentTime,
subEntries: null,
},
{
id: "36c3a34a-208c-4975-bae7-ff833e8017ac",
name: "Block 4",
startTime: null,
endTime: null,
subEntries: [
{
id: "cbfb3b78-dd7f-4ee2-99ea-34d6b0626e23",
name: "Block 5",
startTime: currentTime,
endTime: currentTime,
subEntries: null,
},
{
id: "35ff21e3-af83-45b6-86a2-7e141a12c37f",
name: "Block 12",
startTime: currentTime,
endTime: currentTime,
subEntries: null,
},
],
},
{
id: "a9bda6f2-568c-4d11-a3be-e8586bde8b1c",
name: "Block 6",
startTime: null,
endTime: null,
subEntries: [],
folder: true,
},
{
id: "b8fbcb98-f1e9-4d80-8867-994f58191046",
name: "Block 2",

View file

@ -0,0 +1,27 @@
import moment from "moment";
import { TimeEntry } from "@/timekeep/schema";
export const currentTime = moment();
export const targetEntry: TimeEntry = {
id: "9054dee3-8c15-493b-ad31-f070e08c2699",
name: "Part 1",
startTime: currentTime,
endTime: currentTime,
subEntries: null,
};
export const entries = [
{
id: "7054dee3-8c15-493b-ad31-f070e08c2699",
name: "Part 1",
startTime: null,
endTime: null,
subEntries: [targetEntry],
},
];
export const expected = [
{ id: "7054dee3-8c15-493b-ad31-f070e08c2699", name: "Part 1" },
{ id: "9054dee3-8c15-493b-ad31-f070e08c2699", name: "Part 1" },
];

View file

@ -0,0 +1,52 @@
import moment from "moment";
import { TimeEntry } from "@/timekeep/schema";
export const currentTime = moment();
export const targetEntry: TimeEntry = {
id: "9054dee3-8c15-493b-ad31-f070e08c2699",
name: "Part 1",
startTime: currentTime,
endTime: currentTime,
subEntries: null,
};
export const entries = [
{
id: "7054dee3-8c15-493b-ad31-f070e08c2699",
name: "Part 1",
startTime: null,
endTime: null,
subEntries: [
{
id: "7054dee3-8c15-493b-ad31-f070e08c2699",
name: "Part 1",
startTime: currentTime,
endTime: currentTime,
subEntries: null,
},
{
id: "7054dee3-8c15-493b-ad31-f070e08c269a",
name: "Part 2",
startTime: null,
endTime: null,
subEntries: [targetEntry],
},
],
},
];
export const expected = [
{
id: "7054dee3-8c15-493b-ad31-f070e08c2699",
name: "Part 1",
},
{
id: "7054dee3-8c15-493b-ad31-f070e08c269a",
name: "Part 2",
},
{
id: "9054dee3-8c15-493b-ad31-f070e08c2699",
name: "Part 1",
},
];

View file

@ -0,0 +1,39 @@
import moment from "moment";
import { TimeEntry } from "@/timekeep/schema";
export const currentTime = moment();
export const targetEntry: TimeEntry = {
id: "9054dee3-8c15-493b-ad31-f070e08c2699",
name: "Part 1",
startTime: currentTime,
endTime: currentTime,
subEntries: null,
};
export const entries = [
{
id: "7054dee3-8c15-493b-ad31-f070e08c2699",
name: "Part 1",
startTime: null,
endTime: null,
subEntries: [
{
id: "7054dee3-8c15-493b-ad31-f070e08c2699",
name: "Part 1",
startTime: currentTime,
endTime: currentTime,
subEntries: null,
},
{
id: "7054dee3-8c15-493b-ad31-f070e08c269a",
name: "Part 2",
startTime: null,
endTime: null,
subEntries: [],
},
],
},
];
export const expected = [];

View file

@ -0,0 +1,21 @@
import moment from "moment";
import { TimeEntry } from "@/timekeep/schema";
export const currentTime = moment();
export const targetEntry: TimeEntry = {
id: "9054dee3-8c15-493b-ad31-f070e08c2699",
name: "Part 1",
startTime: currentTime,
endTime: currentTime,
subEntries: null,
};
export const entries = [targetEntry];
export const expected = [
{
id: "9054dee3-8c15-493b-ad31-f070e08c2699",
name: "Part 1",
},
];

View file

@ -0,0 +1,42 @@
import moment from "moment";
export const currentTime = moment();
export const entry = {
id: "9054dee3-8c15-493b-ad31-f070e08c2699",
name: "Part 1",
startTime: null,
endTime: null,
subEntries: [
{
id: "d7afcb74-a6f2-4cb7-80ed-48a2f71aa7a6",
name: "Part 1",
startTime: currentTime.add(1, "hours"),
endTime: currentTime.add(1, "hours"),
subEntries: null,
},
{
id: "da783535-08d3-4f65-97f3-e331273da934",
name: "Part 1",
startTime: currentTime,
endTime: currentTime,
subEntries: null,
},
{
id: "5c305879-61ba-4bc9-8373-568948abf27f",
name: "Part 1",
startTime: null,
endTime: null,
subEntries: null,
},
{
id: "8d491c4c-2441-4f87-bd29-4758b8108500",
name: "Part 1",
startTime: currentTime.add(2, "hours"),
endTime: currentTime.add(2, "hours"),
subEntries: null,
},
],
};
export const output = currentTime;

View file

@ -9,6 +9,7 @@ import {
LoadSuccess,
replaceTimekeepCodeblock,
extractTimekeepCodeblocks,
extractTimekeepCodeblocksWithPosition,
} from "./parser";
import { Timekeep, stripTimekeepRuntimeData } from "./schema";
@ -40,6 +41,50 @@ describe("extracting code blocks", () => {
});
});
describe("extracting code blocks with position", () => {
it("should extract codeblock contents", async () => {
const { text, inputTimekeep1, inputTimekeep2 } =
await import("./__fixtures__/extracting/codeblockContentsPosition");
const output = extractTimekeepCodeblocksWithPosition(text);
expect(stripTimekeepRuntimeData(output[0].timekeep)).toStrictEqual(
stripTimekeepRuntimeData(inputTimekeep1)
);
expect(output[0].startLine).toEqual(7);
expect(output[0].endLine).toEqual(9);
expect(stripTimekeepRuntimeData(output[1].timekeep)).toStrictEqual(
stripTimekeepRuntimeData(inputTimekeep2)
);
expect(output[1].startLine).toEqual(21);
expect(output[1].endLine).toEqual(23);
expect(output.length).toBe(2);
});
it("should ignore codeblocks that are not closed", async () => {
const { text, inputTimekeep1 } =
await import("./__fixtures__/extracting/unclosedCodeBlockPosition");
const output = extractTimekeepCodeblocksWithPosition(text);
expect(stripTimekeepRuntimeData(output[0].timekeep)).toStrictEqual(
stripTimekeepRuntimeData(inputTimekeep1)
);
expect(output[0].startLine).toEqual(7);
expect(output[0].endLine).toEqual(9);
expect(output.length).toBe(1);
});
it("should ignore codeblocks that are not closed", async () => {
const consoleError = vi.spyOn(console, "error").mockImplementation(() => {});
const { text } = await import("./__fixtures__/extracting/invalidCodeBlockContentPosition");
const output = extractTimekeepCodeblocksWithPosition(text);
expect(output.length).toBe(0);
expect(consoleError).toHaveBeenCalled();
});
});
describe("replacing content", () => {
it("should replace codeblock contents", () => {
const lineStart = 4; // Line the codeblock should start on

View file

@ -9,6 +9,7 @@ import {
getEntryDuration,
getTotalDuration,
getEntriesNames,
getStartTime,
} from "./queries";
import { TimeEntry } from "./schema";
@ -29,6 +30,14 @@ describe("getEntryById", () => {
expect(output).toEqual(targetEntry);
});
it("find nested entry second element", async () => {
const { input, targetEntry, targetEntryId } =
await import("./__fixtures__/checking/findEntryByIdNestedSecond");
const output = getEntryById(targetEntryId, input);
expect(output).toEqual(targetEntry);
});
it("find entry non existent", async () => {
const { input, targetEntryId } =
await import("./__fixtures__/checking/findEntryByIdMissing");
@ -36,6 +45,14 @@ describe("getEntryById", () => {
const output = getEntryById(targetEntryId, input);
expect(output).toBeUndefined();
});
it("find entry non existent nested", async () => {
const { input, targetEntryId } =
await import("./__fixtures__/checking/findEntryByIdMissingNested");
const output = getEntryById(targetEntryId, input);
expect(output).toBeUndefined();
});
});
describe("getPathToEntry", () => {
@ -45,9 +62,31 @@ describe("getPathToEntry", () => {
expect(output).toEqual(expected);
});
it("top level path found", () => {});
it("top level path found", async () => {
const { targetEntry, entries, expected } = await import("./__fixtures__/path/pathTopLevel");
const output = getPathToEntry(entries, targetEntry);
expect(output).toEqual(expected);
});
it("deep path found", () => {});
it("child path found", async () => {
const { targetEntry, entries, expected } = await import("./__fixtures__/path/pathChild");
const output = getPathToEntry(entries, targetEntry);
expect(output).toEqual(expected);
});
it("deep child path found", async () => {
const { targetEntry, entries, expected } =
await import("./__fixtures__/path/pathDeepChild");
const output = getPathToEntry(entries, targetEntry);
expect(output).toEqual(expected);
});
it("deep child path not found", async () => {
const { targetEntry, entries, expected } =
await import("./__fixtures__/path/pathNotFoundDeep");
const output = getPathToEntry(entries, targetEntry);
expect(output).toEqual(expected);
});
it("should find running entry path", async () => {
const { input, runningEntry, path } =
@ -203,3 +242,11 @@ describe("getEntriesNames", () => {
expect(outputSet).toEqual(expected);
});
});
describe("getStartTime", () => {
it("should pick the earliest start time", async () => {
const { entry, output } = await import("./__fixtures__/startTime/earlyStartTime");
expect(getStartTime(entry, false)).toEqual(output);
});
});

View file

@ -170,8 +170,8 @@ function makeEntrySingle(target: TimeEntry): TimeEntry {
return target;
}
// Don't collapse if more than 1 entry
if (target.subEntries.length > 1) {
// Don't collapse if more than 1 entry or no entries at all
if (target.subEntries.length != 1) {
return target;
}