feat: enhance habit tracking with pending state management and history refresh
This commit is contained in:
@@ -282,7 +282,7 @@ describe("homepage", () => {
|
||||
await act(async () => container.querySelector("form")!.dispatchEvent(new dom.Event("submit", { bubbles: true, cancelable: true }) as unknown as Event));
|
||||
expect((await f.json("/habits")).habits[0].name).toBe("Read a little");
|
||||
expect(container.querySelector("form")).toBeNull();
|
||||
expect(container.textContent).toContain("Read a little created.");
|
||||
expect(container.textContent).not.toContain("Read a little created.");
|
||||
expect(container.querySelector(".ds-habit-chart")).not.toBeNull();
|
||||
await act(async () => buttonNamed("Sign out").click());
|
||||
expect(container.querySelector("#welcome-title")).not.toBeNull();
|
||||
@@ -326,6 +326,39 @@ describe("homepage", () => {
|
||||
expect((await f.json("/today")).completed).toBe(2);
|
||||
});
|
||||
|
||||
test("a pending counter save leaves other habits usable and refreshes only its history", async () => {
|
||||
const f = connectAccount();
|
||||
const water = await f.json("/habits", "POST", { name: "Water", method: "count", target: 8, unit: "glasses" }, 201);
|
||||
const reading = await f.json("/habits", "POST", { name: "Reading", method: "manual" }, 201);
|
||||
await render("/");
|
||||
const calendar = container.querySelector(`#history-${reading.id} .ds-calendar`);
|
||||
const originalFetch = fetchMock.getMockImplementation()!;
|
||||
let finish!: () => void;
|
||||
const pending = new Promise<void>(resolve => { finish = resolve; });
|
||||
fetchMock.mockImplementation(Object.assign(async (input: Parameters<typeof fetch>[0], init?: RequestInit) => {
|
||||
if (init?.method === "PUT" && String(input).includes(`/habits/${water.id}/`)) await pending;
|
||||
return originalFetch(input, init);
|
||||
}, { preconnect: fetch.preconnect }));
|
||||
fetchMock.mockClear();
|
||||
await act(async () => buttonNamed("Increase Water").click());
|
||||
expect(buttonNamed("Increase Water").disabled).toBe(true);
|
||||
const check = container.querySelector<HTMLInputElement>('[aria-label="Reading done"]')!;
|
||||
expect(check.disabled).toBe(false);
|
||||
await act(async () => check.click());
|
||||
expect(check.checked).toBe(true);
|
||||
expect(buttonNamed("Increase Water").disabled).toBe(true);
|
||||
expect((await f.json("/today")).completed).toBe(1);
|
||||
fetchMock.mockClear();
|
||||
await act(async () => { finish(); await pending; });
|
||||
expect(buttonNamed("Increase Water").disabled).toBe(false);
|
||||
expect(container.querySelector<HTMLInputElement>('[aria-label="Total Water"]')!.value).toBe("1");
|
||||
expect(container.querySelector(`#history-${reading.id} .ds-calendar`)).toBe(calendar);
|
||||
const historyRequests = fetchMock.mock.calls.map(([url]) => String(url)).filter(url => url.includes("/calendar?"));
|
||||
expect(historyRequests).toHaveLength(1);
|
||||
expect(historyRequests[0]).toContain(`/habits/${water.id}/calendar?`);
|
||||
expect((await f.json("/today")).completed).toBe(1);
|
||||
});
|
||||
|
||||
test("inline habit edits persist configuration and delete archives without losing earlier history", async () => {
|
||||
const f = connectAccount();
|
||||
f.setTime("2026-09-03T12:00:00Z");
|
||||
|
||||
Reference in New Issue
Block a user