Skip to content

Commit 1c6cc47

Browse files
authored
Fix vue tests, cleanup test definitions
1 parent 3f53168 commit 1c6cc47

13 files changed

+447
-229
lines changed

Diff for: src/react/ComponentScope.test.tsx

+22-4
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,16 @@
11
import { act, renderHook } from "@testing-library/react-hooks";
22
import { atom, useAtom } from "jotai";
3+
import { createLifecycleUtils } from "../shared/testing/lifecycle";
34
import { ComponentScope, molecule } from "./";
4-
import { strictModeSuite } from "./testing/strictModeSuite";
55
import { createTestInjectorProvider } from "./testing/TestInjectorProvider";
6+
import { strictModeSuite } from "./testing/strictModeSuite";
67
import { useMolecule } from "./useMolecule";
78

9+
const compLifecycle = createLifecycleUtils();
10+
811
const ComponentScopedCountMolecule = molecule((_, scope) => {
912
scope(ComponentScope);
13+
compLifecycle.connect();
1014
return atom(0);
1115
});
1216

@@ -43,15 +47,24 @@ strictModeSuite(({ wrapper: Outer }) => {
4347

4448
describe("Component scoped molecules", () => {
4549
test("should increment counter", () => {
50+
compLifecycle.expectUncalled();
4651
testOneCounter(ComponentScopedCountMolecule, 1);
52+
53+
expect(compLifecycle.executions).toHaveBeenCalledOnce();
54+
expect(compLifecycle.mounts).toHaveBeenCalledOnce();
55+
expect(compLifecycle.unmounts).toHaveBeenCalledOnce();
4756
});
4857
test("two counters should be not be connected when component scoped", () => {
58+
compLifecycle.expectUncalled();
4959
testTwoCounters(ComponentScopedCountMolecule, {
5060
actual1: true,
5161
actual2: true,
5262
expected1: 1,
5363
expected2: 1,
5464
});
65+
expect(compLifecycle.executions).toHaveBeenCalledTimes(2);
66+
expect(compLifecycle.mounts).toHaveBeenCalledTimes(2);
67+
expect(compLifecycle.unmounts).toHaveBeenCalledTimes(2);
5568
});
5669
test("two counters should be not be connected when component scoped, only one", () => {
5770
testTwoCounters(ComponentScopedCountMolecule, {
@@ -77,7 +90,7 @@ strictModeSuite(({ wrapper: Outer }) => {
7790
) {
7891
const TestInjectorProvider = createTestInjectorProvider(Outer);
7992

80-
const { result } = renderHook(() => useCounter(mol), {
93+
const { result, ...rest } = renderHook(() => useCounter(mol), {
8194
wrapper: TestInjectorProvider,
8295
});
8396

@@ -86,6 +99,8 @@ strictModeSuite(({ wrapper: Outer }) => {
8699
});
87100

88101
expect(result.current.count).toBe(expectedResult);
102+
103+
rest.unmount();
89104
}
90105

91106
function testTwoCounters(
@@ -99,10 +114,10 @@ strictModeSuite(({ wrapper: Outer }) => {
99114
) {
100115
const TestInjectorProvider = createTestInjectorProvider(Outer);
101116

102-
const { result: result1 } = renderHook(() => useCounter(mol), {
117+
const { result: result1, ...rest1 } = renderHook(() => useCounter(mol), {
103118
wrapper: TestInjectorProvider,
104119
});
105-
const { result: result2 } = renderHook(() => useCounter(mol), {
120+
const { result: result2, ...rest2 } = renderHook(() => useCounter(mol), {
106121
wrapper: TestInjectorProvider,
107122
});
108123

@@ -115,5 +130,8 @@ strictModeSuite(({ wrapper: Outer }) => {
115130

116131
expect(result1.current.count).toBe(opts.expected1);
117132
expect(result2.current.count).toBe(opts.expected2);
133+
134+
rest1.unmount();
135+
rest2.unmount();
118136
}
119137
});

Diff for: src/react/ScopeProvider.test.tsx

+107-39
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
import { act, renderHook } from "@testing-library/react-hooks";
22
import { atom, useAtom } from "jotai";
33
import React, { ReactNode, useContext, useRef, useState } from "react";
4-
import { ComponentScope, createScope, molecule } from "../vanilla";
4+
import { createLifecycleUtils } from "../shared/testing/lifecycle";
5+
import { ComponentScope, createScope, molecule, use } from "../vanilla";
56
import { ScopeProvider } from "./ScopeProvider";
67
import { ScopeContext } from "./contexts/ScopeContext";
78
import { strictModeSuite } from "./testing/strictModeSuite";
@@ -18,19 +19,23 @@ export const UserScope = createScope("[email protected]");
1819

1920
const AtomScope = createScope(atom("[email protected]"));
2021

21-
export const UserMolecule = molecule((_, getScope) => {
22-
const userId = getScope(UserScope);
22+
const userLifecycle = createLifecycleUtils();
23+
export const UserMolecule = molecule(() => {
24+
const userId = use(UserScope);
2325

26+
userLifecycle.connect(userId);
2427
return {
2528
example: Math.random(),
2629
userId,
2730
};
2831
});
2932

30-
const AtomMolecule = molecule((_, getScope) => {
31-
const userAtom = getScope(AtomScope);
33+
const atomLifecycle = createLifecycleUtils();
34+
const AtomMolecule = molecule(() => {
35+
const userAtom = use(AtomScope);
3236

3337
const userNameAtom = atom((get) => get(userAtom) + " name");
38+
atomLifecycle.connect(userAtom);
3439
return {
3540
example: Math.random(),
3641
userIdAtom: userAtom,
@@ -109,28 +114,41 @@ strictModeSuite(({ wrapper: Outer }) => {
109114
context: useContext(ScopeContext),
110115
};
111116
};
112-
const { result: result1 } = renderHook(useUserMolecule, {
117+
const { result: result1, ...rest1 } = renderHook(useUserMolecule, {
113118
wrapper: Wrapper1,
114119
});
115-
const { result: result2 } = renderHook(useUserMolecule, {
120+
121+
expect(userLifecycle.mounts).toHaveBeenLastCalledWith("[email protected]");
122+
123+
const { result: result2, ...rest2 } = renderHook(useUserMolecule, {
116124
wrapper: Wrapper2,
117125
});
126+
expect(userLifecycle.mounts).toHaveBeenLastCalledWith(
127+
128+
);
118129

119130
expect(result1.current.context).toStrictEqual([
120131
[UserScope, "[email protected]"],
121132
]);
122133
expect(result1.current.molecule.userId).toBe("[email protected]");
123134
expect(result2.current.molecule.userId).toBe("[email protected]");
135+
136+
rest1.unmount();
137+
expect(userLifecycle.unmounts).toHaveBeenLastCalledWith("[email protected]");
138+
139+
rest2.unmount();
140+
expect(userLifecycle.unmounts).toHaveBeenLastCalledWith(
141+
142+
);
143+
144+
userLifecycle.expectToHaveBeenCalledTimes(2);
145+
userLifecycle.expectToMatchCalls(
146+
147+
148+
);
124149
});
125150

126151
describe("String scopes", () => {
127-
const useUserMolecule = () => {
128-
return {
129-
molecule: useMolecule(UserMolecule),
130-
context: useContext(ScopeContext),
131-
};
132-
};
133-
134152
test("String scope values are cleaned up at the right time (not too soon, not too late)", async () => {
135153
const StringScopeTestContext = React.createContext<
136154
ReturnType<typeof useTextHook>
@@ -142,7 +160,7 @@ strictModeSuite(({ wrapper: Outer }) => {
142160
const props = { mountA, mountB, setMountA, setMountB, insideValue };
143161
return props;
144162
};
145-
const sharedKey = "[email protected]";
163+
const sharedAtExample = "[email protected]";
146164
const TestStuffProvider: React.FC<{ children: ReactNode }> = ({
147165
children,
148166
}) => {
@@ -161,69 +179,96 @@ strictModeSuite(({ wrapper: Outer }) => {
161179
([scope]) => scope !== ComponentScope,
162180
);
163181
const context = useContext(StringScopeTestContext);
182+
useMolecule(UserMolecule);
164183
context.insideValue.current = scopes;
165184
return <div>Bad</div>;
166185
};
167186
const Controller = (props: any) => {
168187
return (
169188
<>
170189
{props.mountA && (
171-
<ScopeProvider scope={UserScope} value={sharedKey}>
190+
<ScopeProvider scope={UserScope} value={sharedAtExample}>
172191
<Child />
173192
</ScopeProvider>
174193
)}
175194
{props.mountB && (
176-
<ScopeProvider scope={UserScope} value={sharedKey}>
195+
<ScopeProvider scope={UserScope} value={sharedAtExample}>
177196
<Child />
178197
</ScopeProvider>
179198
)}
180199
</>
181200
);
182201
};
183202

184-
const { result } = renderHook(() => useContext(StringScopeTestContext), {
185-
wrapper: TestStuffProvider,
186-
});
203+
userLifecycle.expectUncalled();
204+
205+
// When the component is initially mounted
206+
const { result, ...rest } = renderHook(
207+
() => useContext(StringScopeTestContext),
208+
{
209+
wrapper: TestStuffProvider,
210+
},
211+
);
187212

188213
const { insideValue } = result.current;
189-
const initialScopes = insideValue.current;
190-
expect(initialScopes).not.toBeUndefined();
191-
expect(initialScopes.length).toBe(1);
192214

193-
const userScopeTuple = initialScopes[0];
194-
if (true) {
195-
const [scopeKey, scopeValue] = userScopeTuple;
196-
expect(scopeKey).toBe(UserScope);
197-
expect(scopeValue).toBe(sharedKey);
198-
}
215+
// Then the lifecycle events are called
216+
expect(userLifecycle.mounts).toHaveBeenCalledWith(sharedAtExample);
217+
// Then the scopes matches the initial value
218+
expect(insideValue.current).toStrictEqual([[UserScope, sharedAtExample]]);
219+
220+
const userScopeTuple = insideValue.current[0];
199221

200-
await act(() => {
222+
// When A is unmounted
223+
act(() => {
201224
result.current.setMountA(false);
202225
});
203226

227+
// Then the molecule is still mounted
228+
userLifecycle.expectActivelyMounted();
229+
204230
const afterUnmountCache = insideValue.current;
205231

232+
// Then the scope tuple is unchanged
206233
expect(afterUnmountCache[0]).toBe(userScopeTuple);
207234

235+
// When B is unmounted
208236
act(() => {
209237
result.current.setMountB(false);
210238
});
211239

240+
// Then the molecule is unmounted
241+
userLifecycle.expectToMatchCalls([sharedAtExample]);
242+
243+
// Then the scope tuple is unchanged
212244
const finalTuples = insideValue.current;
213245
expect(finalTuples[0]).toBe(userScopeTuple);
214246

247+
// When B is re-mounted
215248
act(() => {
216249
result.current.setMountB(true);
217250
});
218251

252+
// Then a fresh tuple is created
219253
const freshTuples = insideValue.current;
220254
const [freshTuple] = freshTuples;
255+
256+
// And it does not match the original
221257
expect(freshTuple).not.toBe(userScopeTuple);
222258
if (true) {
223259
const [scopeKey, scopeValue] = freshTuple;
224260
expect(scopeKey).toBe(UserScope);
225-
expect(scopeValue).toBe(sharedKey);
261+
expect(scopeValue).toBe(sharedAtExample);
226262
}
263+
264+
// When the component is unmounted
265+
rest.unmount();
266+
267+
// Then the user molecule lifecycle has been completed 2
268+
userLifecycle.expectToHaveBeenCalledTimes(2);
269+
270+
// And it has been called with the same value twice, across 2 leases
271+
userLifecycle.expectToMatchCalls([sharedAtExample], [sharedAtExample]);
227272
});
228273
});
229274

@@ -241,8 +286,10 @@ strictModeSuite(({ wrapper: Outer }) => {
241286
</Outer>
242287
);
243288

244-
const voidMolecule = molecule((_, getScope) => {
245-
getScope(VoidScope);
289+
const voidLifecycle = createLifecycleUtils();
290+
const voidMolecule = molecule(() => {
291+
use(VoidScope);
292+
voidLifecycle.connect();
246293
return {
247294
example: Math.random(),
248295
};
@@ -253,14 +300,21 @@ strictModeSuite(({ wrapper: Outer }) => {
253300
context: useContext(ScopeContext),
254301
};
255302
};
256-
const { result: result1 } = renderHook(useVoidMolecule, {
303+
304+
voidLifecycle.expectUncalled();
305+
const { result: result1, ...rest1 } = renderHook(useVoidMolecule, {
257306
wrapper: Wrapper1,
258307
});
259-
const { result: result2 } = renderHook(useVoidMolecule, {
308+
const { result: result2, ...rest2 } = renderHook(useVoidMolecule, {
260309
wrapper: Wrapper2,
261310
});
262311

263312
expect(result1.current.molecule).not.toBe(result2.current.molecule);
313+
314+
rest1.unmount();
315+
rest2.unmount();
316+
317+
voidLifecycle.expectToHaveBeenCalledTimes(2);
264318
});
265319

266320
test("Object scope values are shared across providers", () => {
@@ -284,16 +338,24 @@ strictModeSuite(({ wrapper: Outer }) => {
284338
userId,
285339
};
286340
};
287-
const { result: result1 } = renderHook(useUserMolecule, {
341+
atomLifecycle.expectUncalled();
342+
const { result: result1, ...rest1 } = renderHook(useUserMolecule, {
288343
wrapper: Wrapper1,
289344
});
290-
const { result: result2 } = renderHook(useUserMolecule, {
345+
atomLifecycle.expectActivelyMounted();
346+
const { result: result2, ...rest2 } = renderHook(useUserMolecule, {
291347
wrapper: Wrapper2,
292348
});
293-
349+
atomLifecycle.expectActivelyMounted();
294350
expect(result1.current.molecule).toBe(result2.current.molecule);
295351
expect(result1.current.userId).toBe("[email protected]");
296352
expect(result2.current.userId).toBe("[email protected]");
353+
354+
rest1.unmount();
355+
atomLifecycle.expectActivelyMounted();
356+
rest2.unmount();
357+
358+
atomLifecycle.expectToMatchCalls([childAtom]);
297359
});
298360

299361
test("Use molecule should will use the nested scope", () => {
@@ -307,19 +369,25 @@ strictModeSuite(({ wrapper: Outer }) => {
307369
</Outer>
308370
);
309371

372+
userLifecycle.expectUncalled();
310373
const useUserMolecule = () => {
311374
return {
312375
molecule: useMolecule(UserMolecule),
313376
context: useContext(ScopeContext),
314377
};
315378
};
316-
const { result } = renderHook(useUserMolecule, {
379+
const { result, ...rest } = renderHook(useUserMolecule, {
317380
wrapper: Wrapper,
318381
});
382+
userLifecycle.expectActivelyMounted();
319383

320384
expect(result.current.context).toStrictEqual([
321385
[UserScope, "[email protected]"],
322386
]);
323387
expect(result.current.molecule.userId).toBe("[email protected]");
388+
389+
rest.unmount();
390+
391+
userLifecycle.expectToMatchCalls(["[email protected]"]);
324392
});
325393
});

0 commit comments

Comments
 (0)