feat: browse archived habit history and restore habits

This commit is contained in:
syntaxbullet
2026-09-04 18:33:18 +02:00
parent 9c8a96ba03
commit 19a9aa6862
7 changed files with 94 additions and 17 deletions

View File

@@ -116,6 +116,24 @@ const buttonNamed = (name: string) => [...container.querySelectorAll<HTMLButtonE
.find(button => button.textContent === name || button.getAttribute("aria-label") === name)!;
describe("homepage", () => {
test("archived habits expose history and restore to the dashboard", async () => {
const f = connectAccount();
f.setTime("2026-09-03T12:00:00Z");
const h = await f.json("/habits", "POST", { name: "Reading", method: "manual" }, 201);
await f.json(`/habits/${h.id}/days/2026-09-03/progress`, "PUT", { done: true });
f.setTime("2026-09-04T12:00:00Z");
await f.request(`/habits/${h.id}`, "DELETE");
await render("/");
await act(async () => buttonNamed("Archived habits").click());
expect(container.textContent).toContain("Restore Reading");
await act(async () => buttonNamed("View history").click());
expect(container.querySelector('[aria-label="2026-09-03: 1 of 1 completion · Complete"]')).not.toBeNull();
await act(async () => buttonNamed("Restore Reading").click());
expect(container.textContent).toContain("No archived habits.");
expect((await f.json("/today")).habits[0].name).toBe("Reading");
expect((await f.json(`/habits/${h.id}/days/2026-09-03`)).complete).toBe(true);
});
test("historical check-ins use the selected date's tracking method and preserve today", async () => {
const f = connectAccount();
f.setTime("2026-09-03T12:00:00Z");
@@ -240,12 +258,12 @@ describe("homepage", () => {
expect(saved.target).toBe(10);
expect(container.querySelector("form")).toBeNull();
expect(container.querySelector("h3")?.textContent).toContain("Daily water");
await act(async () => buttonNamed("Delete habit Daily water").click());
await act(async () => buttonNamed("Archive habit Daily water").click());
expect((await f.json("/habits")).habits).toHaveLength(1);
await act(async () => buttonNamed("Cancel").click());
expect((await f.json("/habits")).habits).toHaveLength(1);
await act(async () => buttonNamed("Delete habit Daily water").click());
await act(async () => buttonNamed("Delete habit").click());
await act(async () => buttonNamed("Archive habit Daily water").click());
await act(async () => buttonNamed("Archive habit").click());
expect((await f.json("/habits")).habits).toHaveLength(0);
expect(container.querySelector(".ds-habit-chart")).toBeNull();
const previous = await f.json(`/habits/${habit.id}/days/2026-09-03`);
@@ -398,15 +416,15 @@ describe("homepage", () => {
const f = connectAccount();
const habit = await f.json("/habits", "POST", { name: "Reading", method: "manual" }, 201);
await render("/");
await act(async () => buttonNamed("Delete habit Reading").click());
await act(async () => buttonNamed("Archive habit Reading").click());
fetchMock.mockImplementationOnce((async (input, init) => {
return f.request(String(input).replace("/api", ""), init?.method);
}) as typeof fetch);
fetchMock.mockResolvedValueOnce(new Response(null, { status: 500 }));
await act(async () => buttonNamed("Delete habit").click());
await act(async () => buttonNamed("Archive habit").click());
expect(container.querySelector("form")).toBeNull();
expect(container.textContent).toContain("Your change was saved, but the dashboard could not refresh");
expect(buttonNamed("Delete habit Reading").disabled).toBe(true);
expect(buttonNamed("Archive habit Reading").disabled).toBe(true);
expect((await f.json(`/habits/${habit.id}`)).archived).toBe(true);
await act(async () => buttonNamed("Try again").click());
expect(container.querySelector(".ds-habit-chart")).toBeNull();