feat: add shared overview and harden frontend session state (#407)
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { fireEvent, render, screen } from "@testing-library/react";
|
||||
import { fireEvent, render, screen, within } from "@testing-library/react";
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { MedDetailModal } from "../../components/MedDetailModal";
|
||||
import type { Coverage, Medication, RefillEntry, StockThresholds } from "../../types";
|
||||
@@ -410,6 +410,112 @@ describe("MedDetailModal with refill modal", () => {
|
||||
|
||||
expect(screen.getByText("editStock.packageSize_150")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("shows bottles-based refill input for liquid container and preview in ml package amount", () => {
|
||||
const liquidMed: Medication = {
|
||||
...mockMedication,
|
||||
name: "Liquid Med",
|
||||
packageType: "liquid_container",
|
||||
packCount: 1,
|
||||
packageAmountValue: 150,
|
||||
packageAmountUnit: "ml",
|
||||
totalPills: 150,
|
||||
looseTablets: 150,
|
||||
};
|
||||
|
||||
render(<MedDetailModal {...defaultProps} selectedMed={liquidMed} showRefillModal={true} refillLoose={150} />);
|
||||
|
||||
const refillModal = document.querySelector(".refill-modal");
|
||||
expect(refillModal).not.toBeNull();
|
||||
expect(within(refillModal as HTMLElement).getByText(/form\.bottles/i)).toBeInTheDocument();
|
||||
expect(screen.queryByText(/refill\.pillsToAdd/i)).not.toBeInTheDocument();
|
||||
expect(screen.getByText(/\+150 form\.packageAmountUnitMl/i)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("maps liquid refill bottle input to package amount in ml", () => {
|
||||
const liquidMed: Medication = {
|
||||
...mockMedication,
|
||||
name: "Liquid Med",
|
||||
packageType: "liquid_container",
|
||||
packCount: 1,
|
||||
packageAmountValue: 150,
|
||||
packageAmountUnit: "ml",
|
||||
totalPills: 150,
|
||||
looseTablets: 150,
|
||||
};
|
||||
const onRefillLooseChange = vi.fn();
|
||||
const onRefillPacksChange = vi.fn();
|
||||
|
||||
render(
|
||||
<MedDetailModal
|
||||
{...defaultProps}
|
||||
selectedMed={liquidMed}
|
||||
showRefillModal={true}
|
||||
onRefillLooseChange={onRefillLooseChange}
|
||||
onRefillPacksChange={onRefillPacksChange}
|
||||
refillLoose={0}
|
||||
/>
|
||||
);
|
||||
|
||||
const input = document.querySelector(".refill-modal input[type='number']") as HTMLInputElement;
|
||||
fireEvent.change(input, { target: { value: "2" } });
|
||||
|
||||
expect(onRefillPacksChange).toHaveBeenCalledWith(2);
|
||||
expect(onRefillLooseChange).toHaveBeenCalledWith(300);
|
||||
});
|
||||
|
||||
it("shows tubes-based refill input for tube package and preview in g package amount", () => {
|
||||
const tubeMed: Medication = {
|
||||
...mockMedication,
|
||||
name: "Tube Med",
|
||||
packageType: "tube",
|
||||
packCount: 4,
|
||||
packageAmountValue: 150,
|
||||
packageAmountUnit: "g",
|
||||
totalPills: 600,
|
||||
looseTablets: 600,
|
||||
};
|
||||
|
||||
render(<MedDetailModal {...defaultProps} selectedMed={tubeMed} showRefillModal={true} refillLoose={150} />);
|
||||
|
||||
const refillModal = document.querySelector(".refill-modal");
|
||||
expect(refillModal).not.toBeNull();
|
||||
expect(within(refillModal as HTMLElement).getByText(/form\.tubes/i)).toBeInTheDocument();
|
||||
expect(screen.queryByText(/refill\.pillsToAdd/i)).not.toBeInTheDocument();
|
||||
expect(screen.getByText(/\+150 form\.packageAmountUnitG/i)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("maps tube refill count input to package amount in g", () => {
|
||||
const tubeMed: Medication = {
|
||||
...mockMedication,
|
||||
name: "Tube Med",
|
||||
packageType: "tube",
|
||||
packCount: 4,
|
||||
packageAmountValue: 150,
|
||||
packageAmountUnit: "g",
|
||||
totalPills: 600,
|
||||
looseTablets: 600,
|
||||
};
|
||||
const onRefillLooseChange = vi.fn();
|
||||
const onRefillPacksChange = vi.fn();
|
||||
|
||||
render(
|
||||
<MedDetailModal
|
||||
{...defaultProps}
|
||||
selectedMed={tubeMed}
|
||||
showRefillModal={true}
|
||||
onRefillLooseChange={onRefillLooseChange}
|
||||
onRefillPacksChange={onRefillPacksChange}
|
||||
refillLoose={0}
|
||||
/>
|
||||
);
|
||||
|
||||
const input = document.querySelector(".refill-modal input[type='number']") as HTMLInputElement;
|
||||
fireEvent.change(input, { target: { value: "2" } });
|
||||
|
||||
expect(onRefillPacksChange).toHaveBeenCalledWith(2);
|
||||
expect(onRefillLooseChange).toHaveBeenCalledWith(300);
|
||||
});
|
||||
});
|
||||
|
||||
describe("MedDetailModal actions", () => {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { fireEvent, render, screen } from "@testing-library/react";
|
||||
import { fireEvent, render, screen, waitFor } from "@testing-library/react";
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { ShareDialog } from "../../components/ShareDialog";
|
||||
|
||||
@@ -68,8 +68,9 @@ describe("ShareDialog", () => {
|
||||
|
||||
it("shows generated link", () => {
|
||||
render(<ShareDialog {...defaultProps} shareLink="http://example.com/share/abc123" />);
|
||||
const input = screen.getByRole("textbox");
|
||||
expect(input).toHaveValue("http://example.com/share/abc123");
|
||||
const inputs = screen.getAllByRole("textbox") as HTMLInputElement[];
|
||||
expect(inputs[0]).toHaveValue("http://example.com/share/abc123");
|
||||
expect(inputs[1]).toHaveValue("http://example.com/share/abc123/overview");
|
||||
});
|
||||
|
||||
it("calls onCopyShareLink when copy button is clicked", () => {
|
||||
@@ -85,13 +86,23 @@ describe("ShareDialog", () => {
|
||||
|
||||
it("selects link text when input is clicked", () => {
|
||||
render(<ShareDialog {...defaultProps} shareLink="http://example.com/share/abc123" />);
|
||||
const input = screen.getByRole("textbox") as HTMLInputElement;
|
||||
const input = screen.getAllByRole("textbox")[0] as HTMLInputElement;
|
||||
const selectMock = vi.fn();
|
||||
input.select = selectMock;
|
||||
fireEvent.click(input);
|
||||
expect(selectMock).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("copies overview link when overview copy button is clicked", async () => {
|
||||
render(<ShareDialog {...defaultProps} shareLink="http://example.com/share/abc123" />);
|
||||
|
||||
fireEvent.click(screen.getByRole("button", { name: /share\.copyOverviewLink/i }));
|
||||
|
||||
await waitFor(() => {
|
||||
expect(navigator.clipboard.writeText).toHaveBeenCalledWith("http://example.com/share/abc123/overview");
|
||||
});
|
||||
});
|
||||
|
||||
it("calls person and period change callbacks", () => {
|
||||
render(<ShareDialog {...defaultProps} />);
|
||||
|
||||
|
||||
@@ -294,4 +294,143 @@ describe("UserFilterModal", () => {
|
||||
expect(screen.queryByText("Med2")).not.toBeInTheDocument();
|
||||
expect(screen.getByText("Med3")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("renders tube intakes as applications and stock in g", () => {
|
||||
const onClose = vi.fn();
|
||||
const onOpenMedDetail = vi.fn();
|
||||
|
||||
const tubeMedication: Medication = {
|
||||
...mockMedication,
|
||||
id: 10,
|
||||
name: "Tube Med",
|
||||
genericName: "Tube Generic",
|
||||
packageType: "tube",
|
||||
totalPills: 600,
|
||||
looseTablets: 600,
|
||||
intakes: [
|
||||
{
|
||||
usage: 1,
|
||||
every: 1,
|
||||
start: "2024-01-01T21:04:00",
|
||||
takenBy: "John",
|
||||
intakeRemindersEnabled: true,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const tubeCoverage: Coverage = {
|
||||
name: "Tube Med",
|
||||
medsLeft: 600,
|
||||
daysLeft: null,
|
||||
depletionDate: null,
|
||||
depletionTime: null,
|
||||
nextDose: null,
|
||||
};
|
||||
|
||||
render(
|
||||
<UserFilterModal
|
||||
selectedUser="John"
|
||||
meds={[tubeMedication]}
|
||||
coverage={{ all: [tubeCoverage] }}
|
||||
settings={defaultSettings}
|
||||
onClose={onClose}
|
||||
onClearUser={vi.fn()}
|
||||
onOpenMedDetail={onOpenMedDetail}
|
||||
/>
|
||||
);
|
||||
|
||||
expect(screen.getByText(/form\.blisters\.applications_1/)).toBeInTheDocument();
|
||||
expect(screen.getByText("600/600 form.packageAmountUnitG")).toBeInTheDocument();
|
||||
expect(screen.queryByText(/600\/600 .*common\.pills/)).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("renders liquid container intakes and stock in ml", () => {
|
||||
const onClose = vi.fn();
|
||||
const onOpenMedDetail = vi.fn();
|
||||
|
||||
const liquidMedication: Medication = {
|
||||
...mockMedication,
|
||||
id: 11,
|
||||
name: "Liquid Container",
|
||||
genericName: "Liquid Generic",
|
||||
packageType: "liquid_container",
|
||||
totalPills: 150,
|
||||
looseTablets: 150,
|
||||
intakes: [
|
||||
{
|
||||
usage: 2,
|
||||
every: 1,
|
||||
start: "2024-01-01T09:32:00",
|
||||
intakeUnit: "ml",
|
||||
takenBy: "John",
|
||||
intakeRemindersEnabled: true,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const liquidCoverage: Coverage = {
|
||||
name: "Liquid Container",
|
||||
medsLeft: 0,
|
||||
daysLeft: 0,
|
||||
depletionDate: null,
|
||||
depletionTime: null,
|
||||
nextDose: null,
|
||||
};
|
||||
|
||||
render(
|
||||
<UserFilterModal
|
||||
selectedUser="John"
|
||||
meds={[liquidMedication]}
|
||||
coverage={{ all: [liquidCoverage] }}
|
||||
settings={defaultSettings}
|
||||
onClose={onClose}
|
||||
onClearUser={vi.fn()}
|
||||
onOpenMedDetail={onOpenMedDetail}
|
||||
/>
|
||||
);
|
||||
|
||||
expect(screen.getByText(/2 form\.packageAmountUnitMl common\.daily/)).toBeInTheDocument();
|
||||
expect(screen.getByText("0/150 form.packageAmountUnitMl")).toBeInTheDocument();
|
||||
expect(screen.queryByText(/0\/150 .*common\.pills/)).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("renders medicationForm liquid as ml in modal fallback", () => {
|
||||
const onClose = vi.fn();
|
||||
const onOpenMedDetail = vi.fn();
|
||||
|
||||
const legacyLiquidMedication: Medication = {
|
||||
...mockMedication,
|
||||
id: 12,
|
||||
name: "Legacy Liquid",
|
||||
medicationForm: "liquid",
|
||||
packageType: "bottle",
|
||||
totalPills: 100,
|
||||
looseTablets: 100,
|
||||
blisters: [{ usage: 1, every: 1, start: "2024-01-01T10:00:00" }],
|
||||
};
|
||||
|
||||
const legacyLiquidCoverage: Coverage = {
|
||||
name: "Legacy Liquid",
|
||||
medsLeft: 40,
|
||||
daysLeft: 10,
|
||||
depletionDate: null,
|
||||
depletionTime: null,
|
||||
nextDose: null,
|
||||
};
|
||||
|
||||
render(
|
||||
<UserFilterModal
|
||||
selectedUser="John"
|
||||
meds={[legacyLiquidMedication]}
|
||||
coverage={{ all: [legacyLiquidCoverage] }}
|
||||
settings={defaultSettings}
|
||||
onClose={onClose}
|
||||
onClearUser={vi.fn()}
|
||||
onOpenMedDetail={onOpenMedDetail}
|
||||
/>
|
||||
);
|
||||
|
||||
expect(screen.getByText(/1 form\.packageAmountUnitMl common\.daily/)).toBeInTheDocument();
|
||||
expect(screen.getByText("40/100 form.packageAmountUnitMl")).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user