test: tests files for needed tests, timesheetStatusBarItem tests

This commit is contained in:
Jacobtread 2026-04-04 14:25:18 +13:00
parent 8b04d70bd2
commit e8a7eb5cd7
24 changed files with 177 additions and 44 deletions

View file

@ -17,5 +17,8 @@ vi.mock("obsidian", () => {
Vault: MockVault,
Component: MockComponent,
Notice: MockNotice,
setIcon: vi.fn((containerEl: HTMLElement, icon: string) => {
containerEl.innerHTML += `<svg data-icon="${icon}"></svg>`;
}),
};
});

View file

@ -72,7 +72,7 @@ describe("DomComponent", () => {
comp.wrapperEl = wrapper;
container.appendChild(wrapper);
comp.onunload();
comp.unload();
expect(comp.unmounted).toBe(true); // only unload sets unmounted
expect(wrapper.parentElement).toBeNull();
@ -89,7 +89,7 @@ describe("DomComponent", () => {
child.wrapperEl = wrapper;
container.appendChild(wrapper);
child.onunload();
child.unload();
expect(child.unmounted).toBe(true);
expect(wrapper.parentElement).toBe(container);

View file

@ -7,12 +7,6 @@ import { createMockContainer } from "@/__mocks__/obsidian";
import { createObsidianIcon } from "./obsidianIcon";
vi.mock("obsidian", () => ({
setIcon: vi.fn((containerEl: HTMLElement, icon: string) => {
containerEl.innerHTML += `<svg data-icon="${icon}"></svg>`;
}),
}));
describe("createObsidianIcon", () => {
let container: HTMLElement;

View file

@ -0,0 +1,5 @@
// @vitest-environment happy-dom
import { describe } from "vitest";
describe("Timesheet", () => {});

View file

@ -0,0 +1,5 @@
// @vitest-environment happy-dom
import { describe } from "vitest";
describe("TimesheetApp", () => {});

View file

@ -35,7 +35,7 @@ describe("TimesheetCounters", () => {
});
it("should initialize wrapper and timers on load", () => {
component.onload();
component.load();
expect(component.wrapperEl).toBeInstanceOf(HTMLDivElement);
expect(component.wrapperEl?.className).toBe("timekeep-timers");
@ -47,13 +47,13 @@ describe("TimesheetCounters", () => {
it("should call updateTimers on load", () => {
const spy = vi.spyOn(component, "updateTimers");
component.onload();
component.load();
expect(spy).toHaveBeenCalled();
});
it("should set timer values using getEntryDuration and getTotalDuration", () => {
component.onload();
component.load();
const currentSetValues = vi.spyOn(component.currentTimer, "setValues");
const totalSetValues = vi.spyOn(component.totalTimer, "setValues");
@ -73,7 +73,7 @@ describe("TimesheetCounters", () => {
const registerSpy = vi.spyOn(component, "registerInterval");
component.onload();
component.load();
expect(component.currentContentInterval).toBeDefined();
expect(registerSpy).toHaveBeenCalledWith(component.currentContentInterval);
@ -87,14 +87,14 @@ describe("TimesheetCounters", () => {
const clearSpy = vi.spyOn(global, "clearInterval");
isKeepRunning.mockReturnValue(true);
component.onload();
component.load();
expect(clearSpy).toHaveBeenCalledWith(fakeIntervalID);
});
it("should show the current and total duration if theres a running entry", () => {
const start = moment();
const oneHourLater = moment().add(1, "hour");
const oneHourLater = moment(start).add(1, "hour");
vi.useFakeTimers();
vi.setSystemTime(oneHourLater.toDate());
@ -124,7 +124,7 @@ describe("TimesheetCounters", () => {
it("should show total duration as the sum of all entry durations", () => {
const start = moment();
const oneHourLater = moment().add(1, "hour");
const oneHourLater = moment(start).add(1, "hour");
vi.useFakeTimers();
vi.setSystemTime(oneHourLater.toDate());

View file

@ -0,0 +1,5 @@
// @vitest-environment happy-dom
import { describe } from "vitest";
describe("TimesheetExportActions", () => {});

View file

@ -0,0 +1,5 @@
// @vitest-environment happy-dom
import { describe } from "vitest";
describe("TimesheetFileEntry", () => {});

View file

@ -34,7 +34,7 @@ describe("TimesheetLoadError", () => {
expect(container.children.length).toBe(1);
component.onunload();
component.unload();
expect(container.children.length).toBe(0);
});
});

View file

@ -0,0 +1,5 @@
// @vitest-environment happy-dom
import { describe } from "vitest";
describe("TimesheetNameInput", () => {});

View file

@ -0,0 +1,5 @@
// @vitest-environment happy-dom
import { describe } from "vitest";
describe("TimesheetRowContainer", () => {});

View file

@ -0,0 +1,5 @@
// @vitest-environment happy-dom
import { describe } from "vitest";
describe("TimesheetRowContent", () => {});

View file

@ -14,7 +14,7 @@ import { formatTimestamp } from "@/utils/time";
import { DomComponent } from "./domComponent";
import { createObsidianIcon } from "./obsidianIcon";
import { TimekeepName } from "./timesheetName";
import { TimesheetRowDurationComponent } from "./timesheetRowDuration";
import { TimesheetRowDuration } from "./timesheetRowDuration";
/**
* Component for the contents of a timesheet row
@ -123,7 +123,7 @@ export class TimesheetRowContent extends DomComponent {
cls: ["timekeep-col", "timekeep-col--duration"],
});
const duration = new TimesheetRowDurationComponent(durationColEl, entry);
const duration = new TimesheetRowDuration(durationColEl, entry);
this.addChild(duration);

View file

@ -0,0 +1,5 @@
// @vitest-environment happy-dom
import { describe } from "vitest";
describe("TimesheetRowContentEditing", () => {});

View file

@ -7,12 +7,12 @@ import { createMockContainer } from "@/__mocks__/obsidian";
import * as queries from "@/timekeep/queries";
import * as timeUtils from "@/utils/time";
import { TimesheetRowDurationComponent } from "./timesheetRowDuration";
import { TimesheetRowDuration } from "./timesheetRowDuration";
describe("TimesheetRowDurationComponent", () => {
describe("TimesheetRowDuration", () => {
let container: HTMLElement;
let entry: any;
let component: TimesheetRowDurationComponent;
let component: TimesheetRowDuration;
beforeEach(() => {
container = createMockContainer();
@ -30,7 +30,7 @@ describe("TimesheetRowDurationComponent", () => {
// Mock setInterval / clearInterval
vi.useFakeTimers();
component = new TimesheetRowDurationComponent(container, entry);
component = new TimesheetRowDuration(container, entry);
});
afterEach(() => {
@ -38,7 +38,7 @@ describe("TimesheetRowDurationComponent", () => {
});
it("should create a wrapper span with the correct class", () => {
component.onload();
component.load();
expect(component.wrapperEl).toBeInstanceOf(HTMLSpanElement);
expect(component.wrapperEl?.className).toBe("timekeep-time");
@ -47,13 +47,13 @@ describe("TimesheetRowDurationComponent", () => {
it("should call updateTime on load", () => {
const spy = vi.spyOn(component, "updateTime");
component.onload();
component.load();
expect(spy).toHaveBeenCalled();
});
it("should set textContent based on getEntryDuration and formatDurationLong", () => {
component.onload();
component.load();
expect(queries.getEntryDuration).toHaveBeenCalledWith(entry, expect.any(moment));
expect(timeUtils.formatDurationLong).toHaveBeenCalledWith(42);
@ -64,7 +64,7 @@ describe("TimesheetRowDurationComponent", () => {
(queries.isEntryRunning as any).mockReturnValue(true);
const registerSpy = vi.spyOn(component, "registerInterval");
component.onload();
component.load();
expect(component.currentContentInterval).toBeDefined();
expect(registerSpy).toHaveBeenCalledWith(component.currentContentInterval);
@ -76,7 +76,7 @@ describe("TimesheetRowDurationComponent", () => {
const clearSpy = vi.spyOn(global, "clearInterval");
(queries.isEntryRunning as any).mockReturnValue(true);
component.onload();
component.load();
expect(clearSpy).toHaveBeenCalledWith(fakeInterval);
});
@ -84,7 +84,7 @@ describe("TimesheetRowDurationComponent", () => {
it("should not schedule interval if entry is not running", () => {
(queries.isEntryRunning as any).mockReturnValue(false);
const registerSpy = vi.spyOn(component, "registerInterval");
component.onload();
component.load();
expect(component.currentContentInterval).not.toBeDefined();
expect(registerSpy).not.toHaveBeenCalled();

View file

@ -10,7 +10,7 @@ import { DomComponent } from "./domComponent";
* Component for rendering the live-updating duration on
* an entry row
*/
export class TimesheetRowDurationComponent extends DomComponent {
export class TimesheetRowDuration extends DomComponent {
/** The entry this duration belongs to */
entry: TimeEntry;

View file

@ -34,7 +34,7 @@ describe("TimesheetSaveError", () => {
});
it("renders wrapper, error message, and buttons on load", () => {
component.onload();
component.load();
const wrapper = container.querySelector(".timekeep-container");
expect(wrapper).not.toBeNull();
@ -54,13 +54,13 @@ describe("TimesheetSaveError", () => {
});
it("calls handleSaveTimekeep on retry button click", () => {
component.onload();
component.load();
component.onRetrySave();
expect(handleSaveTimekeep).toHaveBeenCalledWith(timekeepStore.getState());
});
it("writes JSON to clipboard on copy button click", async () => {
component.onload();
component.load();
await component.onCopy();
@ -70,8 +70,8 @@ describe("TimesheetSaveError", () => {
});
it("removes wrapper on unload", () => {
component.onload();
component.onunload();
component.load();
component.unload();
const wrapper = container.querySelector(".timekeep-container");
expect(wrapper).toBeNull();
});

View file

@ -0,0 +1,5 @@
// @vitest-environment happy-dom
import { describe } from "vitest";
describe("TimesheetStart", () => {});

View file

@ -0,0 +1,5 @@
// @vitest-environment happy-dom
import { describe } from "vitest";
describe("TimesheetStartEditing", () => {});

View file

@ -0,0 +1,5 @@
// @vitest-environment happy-dom
import { describe } from "vitest";
describe("TimesheetStartRunning", () => {});

View file

@ -0,0 +1,76 @@
// @vitest-environment happy-dom
import moment from "moment";
import { v4 } from "uuid";
import { describe, beforeEach, it, expect, vi, afterEach } from "vitest";
import { createMockContainer } from "@/__mocks__/obsidian";
import { TimeEntry } from "@/timekeep/schema";
import { TimesheetStatusBarItem } from "./timesheetStatusBarItem";
describe("TimesheetStatusBarItem", () => {
const start = moment();
const oneHourLater = start.add(1, "hour");
const entry: TimeEntry = {
id: v4(),
name: "Test",
startTime: moment(start),
endTime: null,
subEntries: null,
};
let containerEl: HTMLElement;
let onOpen: VoidFunction;
let onStop: VoidFunction;
beforeEach(() => {
onOpen = vi.fn();
onStop = vi.fn();
vi.useFakeTimers();
vi.setSystemTime(oneHourLater.toDate());
containerEl = createMockContainer();
});
afterEach(() => {
vi.useRealTimers();
});
it("should initialize with the correct entry and callback functions", () => {
const statusBarItem = new TimesheetStatusBarItem(containerEl, entry, onOpen, onStop);
expect(statusBarItem.entry).toBe(entry);
expect(statusBarItem.onOpen).toBe(onOpen);
expect(statusBarItem.onStop).toBe(onStop);
});
it("should call onStop when the stop icon is clicked", () => {
const statusBarItem = new TimesheetStatusBarItem(containerEl, entry, onOpen, onStop);
statusBarItem.load();
// Simulate click on the stop icon
const stopIcon = containerEl.querySelector(".button-icon");
const event = new MouseEvent("click", { bubbles: true, cancelable: true });
if (stopIcon) {
stopIcon.dispatchEvent(event);
}
expect(onStop).toHaveBeenCalledTimes(1);
});
it("should call onOpen when the content area is clicked", () => {
const statusBarItem = new TimesheetStatusBarItem(containerEl, entry, onOpen, onStop);
statusBarItem.load();
// Simulate click on the content element
const contentEl = containerEl.querySelector(".timekeep-status-item__content");
const event = new MouseEvent("click", { bubbles: true, cancelable: true });
if (contentEl) {
contentEl.dispatchEvent(event);
}
expect(onOpen).toHaveBeenCalledTimes(1);
});
});

View file

@ -2,7 +2,7 @@ import { TimeEntry } from "@/timekeep/schema";
import { DomComponent } from "./domComponent";
import { createObsidianIcon } from "./obsidianIcon";
import { TimesheetRowDurationComponent } from "./timesheetRowDuration";
import { TimesheetRowDuration } from "./timesheetRowDuration";
export class TimesheetStatusBarItem extends DomComponent {
/** The entry this duration belongs to */
@ -12,7 +12,7 @@ export class TimesheetStatusBarItem extends DomComponent {
currentContentInterval: number | undefined;
/** Component for rendering the real time updating duration */
duration: TimesheetRowDurationComponent;
duration: TimesheetRowDuration;
/** Callback to open the file when the content is clicked */
onOpen: VoidFunction;
@ -44,7 +44,6 @@ export class TimesheetStatusBarItem extends DomComponent {
"timekeep-status-item__button",
"button-icon",
]);
this.registerDomEvent(stopIcon, "click", this.onStop);
stopIcon.title = "Stop Entry";
const contentEl = wrapperEl.createDiv({
@ -57,8 +56,9 @@ export class TimesheetStatusBarItem extends DomComponent {
text: entry.name + ":",
});
this.addChild(new TimesheetRowDurationComponent(contentEl, entry));
this.addChild(new TimesheetRowDuration(contentEl, entry));
this.registerDomEvent(stopIcon, "click", this.onStop);
this.registerDomEvent(contentEl, "click", this.onOpen);
}
}

View file

@ -0,0 +1,5 @@
// @vitest-environment happy-dom
import { describe } from "vitest";
describe("TimesheetTable", () => {});

View file

@ -16,7 +16,7 @@ describe("TimesheetTimer", () => {
});
it("should create timer elements on load", () => {
timer.onload();
timer.load();
const timerEl = container.children[0] as HTMLElement;
expect(timerEl).toBeDefined();
@ -36,16 +36,16 @@ describe("TimesheetTimer", () => {
});
it("should remove timer element on unload", () => {
timer.onload();
timer.load();
const timerEl = container.children[0] as HTMLElement;
expect(timerEl).toBeDefined();
timer.onunload();
timer.unload();
expect(timerEl.parentElement).toBeNull();
});
it("should hide or show the timer element", () => {
timer.onload();
timer.load();
const timerEl = container.children[0] as HTMLElement;
timer.setHidden(true);
@ -56,7 +56,7 @@ describe("TimesheetTimer", () => {
});
it("should set primary and secondary values correctly", () => {
timer.onload();
timer.load();
const timerEl = container.children[0] as HTMLElement;
const primary = timerEl.children[0] as HTMLDivElement;
const secondary = timerEl.children[1] as HTMLDivElement;
@ -73,7 +73,7 @@ describe("TimesheetTimer", () => {
});
it("secondary value should be hidden if it is empty", () => {
timer.onload();
timer.load();
const timerEl = container.children[0] as HTMLElement;
const secondary = timerEl.children[1] as HTMLDivElement;