Skip to content

Commit abe3626

Browse files
authored
Maint/store tests (#167)
* add store tests * add more tests * change jobID * update wording * add a few more test cases * update descriptions
1 parent e389b14 commit abe3626

File tree

5 files changed

+186
-1
lines changed

5 files changed

+186
-1
lines changed

bun.lockb

19.4 KB
Binary file not shown.

package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
"test": "vitest"
1313
},
1414
"dependencies": {
15+
"@testing-library/react": "^16.3.0",
1516
"@types/lodash-es": "^4.17.12",
1617
"antd": "^5.27.1",
1718
"firebase": "^11.2.0",
@@ -33,6 +34,7 @@
3334
"eslint-plugin-react-hooks": "^5.0.0",
3435
"eslint-plugin-react-refresh": "^0.4.13",
3536
"globals": "^15.11.0",
37+
"jsdom": "^27.2.0",
3638
"typescript": "~5.6.2",
3739
"typescript-eslint": "^8.10.0",
3840
"vite": "^5.4.9",

src/state/store.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ type Actions = {
4848

4949
export type RecipeStore = RecipeState & UIState & Actions;
5050

51-
const INITIAL_RECIPE_ID = "peroxisome_v_gradient_packing";
51+
export const INITIAL_RECIPE_ID = "peroxisome_v_gradient_packing";
5252

5353
const initialState: RecipeState & UIState = {
5454
selectedRecipeId: INITIAL_RECIPE_ID,

src/test/store.test.ts

Lines changed: 182 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,182 @@
1+
import { act, renderHook } from "@testing-library/react";
2+
import { expect, test } from "vitest";
3+
import { useRecipeStore, INITIAL_RECIPE_ID } from "../state/store";
4+
import { EMPTY_PACKING_RESULT } from "../state/constants";
5+
6+
7+
test("recipeStore loads default state", () => {
8+
const { result } = renderHook(() => useRecipeStore());
9+
const store = result.current;
10+
11+
expect(store.selectedRecipeId).toBe(INITIAL_RECIPE_ID);
12+
expect(store.isPacking).toBe(false);
13+
expect(store.recipes).toBeDefined();
14+
expect(store.inputOptions).toBeDefined();
15+
expect(store.packingResults).toBeDefined();
16+
expect(store.packingResults[INITIAL_RECIPE_ID]).toEqual(EMPTY_PACKING_RESULT);
17+
});
18+
19+
test("selectedRecipeId defaults to INITIAL_RECIPE_ID when invalid id selected", () => {
20+
const { result } = renderHook(() => useRecipeStore());
21+
const store = result.current;
22+
23+
act(() => {
24+
store.selectRecipe("non-existent-recipe-id");
25+
});
26+
27+
expect(store.selectedRecipeId).toBe(INITIAL_RECIPE_ID);
28+
});
29+
30+
test("recipeStore loads all recipes", async () => {
31+
const { result } = renderHook(() => useRecipeStore());
32+
33+
await result.current.loadInputOptions();
34+
await result.current.loadAllRecipes();
35+
36+
expect(Object.keys(result.current.inputOptions)).toHaveLength(5);
37+
38+
for (const recipeId of Object.keys(result.current.inputOptions)) {
39+
await result.current.selectRecipe(recipeId);
40+
expect(result.current.recipes[recipeId]).toBeDefined();
41+
expect(result.current.recipes[recipeId].recipeId).toBe(recipeId);
42+
expect(result.current.selectedRecipeId).toBe(recipeId);
43+
}
44+
expect(Object.keys(result.current.recipes)).toHaveLength(5);
45+
});
46+
47+
test("editRecipe updates recipe object", async () => {
48+
const { result } = renderHook(() => useRecipeStore());
49+
50+
const recipeId = INITIAL_RECIPE_ID;
51+
await result.current.selectRecipe(recipeId);
52+
53+
const path = "composition.membrane.count";
54+
const initialValue = 1;
55+
expect(result.current.getCurrentValue(path)).toBe(initialValue);
56+
57+
const newValue = 3;
58+
act(() => {
59+
result.current.editRecipe(recipeId, path, newValue);
60+
});
61+
expect(result.current.getCurrentValue(path)).toBe(newValue);
62+
});
63+
64+
test("restoreRecipeDefault resets recipe to initial state", async () => {
65+
const { result } = renderHook(() => useRecipeStore());
66+
67+
const recipeId = INITIAL_RECIPE_ID;
68+
await result.current.selectRecipe(recipeId);
69+
70+
const path = "composition.membrane.count";
71+
const initialValue = 1;
72+
73+
act(() => {
74+
result.current.editRecipe(recipeId, path, 6);
75+
result.current.restoreRecipeDefault(recipeId);
76+
});
77+
expect(result.current.getCurrentValue(path)).toBe(initialValue);
78+
});
79+
80+
test("editing one recipe does not affect others", async () => {
81+
const { result } = renderHook(() => useRecipeStore());
82+
83+
const recipeId1 = INITIAL_RECIPE_ID;
84+
85+
// Select a different recipe ID for recipeId2
86+
const recipeId2 = Object.keys(result.current.inputOptions)[1];
87+
88+
await result.current.selectRecipe(recipeId1);
89+
const path = "composition.membrane.count";
90+
const initialCount = 1;
91+
expect(result.current.getCurrentValue(path)).toBe(initialCount);
92+
93+
await result.current.selectRecipe(recipeId2);
94+
expect(result.current.getCurrentValue(path)).toBe(initialCount);
95+
96+
const newValue1 = 5;
97+
await result.current.selectRecipe(recipeId1);
98+
act(() => {
99+
result.current.editRecipe(recipeId1, path, newValue1);
100+
});
101+
expect(result.current.getCurrentValue(path)).toBe(newValue1);
102+
103+
// Switch back to recipeId2 and verify its value is unchanged
104+
await result.current.selectRecipe(recipeId2);
105+
expect(result.current.getCurrentValue(path)).toBe(initialCount);
106+
107+
// Switch back to recipeId1 and verify its value is the edited one
108+
await result.current.selectRecipe(recipeId1);
109+
expect(result.current.getCurrentValue(path)).toBe(newValue1);
110+
});
111+
112+
test("setJobLogs updates job logs for current recipe", async () => {
113+
const { result } = renderHook(() => useRecipeStore());
114+
115+
const recipeId = INITIAL_RECIPE_ID;
116+
await result.current.selectRecipe(recipeId);
117+
118+
const logs = "Some Error Message";
119+
120+
act(() => {
121+
result.current.setJobLogs(logs);
122+
});
123+
124+
expect(result.current.packingResults[recipeId].jobLogs).toBe(logs);
125+
126+
for (const recipeId of Object.keys(result.current.inputOptions)) {
127+
if (recipeId !== INITIAL_RECIPE_ID) {
128+
// No other job logs should have been updated to `logs`
129+
expect(result.current.packingResults[recipeId]?.jobLogs).not.toBe(logs);
130+
}
131+
}
132+
});
133+
134+
test("setJobId updates job ID for current recipe", async () => {
135+
const { result } = renderHook(() => useRecipeStore());
136+
137+
const recipeId = INITIAL_RECIPE_ID;
138+
await result.current.selectRecipe(recipeId);
139+
140+
const jobId = "job-123";
141+
142+
act(() => {
143+
result.current.setJobId(jobId);
144+
});
145+
146+
expect(result.current.packingResults[recipeId].jobId).toBe(jobId);
147+
148+
for (const recipeId of Object.keys(result.current.inputOptions)) {
149+
if (recipeId !== INITIAL_RECIPE_ID) {
150+
// No other job ids should have been updated to `jobId`
151+
expect(result.current.packingResults[recipeId]?.jobId).not.toBe(jobId);
152+
}
153+
}
154+
});
155+
156+
test("setPackingResults updates packing results for current recipe", async () => {
157+
const { result } = renderHook(() => useRecipeStore());
158+
159+
const recipeId = INITIAL_RECIPE_ID;
160+
await result.current.selectRecipe(recipeId);
161+
162+
const packingResult = {
163+
jobId: "job-456",
164+
jobLogs: "Packing completed successfully.",
165+
resultUrl: "http://example.com/result",
166+
runTime: 120,
167+
outputDir: "/output/dir",
168+
};
169+
170+
act(() => {
171+
result.current.setPackingResults(packingResult);
172+
});
173+
174+
expect(result.current.packingResults[recipeId]).toEqual(packingResult);
175+
176+
for (const recipeId of Object.keys(result.current.inputOptions)) {
177+
if (recipeId !== INITIAL_RECIPE_ID) {
178+
// No other packing results should have been updated to `packingResult`
179+
expect(result.current.packingResults[recipeId]).not.toEqual(packingResult);
180+
}
181+
}
182+
});

vitest.config.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,5 +8,6 @@ export default defineConfig({
88
provider: 'v8',
99
reporter: ['text', 'json-summary', 'json'],
1010
},
11+
environment: 'jsdom',
1112
},
1213
})

0 commit comments

Comments
 (0)