fix: restore schedule interaction correctness
* fix: restore schedule interaction correctness * fix: use scheduled stock timing for historical doses
This commit is contained in:
@@ -196,8 +196,6 @@ describe("useAppContext", () => {
|
||||
setTakenDoses: vi.fn(),
|
||||
takenDoseTimestamps: new Map<string, number>(),
|
||||
dismissedDoses: new Set<string>(),
|
||||
showClearMissedConfirm: true,
|
||||
setShowClearMissedConfirm: vi.fn(),
|
||||
clearDosesState: vi.fn(),
|
||||
getDoseId: vi.fn((base: string, person: string | null) => (person ? `${base}-${person}` : base)),
|
||||
isDoseTakenAutomatically: vi.fn(() => false),
|
||||
@@ -376,38 +374,6 @@ describe("useAppContext", () => {
|
||||
expect(window.history.back).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("dismisses missed doses and posts unique medication IDs", async () => {
|
||||
const { result } = renderHook(() => useAppContext(), { wrapper });
|
||||
|
||||
await act(async () => {
|
||||
await result.current.dismissMissedDoses(["11-0-1730000000000", "11-2-1730000100000", "12-0-1730000200000"]);
|
||||
});
|
||||
|
||||
expect(fetch).toHaveBeenCalledWith(
|
||||
"/api/medications/dismiss-until",
|
||||
expect.objectContaining({
|
||||
method: "POST",
|
||||
credentials: "include",
|
||||
})
|
||||
);
|
||||
|
||||
const body = JSON.parse((fetch as ReturnType<typeof vi.fn>).mock.calls[0][1].body as string);
|
||||
expect(body.medicationIds).toEqual([11, 12]);
|
||||
expect(mockUseMedications().loadMeds).toHaveBeenCalled();
|
||||
expect(mockUseDoses().setShowClearMissedConfirm).toHaveBeenCalledWith(false);
|
||||
});
|
||||
|
||||
it("does not dismiss missed doses for empty/invalid IDs", async () => {
|
||||
const { result } = renderHook(() => useAppContext(), { wrapper });
|
||||
|
||||
await act(async () => {
|
||||
await result.current.dismissMissedDoses([]);
|
||||
await result.current.dismissMissedDoses(["invalid-dose-id"]);
|
||||
});
|
||||
|
||||
expect(fetch).not.toHaveBeenCalledWith("/api/medications/dismiss-until", expect.anything());
|
||||
});
|
||||
|
||||
it("imports data and triggers reload plus import result state", async () => {
|
||||
const { result } = renderHook(() => useAppContext(), { wrapper });
|
||||
|
||||
@@ -583,15 +549,4 @@ describe("useAppContext", () => {
|
||||
|
||||
expect(mockAlert).toHaveBeenCalledWith("exportImport.importError: Import failed");
|
||||
});
|
||||
|
||||
it("keeps clear-missed confirm open when dismiss request fails", async () => {
|
||||
(global.fetch as ReturnType<typeof vi.fn>).mockRejectedValueOnce(new Error("network"));
|
||||
const { result } = renderHook(() => useAppContext(), { wrapper });
|
||||
|
||||
await act(async () => {
|
||||
await result.current.dismissMissedDoses(["11-0-1730000000000"]);
|
||||
});
|
||||
|
||||
expect(mockUseDoses().setShowClearMissedConfirm).not.toHaveBeenCalledWith(false);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -20,7 +20,6 @@ describe("useDoses", () => {
|
||||
|
||||
expect(result.current.takenDoses.size).toBe(0);
|
||||
expect(result.current.dismissedDoses.size).toBe(0);
|
||||
expect(result.current.showClearMissedConfirm).toBe(false);
|
||||
});
|
||||
|
||||
it("loads taken doses from API on mount", async () => {
|
||||
@@ -273,14 +272,32 @@ describe("useDoses", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("setShowClearMissedConfirm works", () => {
|
||||
it("shows an out-of-stock alert and reverts the optimistic mark", async () => {
|
||||
const alertMock = vi.fn();
|
||||
global.alert = alertMock;
|
||||
|
||||
(global.fetch as ReturnType<typeof vi.fn>)
|
||||
.mockResolvedValueOnce({ ok: true, json: () => Promise.resolve({ doses: [] }) })
|
||||
.mockResolvedValueOnce({
|
||||
ok: false,
|
||||
json: () => Promise.resolve({ code: "OUT_OF_STOCK" }),
|
||||
})
|
||||
.mockResolvedValueOnce({ ok: true, json: () => Promise.resolve({ doses: [] }) });
|
||||
|
||||
const { result } = renderHook(() => useDoses());
|
||||
|
||||
act(() => {
|
||||
result.current.setShowClearMissedConfirm(true);
|
||||
await waitFor(() => {
|
||||
expect(result.current.takenDoses.size).toBe(0);
|
||||
});
|
||||
|
||||
expect(result.current.showClearMissedConfirm).toBe(true);
|
||||
await act(async () => {
|
||||
await result.current.markDoseTaken("blocked-dose");
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
expect(result.current.takenDoses.has("blocked-dose")).toBe(false);
|
||||
});
|
||||
expect(alertMock).toHaveBeenCalledWith("common.outOfStockTakeBlocked");
|
||||
});
|
||||
|
||||
it("undoDoseTaken encodes special characters in dose ID", async () => {
|
||||
|
||||
@@ -182,10 +182,7 @@ const createMockAppContext = (overrides = {}) => ({
|
||||
getDayStockStatus: vi.fn(() => "success"),
|
||||
getDoseId: vi.fn((id, person) => (person ? `${id}-${person}` : id)),
|
||||
isDoseTakenAutomatically: vi.fn(() => false),
|
||||
showClearMissedConfirm: false,
|
||||
setShowClearMissedConfirm: vi.fn(),
|
||||
clearingMissed: false,
|
||||
dismissMissedDoses: vi.fn(),
|
||||
loadMeds: vi.fn(),
|
||||
loadSettings: vi.fn(),
|
||||
...overrides,
|
||||
});
|
||||
@@ -977,26 +974,18 @@ describe("DashboardPage with past days", () => {
|
||||
}
|
||||
});
|
||||
|
||||
it("shows clear missed doses button when there are missed doses", () => {
|
||||
render(
|
||||
<MemoryRouter>
|
||||
<DashboardPage />
|
||||
</MemoryRouter>
|
||||
);
|
||||
it("posts the computed dismiss-until payload when clearing missed doses", async () => {
|
||||
const loadMeds = vi.fn();
|
||||
const alertMock = vi.fn();
|
||||
global.alert = alertMock;
|
||||
global.fetch = vi.fn().mockResolvedValue({ ok: true });
|
||||
|
||||
// Should show clear missed button
|
||||
const clearBtn = document.querySelector(".clear-missed-btn");
|
||||
expect(clearBtn).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("opens clear missed confirmation modal and confirms action", () => {
|
||||
const dismissMissedDoses = vi.fn();
|
||||
mockContextValue = createMockAppContext({
|
||||
meds: mockMeds,
|
||||
coverageByMed: { Aspirin: { medsLeft: 25, daysLeft: 25 } },
|
||||
pastDays: mockPastDays,
|
||||
showPastDays: false,
|
||||
missedPastDoseIds: ["1-0-1-John", "1-0-2-John"],
|
||||
showClearMissedConfirm: true,
|
||||
dismissMissedDoses,
|
||||
missedPastDoseIds: [`${mockPastDays[0].meds[0].doses[0].id}-John`],
|
||||
loadMeds,
|
||||
});
|
||||
|
||||
render(
|
||||
@@ -1005,9 +994,26 @@ describe("DashboardPage with past days", () => {
|
||||
</MemoryRouter>
|
||||
);
|
||||
|
||||
expect(screen.getByText(/dashboard\.schedules\.clearMissedConfirmTitle/i)).toBeInTheDocument();
|
||||
fireEvent.click(screen.getByRole("button", { name: /dashboard\.schedules\.clearMissed/i }));
|
||||
fireEvent.click(screen.getByRole("button", { name: /dashboard\.schedules\.clearMissedConfirm/i }));
|
||||
expect(dismissMissedDoses).toHaveBeenCalledWith(["1-0-1-John", "1-0-2-John"]);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(global.fetch).toHaveBeenCalledWith(
|
||||
"/api/medications/dismiss-until",
|
||||
expect.objectContaining({
|
||||
method: "POST",
|
||||
credentials: "include",
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
const body = JSON.parse(((global.fetch as ReturnType<typeof vi.fn>).mock.calls[0]?.[1]?.body as string) ?? "{}");
|
||||
expect(body).toEqual({
|
||||
medicationIds: [1],
|
||||
until: mockPastDays[0].date.toISOString().slice(0, 10),
|
||||
});
|
||||
expect(loadMeds).toHaveBeenCalled();
|
||||
expect(alertMock).toHaveBeenCalledWith(expect.stringContaining("dashboard.schedules.clearMissedSuccess"));
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1169,25 +1175,6 @@ describe("DashboardPage additional branches", () => {
|
||||
expect(openScheduleLightbox).toHaveBeenCalledWith("/api/images/aspirin.png");
|
||||
});
|
||||
|
||||
it("clicking clear missed button opens confirmation", () => {
|
||||
const setShowClearMissedConfirm = vi.fn();
|
||||
mockContextValue = createMockAppContext({
|
||||
pastDays: mockPastDays,
|
||||
missedPastDoseIds: ["1-0-1-John"],
|
||||
setShowClearMissedConfirm,
|
||||
});
|
||||
|
||||
render(
|
||||
<MemoryRouter>
|
||||
<DashboardPage />
|
||||
</MemoryRouter>
|
||||
);
|
||||
|
||||
const clearBtn = document.querySelector(".clear-missed-btn") as HTMLButtonElement;
|
||||
fireEvent.click(clearBtn);
|
||||
expect(setShowClearMissedConfirm).toHaveBeenCalledWith(true);
|
||||
});
|
||||
|
||||
it("renders and interacts with today day schedule block", () => {
|
||||
const markDoseTaken = vi.fn();
|
||||
const undoDoseTaken = vi.fn();
|
||||
|
||||
@@ -1636,6 +1636,15 @@ describe("computeMissedPastDoseIds", () => {
|
||||
expect(result).toEqual([`1-0-${march13}`, `1-0-${march14}`]);
|
||||
});
|
||||
|
||||
it("matches medication dismissedUntil via display name when the schedule row uses genericName", () => {
|
||||
const march10 = new Date("2024-03-10T00:00:00").getTime();
|
||||
const pastDays = [makePastDay("Acetylsalicylic Acid", [{ id: `1-0-${march10}` }])];
|
||||
const meds = [{ name: "", genericName: "Acetylsalicylic Acid", dismissedUntil: "2024-03-12" }];
|
||||
|
||||
const result = computeMissedPastDoseIds(pastDays, meds, new Set(), new Set());
|
||||
expect(result).toEqual([]);
|
||||
});
|
||||
|
||||
it("expands takenBy people into separate dose IDs", () => {
|
||||
const march10 = new Date("2024-03-10T00:00:00").getTime();
|
||||
const pastDays = [makePastDay("SharedMed", [{ id: `1-0-${march10}`, takenBy: ["Alice", "Bob"] }])];
|
||||
|
||||
Reference in New Issue
Block a user