frontend: cover the two ephemeral stores
The mood-entry component list and the care-action link hand-off had no offline check, so a lost default intensity, a broken de-dupe or a link that outlives its thirty-minute window could only be caught by wiring the screens together on a device. Both stores are now driven through their public actions, with the expiry boundary pinned
Pedro Lucas Porcellis porcellis@eletrotupi.com · 9 days ago
e11faebd227a1ab47246f3cf6d02d42cc42a6c8c Browse filesParents
b3ead45Changed files
2
Additions
+204
Deletions
0
/dev/null → frontend/tests/stores/careActionLink.unit.test.ts
1 +
import { useCareActionLinkStore } from "@/stores";
2 +
3 +
const TTL_MS = 30 * 60 * 1000;
4 +
const START = new Date(2025, 0, 15, 10, 0, 0);
5 +
6 +
const CLEARED = {
7 +
pendingId: null,
8 +
expiresAt: null,
9 +
triggerLinked: false,
10 +
moodLinked: false,
11 +
};
12 +
13 +
const SIDES = [
14 +
{
15 +
side: "trigger",
16 +
link: "linkTrigger",
17 +
otherLink: "linkMood",
18 +
linkedFlag: "triggerLinked",
19 +
},
20 +
{
21 +
side: "mood",
22 +
link: "linkMood",
23 +
otherLink: "linkTrigger",
24 +
linkedFlag: "moodLinked",
25 +
},
26 +
] as const;
27 +
28 +
const state = () => useCareActionLinkStore.getState();
29 +
30 +
beforeEach(() => {
31 +
jest.useFakeTimers();
32 +
jest.setSystemTime(START);
33 +
useCareActionLinkStore.setState(CLEARED);
34 +
});
35 +
36 +
afterEach(() => {
37 +
jest.useRealTimers();
38 +
});
39 +
40 +
describe("setPending", () => {
41 +
it("arms the expiry thirty minutes out and drops the linked sides", () => {
42 +
useCareActionLinkStore.setState({ triggerLinked: true, moodLinked: true });
43 +
44 +
state().setPending(42);
45 +
46 +
expect(state().pendingId).toBe(42);
47 +
expect(state().expiresAt).toBe(START.getTime() + TTL_MS);
48 +
expect(state().triggerLinked).toBe(false);
49 +
expect(state().moodLinked).toBe(false);
50 +
});
51 +
});
52 +
53 +
describe.each(SIDES)(
54 +
"linking the $side side",
55 +
({ link, otherLink, linkedFlag }) => {
56 +
it("arms that side and hands back the pending id", () => {
57 +
state().setPending(7);
58 +
59 +
expect(state()[link]()).toBe(7);
60 +
expect(state().pendingId).toBe(7);
61 +
expect(state()[linkedFlag]).toBe(true);
62 +
});
63 +
64 +
it("clears the pending link once the other side links", () => {
65 +
state().setPending(7);
66 +
state()[link]();
67 +
68 +
expect(state()[otherLink]()).toBe(7);
69 +
expect(state()).toMatchObject(CLEARED);
70 +
});
71 +
72 +
it("refuses a repeat of its own side", () => {
73 +
state().setPending(7);
74 +
state()[link]();
75 +
76 +
expect(state()[link]()).toBeNull();
77 +
expect(state().pendingId).toBe(7);
78 +
expect(state()[linkedFlag]).toBe(true);
79 +
});
80 +
81 +
it("still hands back the id at the exact expiry boundary", () => {
82 +
state().setPending(9);
83 +
jest.setSystemTime(START.getTime() + TTL_MS);
84 +
85 +
expect(state()[link]()).toBe(9);
86 +
expect(state()[linkedFlag]).toBe(true);
87 +
});
88 +
89 +
it("discards an expired pending action instead of handing it off", () => {
90 +
state().setPending(9);
91 +
jest.setSystemTime(START.getTime() + TTL_MS + 1);
92 +
93 +
expect(state()[link]()).toBeNull();
94 +
expect(state()).toMatchObject(CLEARED);
95 +
});
96 +
},
97 +
);
98 +
99 +
describe("with nothing pending", () => {
100 +
it("hands back null for either side", () => {
101 +
expect(state().linkTrigger()).toBeNull();
102 +
expect(state().linkMood()).toBeNull();
103 +
});
104 +
});
105 +
106 +
describe("clear", () => {
107 +
it("resets every field", () => {
108 +
state().setPending(3);
109 +
useCareActionLinkStore.setState({ triggerLinked: true, moodLinked: true });
110 +
111 +
state().clear();
112 +
113 +
expect(state()).toMatchObject(CLEARED);
114 +
});
115 +
});
/dev/null → frontend/tests/stores/moodEntry.unit.test.ts
1 +
import { useMoodEntryStore } from "@/stores";
2 +
3 +
const state = () => useMoodEntryStore.getState();
4 +
const component = (id: string, intensity: number) => ({ id, intensity });
5 +
6 +
beforeEach(() => {
7 +
useMoodEntryStore.setState({ selectedMood: null, components: [] });
8 +
});
9 +
10 +
describe("setSelectedMood", () => {
11 +
it("records the selected mood", () => {
12 +
state().setSelectedMood("good");
13 +
14 +
expect(state().selectedMood).toBe("good");
15 +
});
16 +
});
17 +
18 +
describe("setComponents", () => {
19 +
it("replaces the whole component list at once", () => {
20 +
state().setComponents([component("joy", 3), component("calm", 2)]);
21 +
22 +
expect(state().components).toStrictEqual([
23 +
component("joy", 3),
24 +
component("calm", 2),
25 +
]);
26 +
});
27 +
});
28 +
29 +
describe("addComponent", () => {
30 +
it("appends with the default intensity", () => {
31 +
state().addComponent("joy");
32 +
33 +
expect(state().components).toStrictEqual([component("joy", 1)]);
34 +
});
35 +
36 +
it("keeps the order the components were added in", () => {
37 +
state().addComponent("joy");
38 +
state().addComponent("calm");
39 +
40 +
expect(state().components).toStrictEqual([
41 +
component("joy", 1),
42 +
component("calm", 1),
43 +
]);
44 +
});
45 +
46 +
it("ignores a re-add, keeping the intensity already set", () => {
47 +
state().addComponent("joy");
48 +
state().setComponentIntensity("joy", 3);
49 +
50 +
state().addComponent("joy");
51 +
52 +
expect(state().components).toStrictEqual([component("joy", 3)]);
53 +
});
54 +
});
55 +
56 +
describe("removeComponent", () => {
57 +
it("drops the component and leaves the rest", () => {
58 +
state().setComponents([component("joy", 1), component("calm", 2)]);
59 +
60 +
state().removeComponent("joy");
61 +
62 +
expect(state().components).toStrictEqual([component("calm", 2)]);
63 +
});
64 +
});
65 +
66 +
describe("setComponentIntensity", () => {
67 +
it("changes only the target component", () => {
68 +
state().setComponents([component("joy", 2), component("calm", 1)]);
69 +
70 +
state().setComponentIntensity("calm", 3);
71 +
72 +
expect(state().components).toStrictEqual([
73 +
component("joy", 2),
74 +
component("calm", 3),
75 +
]);
76 +
});
77 +
});
78 +
79 +
describe("reset", () => {
80 +
it("clears the mood and the component list", () => {
81 +
state().setSelectedMood("good");
82 +
state().addComponent("joy");
83 +
84 +
state().reset();
85 +
86 +
expect(state().selectedMood).toBeNull();
87 +
expect(state().components).toStrictEqual([]);
88 +
});
89 +
});