From dfc706c0a304b67f55855dd13d6d21c835bd9fe2 Mon Sep 17 00:00:00 2001 From: David Maskasky Date: Tue, 24 Sep 2024 16:38:52 -0700 Subject: [PATCH] chore(tests): update references to tests to point to local jotai instance --- ..._basic_spec.tsx => 01_basic_spec.test.tsx} | 546 ++++++++++-------- .../ScopeProvider/02_removeScope.test.tsx | 207 +++++++ __tests__/ScopeProvider/02_removeScope.tsx | 129 ----- .../{03_nested.tsx => 03_nested.test.tsx} | 111 ++-- .../{04_derived.tsx => 04_derived.test.tsx} | 97 ++-- .../ScopeProvider/05_derived_self.test.tsx | 63 ++ __tests__/ScopeProvider/05_derived_self.tsx | 53 -- ...parent.tsx => 06_implicit_parent.test.tsx} | 92 +-- .../{07_writable.tsx => 07_writable.test.tsx} | 100 ++-- .../{08_family.tsx => 08_family.test.tsx} | 144 ++--- __tests__/ScopeProvider/09_mount.test.tsx | 77 +++ __tests__/ScopeProvider/09_scope_provider.tsx | 43 -- .../createIsolation/01_basic_spec.test.tsx | 7 + __tests__/createIsolation/01_basic_spec.tsx | 7 - __tests__/utils.ts | 66 ++- package.json | 9 +- pnpm-lock.yaml | 31 +- 17 files changed, 1038 insertions(+), 744 deletions(-) rename __tests__/ScopeProvider/{01_basic_spec.tsx => 01_basic_spec.test.tsx} (68%) create mode 100644 __tests__/ScopeProvider/02_removeScope.test.tsx delete mode 100644 __tests__/ScopeProvider/02_removeScope.tsx rename __tests__/ScopeProvider/{03_nested.tsx => 03_nested.test.tsx} (63%) rename __tests__/ScopeProvider/{04_derived.tsx => 04_derived.test.tsx} (88%) create mode 100644 __tests__/ScopeProvider/05_derived_self.test.tsx delete mode 100644 __tests__/ScopeProvider/05_derived_self.tsx rename __tests__/ScopeProvider/{06_implicit_parent.tsx => 06_implicit_parent.test.tsx} (50%) rename __tests__/ScopeProvider/{07_writable.tsx => 07_writable.test.tsx} (64%) rename __tests__/ScopeProvider/{08_family.tsx => 08_family.test.tsx} (71%) create mode 100644 __tests__/ScopeProvider/09_mount.test.tsx delete mode 100644 __tests__/ScopeProvider/09_scope_provider.tsx create mode 100644 __tests__/createIsolation/01_basic_spec.test.tsx delete mode 100644 __tests__/createIsolation/01_basic_spec.tsx diff --git a/__tests__/ScopeProvider/01_basic_spec.tsx b/__tests__/ScopeProvider/01_basic_spec.test.tsx similarity index 68% rename from __tests__/ScopeProvider/01_basic_spec.tsx rename to __tests__/ScopeProvider/01_basic_spec.test.tsx index 1c9d6e2..e48ccb2 100644 --- a/__tests__/ScopeProvider/01_basic_spec.tsx +++ b/__tests__/ScopeProvider/01_basic_spec.test.tsx @@ -1,4 +1,4 @@ -import { render } from '@testing-library/react' +import { render } from '@testing-library/react'; import { useAtom, useSetAtom, @@ -6,10 +6,10 @@ import { atom, type WritableAtom, type SetStateAction, -} from 'jotai' -import { atomWithReducer } from 'jotai/vanilla/utils' -import { ScopeProvider } from '../../src/index' -import { clickButton, getTextContents } from '../utils' +} from '../../jotai'; +import { atomWithReducer } from '../../jotai/vanilla/utils'; +import { ScopeProvider } from '../../src/index'; +import { clickButton, getTextContents } from '../utils'; describe('Counter', () => { /* @@ -18,10 +18,10 @@ describe('Counter', () => { S1[]: base0 */ test('01. ScopeProvider does not provide isolation for unscoped primitive atoms', () => { - const baseAtom = atom(0) - baseAtom.debugLabel = 'base' + const baseAtom = atom(0); + baseAtom.debugLabel = 'base'; function Counter({ level }: { level: string }) { - const [base, increaseBase] = useAtom(baseAtom) + const [base, increaseBase] = useAtom(baseAtom); return (
base:{base} @@ -33,7 +33,7 @@ describe('Counter', () => { increase
- ) + ); } function App() { @@ -46,30 +46,30 @@ describe('Counter', () => { - ) + ); } - const { container } = render() - const increaseUnscopedBase = '.level0.setBase' - const increaseScopedBase = '.level1.setBase' - const atomValueSelectors = ['.level0.base', '.level1.base'] + const { container } = render(); + const increaseUnscopedBase = '.level0.setBase'; + const increaseScopedBase = '.level1.setBase'; + const atomValueSelectors = ['.level0.base', '.level1.base']; expect(getTextContents(container, atomValueSelectors)).toEqual([ '0', // level0 base '0', // level1 base - ]) + ]); - clickButton(container, increaseUnscopedBase) + clickButton(container, increaseUnscopedBase); expect(getTextContents(container, atomValueSelectors)).toEqual([ '1', // level0 base '1', // level1 base - ]) + ]); - clickButton(container, increaseScopedBase) + clickButton(container, increaseScopedBase); expect(getTextContents(container, atomValueSelectors)).toEqual([ '2', // level0 base '2', // level1 base - ]) - }) + ]); + }); /* base, Derived(base) @@ -77,23 +77,27 @@ describe('Counter', () => { S1[]: base0 Derived0(base0) */ test('02. unscoped derived atoms are unaffected in ScopeProvider', () => { - const baseAtom = atom(0) + const baseAtom = atom(0); const derivedAtom = atom( (get) => get(baseAtom), (_get, set, value: SetStateAction) => set(baseAtom, value), - ) - baseAtom.debugLabel = 'base' + ); + baseAtom.debugLabel = 'base'; function Counter({ level }: { level: string }) { - const [derived, setDerived] = useAtom(derivedAtom) - const increaseDerived = () => setDerived((c) => c + 1) + const [derived, setDerived] = useAtom(derivedAtom); + const increaseDerived = () => setDerived((c) => c + 1); return (
base:{derived} -
- ) + ); } function App() { @@ -106,30 +110,30 @@ describe('Counter', () => { - ) + ); } - const { container } = render() - const increaseUnscopedBase = '.level0.setDerived' - const increaseScopedBase = '.level1.setDerived' - const atomValueSelectors = ['.level0.derived', '.level1.derived'] + const { container } = render(); + const increaseUnscopedBase = '.level0.setDerived'; + const increaseScopedBase = '.level1.setDerived'; + const atomValueSelectors = ['.level0.derived', '.level1.derived']; expect(getTextContents(container, atomValueSelectors)).toEqual([ '0', // level 0 derived '0', // level 1 derived - ]) + ]); - clickButton(container, increaseUnscopedBase) + clickButton(container, increaseUnscopedBase); expect(getTextContents(container, atomValueSelectors)).toEqual([ '1', // level 0 derived '1', // level 1 derived - ]) + ]); - clickButton(container, increaseScopedBase) + clickButton(container, increaseScopedBase); expect(getTextContents(container, atomValueSelectors)).toEqual([ '2', // level 0 derived '2', // level 1 derived - ]) - }) + ]); + }); /* base @@ -137,10 +141,10 @@ describe('Counter', () => { S1[base]: base1 */ test('03. ScopeProvider provides isolation for scoped primitive atoms', () => { - const baseAtom = atom(0) - baseAtom.debugLabel = 'base' + const baseAtom = atom(0); + baseAtom.debugLabel = 'base'; function Counter({ level }: { level: string }) { - const [base, increaseBase] = useAtom(baseAtom) + const [base, increaseBase] = useAtom(baseAtom); return (
base:{base} @@ -152,7 +156,7 @@ describe('Counter', () => { increase
- ) + ); } function App() { @@ -165,30 +169,30 @@ describe('Counter', () => { - ) + ); } - const { container } = render() - const increaseUnscopedBase = '.level0.setBase' - const increaseScopedBase = '.level1.setBase' - const atomValueSelectors = ['.level0.base', '.level1.base'] + const { container } = render(); + const increaseUnscopedBase = '.level0.setBase'; + const increaseScopedBase = '.level1.setBase'; + const atomValueSelectors = ['.level0.base', '.level1.base']; expect(getTextContents(container, atomValueSelectors)).toEqual([ '0', // level0 base '0', // level1 base - ]) + ]); - clickButton(container, increaseUnscopedBase) + clickButton(container, increaseUnscopedBase); expect(getTextContents(container, atomValueSelectors)).toEqual([ '1', // level0 base '0', // level1 base - ]) + ]); - clickButton(container, increaseScopedBase) + clickButton(container, increaseScopedBase); expect(getTextContents(container, atomValueSelectors)).toEqual([ '1', // level0 base '1', // level1 base - ]) - }) + ]); + }); /* base, derived(base) @@ -196,26 +200,30 @@ describe('Counter', () => { S1[base]: derived0(base1) */ test('04. unscoped derived can read and write to scoped primitive atoms', () => { - const baseAtom = atom(0) - baseAtom.debugLabel = 'base' + const baseAtom = atom(0); + baseAtom.debugLabel = 'base'; const derivedAtom = atom( (get) => get(baseAtom), (get, set) => set(baseAtom, get(baseAtom) + 1), - ) - derivedAtom.debugLabel = 'derived' + ); + derivedAtom.debugLabel = 'derived'; function Counter({ level }: { level: string }) { - const [derived, increaseFromDerived] = useAtom(derivedAtom) - const value = useAtomValue(baseAtom) + const [derived, increaseFromDerived] = useAtom(derivedAtom); + const value = useAtomValue(baseAtom); return (
base:{derived} value:{value} -
- ) + ); } function App() { @@ -228,36 +236,41 @@ describe('Counter', () => { - ) + ); } - const { container } = render() - const increaseUnscopedBase = '.level0.setBase' - const increaseScopedBase = '.level1.setBase' - const atomValueSelectors = ['.level0.base', '.level0.value', '.level1.base', '.level1.value'] + const { container } = render(); + const increaseUnscopedBase = '.level0.setBase'; + const increaseScopedBase = '.level1.setBase'; + const atomValueSelectors = [ + '.level0.base', + '.level0.value', + '.level1.base', + '.level1.value', + ]; expect(getTextContents(container, atomValueSelectors)).toEqual([ '0', // level0 base '0', // level0 value '0', // level1 base '0', // level1 value - ]) + ]); - clickButton(container, increaseUnscopedBase) + clickButton(container, increaseUnscopedBase); expect(getTextContents(container, atomValueSelectors)).toEqual([ '1', // level0 base '1', // level0 value '0', // level1 base '0', // level1 value - ]) + ]); - clickButton(container, increaseScopedBase) + clickButton(container, increaseScopedBase); expect(getTextContents(container, atomValueSelectors)).toEqual([ '1', // level0 base '1', // level0 value '1', // level1 base '1', // level1 value - ]) - }) + ]); + }); /* base, notScoped, derived(base + notScoped) @@ -265,38 +278,46 @@ describe('Counter', () => { S1[base]: derived0(base1 + notScoped0) */ test('05. unscoped derived can read both scoped and unscoped atoms', () => { - const baseAtom = atomWithReducer(0, (v) => v + 1) - baseAtom.debugLabel = 'base' - const notScopedAtom = atomWithReducer(0, (v) => v + 1) - notScopedAtom.debugLabel = 'notScoped' + const baseAtom = atomWithReducer(0, (v) => v + 1); + baseAtom.debugLabel = 'base'; + const notScopedAtom = atomWithReducer(0, (v) => v + 1); + notScopedAtom.debugLabel = 'notScoped'; const derivedAtom = atom((get) => ({ base: get(baseAtom), notScoped: get(notScopedAtom), - })) - derivedAtom.debugLabel = 'derived' + })); + derivedAtom.debugLabel = 'derived'; function Counter({ level }: { level: string }) { - const increaseBase = useSetAtom(baseAtom) - const derived = useAtomValue(derivedAtom) + const increaseBase = useSetAtom(baseAtom); + const derived = useAtomValue(derivedAtom); return (
base:{derived.base} not scoped: {derived.notScoped} -
- ) + ); } function IncreaseUnscoped() { - const increaseNotScoped = useSetAtom(notScopedAtom) + const increaseNotScoped = useSetAtom(notScopedAtom); return ( - - ) + ); } function App() { @@ -310,41 +331,45 @@ describe('Counter', () => { - ) + ); } - const { container } = render() - const increaseUnscopedBase = '.level0.setBase' - const increaseScopedBase = '.level1.setBase' - const increaseNotScoped = '.increaseNotScoped' - const atomValueSelectors = ['.level0.base', '.level1.base', '.level1.notScoped'] + const { container } = render(); + const increaseUnscopedBase = '.level0.setBase'; + const increaseScopedBase = '.level1.setBase'; + const increaseNotScoped = '.increaseNotScoped'; + const atomValueSelectors = [ + '.level0.base', + '.level1.base', + '.level1.notScoped', + ]; expect(getTextContents(container, atomValueSelectors)).toEqual([ '0', // level0 base '0', // level1 base '0', // level1 notScoped - ]) + ]); - clickButton(container, increaseUnscopedBase) + clickButton(container, increaseUnscopedBase); expect(getTextContents(container, atomValueSelectors)).toEqual([ '1', // level0 base '0', // level1 base '0', // level1 notScoped - ]) + ]); - clickButton(container, increaseScopedBase) + clickButton(container, increaseScopedBase); expect(getTextContents(container, atomValueSelectors)).toEqual([ '1', // level0 base '1', // level1 base '0', // level1 notScoped - ]) + ]); - clickButton(container, increaseNotScoped) + clickButton(container, increaseNotScoped); expect(getTextContents(container, atomValueSelectors)).toEqual([ '1', // level0 base '1', // level1 base '1', // level1 notScoped - ]) - }) + ]); + }); /* base, derived(base), @@ -352,29 +377,37 @@ describe('Counter', () => { S1[derived]: derived1(base1) */ test('06. dependencies of scoped derived are implicitly scoped', () => { - const baseAtom = atomWithReducer(0, (v) => v + 1) - baseAtom.debugLabel = 'base' + const baseAtom = atomWithReducer(0, (v) => v + 1); + baseAtom.debugLabel = 'base'; const derivedAtom = atom( (get) => get(baseAtom), (_get, set) => set(baseAtom), - ) - derivedAtom.debugLabel = 'derived' + ); + derivedAtom.debugLabel = 'derived'; function Counter({ level }: { level: string }) { - const increaseBase = useSetAtom(baseAtom) - const [derived, setDerived] = useAtom(derivedAtom) + const increaseBase = useSetAtom(baseAtom); + const [derived, setDerived] = useAtom(derivedAtom); return (
base:{derived} - -
- ) + ); } function App() { @@ -387,25 +420,25 @@ describe('Counter', () => { - ) + ); } - const { container } = render() - const increaseUnscopedBase = '.level0.setBase' - const increaseScopedBase = '.level1.setBase' - const increaseScopedDerived = '.level1.setDerived' - const atomValueSelectors = ['.level0.base', '.level1.base'] + const { container } = render(); + const increaseUnscopedBase = '.level0.setBase'; + const increaseScopedBase = '.level1.setBase'; + const increaseScopedDerived = '.level1.setDerived'; + const atomValueSelectors = ['.level0.base', '.level1.base']; - expect(getTextContents(container, atomValueSelectors)).toEqual(['0', '0']) + expect(getTextContents(container, atomValueSelectors)).toEqual(['0', '0']); - clickButton(container, increaseUnscopedBase) - expect(getTextContents(container, atomValueSelectors)).toEqual(['1', '0']) + clickButton(container, increaseUnscopedBase); + expect(getTextContents(container, atomValueSelectors)).toEqual(['1', '0']); - clickButton(container, increaseScopedBase) - expect(getTextContents(container, atomValueSelectors)).toEqual(['2', '0']) + clickButton(container, increaseScopedBase); + expect(getTextContents(container, atomValueSelectors)).toEqual(['2', '0']); - clickButton(container, increaseScopedDerived) - expect(getTextContents(container, atomValueSelectors)).toEqual(['2', '1']) - }) + clickButton(container, increaseScopedDerived); + expect(getTextContents(container, atomValueSelectors)).toEqual(['2', '1']); + }); /* base, derivedA(base), derivemB(base) @@ -413,23 +446,23 @@ describe('Counter', () => { S1[derivedA, derivedB]: derivedA1(base1), derivedB1(base1) */ test('07. scoped derived atoms can share implicitly scoped dependencies', () => { - const baseAtom = atomWithReducer(0, (v) => v + 1) - baseAtom.debugLabel = 'base' + const baseAtom = atomWithReducer(0, (v) => v + 1); + baseAtom.debugLabel = 'base'; const derivedAtomA = atom( (get) => get(baseAtom), (_get, set) => set(baseAtom), - ) - derivedAtomA.debugLabel = 'derivedAtomA' + ); + derivedAtomA.debugLabel = 'derivedAtomA'; const derivedAtomB = atom( (get) => get(baseAtom), (_get, set) => set(baseAtom), - ) - derivedAtomB.debugLabel = 'derivedAtomB' + ); + derivedAtomB.debugLabel = 'derivedAtomB'; function Counter({ level }: { level: string }) { - const setBase = useSetAtom(baseAtom) - const [derivedA, setDerivedA] = useAtom(derivedAtomA) - const [derivedB, setDerivedB] = useAtom(derivedAtomB) + const setBase = useSetAtom(baseAtom); + const [derivedA, setDerivedA] = useAtom(derivedAtomA); + const [derivedB, setDerivedB] = useAtom(derivedAtomB); return (
base:{derivedA} @@ -437,17 +470,29 @@ describe('Counter', () => { {derivedA} derivedB: {derivedB} - - -
- ) + ); } function App() { @@ -460,49 +505,53 @@ describe('Counter', () => { - ) + ); } - const { container } = render() - const increaseLevel0Base = '.level0.setBase' - const increaseLevel1Base = '.level1.setBase' - const increaseLevel1DerivedA = '.level1.setDerivedA' - const increaseLevel1DerivedB = '.level1.setDerivedB' - const atomValueSelectors = ['.level0.derivedA', '.level1.derivedA', '.level1.derivedB'] + const { container } = render(); + const increaseLevel0Base = '.level0.setBase'; + const increaseLevel1Base = '.level1.setBase'; + const increaseLevel1DerivedA = '.level1.setDerivedA'; + const increaseLevel1DerivedB = '.level1.setDerivedB'; + const atomValueSelectors = [ + '.level0.derivedA', + '.level1.derivedA', + '.level1.derivedB', + ]; expect(getTextContents(container, atomValueSelectors)).toEqual([ '0', // level0 derivedA '0', // level1 derivedA '0', // level1 derivedB - ]) + ]); - clickButton(container, increaseLevel0Base) + clickButton(container, increaseLevel0Base); expect(getTextContents(container, atomValueSelectors)).toEqual([ '1', // level0 derivedA '0', // level1 derivedA '0', // level1 derivedB - ]) + ]); - clickButton(container, increaseLevel1Base) + clickButton(container, increaseLevel1Base); expect(getTextContents(container, atomValueSelectors)).toEqual([ '2', // level0 derivedA '0', // level1 derivedA '0', // level1 derivedB - ]) + ]); - clickButton(container, increaseLevel1DerivedA) + clickButton(container, increaseLevel1DerivedA); expect(getTextContents(container, atomValueSelectors)).toEqual([ '2', // level0 derivedA '1', // level1 derivedA '1', // level1 derivedB - ]) + ]); - clickButton(container, increaseLevel1DerivedB) + clickButton(container, increaseLevel1DerivedB); expect(getTextContents(container, atomValueSelectors)).toEqual([ '2', // level0 derivedA '2', // level1 derivedA '2', // level1 derivedB - ]) - }) + ]); + }); /* base, derivedA(base), derivedB(base) @@ -512,18 +561,22 @@ describe('Counter', () => { S3[base]: base3 */ test('08. nested scopes provide isolation for primitive atoms at every level', () => { - const baseAtom = atomWithReducer(0, (v) => v + 1) + const baseAtom = atomWithReducer(0, (v) => v + 1); function Counter({ level }: { level: string }) { - const [base, increaseBase] = useAtom(baseAtom) + const [base, increaseBase] = useAtom(baseAtom); return (
base:{base} -
- ) + ); } function App() { @@ -539,25 +592,41 @@ describe('Counter', () => { - ) + ); } - const { container } = render() - const increaseUnscopedBase = '.level0.setBase' - const increaseScopedBase = '.level1.setBase' - const increaseDoubleScopedBase = '.level2.setBase' - const atomValueSelectors = ['.level0.base', '.level1.base', '.level2.base'] + const { container } = render(); + const increaseUnscopedBase = '.level0.setBase'; + const increaseScopedBase = '.level1.setBase'; + const increaseDoubleScopedBase = '.level2.setBase'; + const atomValueSelectors = ['.level0.base', '.level1.base', '.level2.base']; - expect(getTextContents(container, atomValueSelectors)).toEqual(['0', '0', '0']) + expect(getTextContents(container, atomValueSelectors)).toEqual([ + '0', + '0', + '0', + ]); - clickButton(container, increaseUnscopedBase) - expect(getTextContents(container, atomValueSelectors)).toEqual(['1', '0', '0']) + clickButton(container, increaseUnscopedBase); + expect(getTextContents(container, atomValueSelectors)).toEqual([ + '1', + '0', + '0', + ]); - clickButton(container, increaseScopedBase) - expect(getTextContents(container, atomValueSelectors)).toEqual(['1', '1', '0']) + clickButton(container, increaseScopedBase); + expect(getTextContents(container, atomValueSelectors)).toEqual([ + '1', + '1', + '0', + ]); - clickButton(container, increaseDoubleScopedBase) - expect(getTextContents(container, atomValueSelectors)).toEqual(['1', '1', '1']) - }) + clickButton(container, increaseDoubleScopedBase); + expect(getTextContents(container, atomValueSelectors)).toEqual([ + '1', + '1', + '1', + ]); + }); /* baseA, baseB, baseC, derived(baseA + baseB + baseC) @@ -567,12 +636,12 @@ describe('Counter', () => { */ test('09. unscoped derived atoms in nested scoped can read and write to scoped primitive atoms at every level', async () => { function clickButtonGetResults(buttonSelector: string) { - const baseAAtom = atom(0) - baseAAtom.debugLabel = 'baseA' - const baseBAtom = atom(0) - baseBAtom.debugLabel = 'baseB' - const baseCAtom = atom(0) - baseCAtom.debugLabel = 'baseC' + const baseAAtom = atom(0); + baseAAtom.debugLabel = 'baseA'; + const baseBAtom = atom(0); + baseBAtom.debugLabel = 'baseB'; + const baseCAtom = atom(0); + baseCAtom.debugLabel = 'baseC'; const derivedAtom = atom( (get) => ({ baseA: get(baseAAtom), @@ -580,25 +649,25 @@ describe('Counter', () => { baseC: get(baseCAtom), }), (get, set) => { - set(baseAAtom, get(baseAAtom) + 1) - set(baseBAtom, get(baseBAtom) + 1) - set(baseCAtom, get(baseCAtom) + 1) + set(baseAAtom, get(baseAAtom) + 1); + set(baseBAtom, get(baseBAtom) + 1); + set(baseCAtom, get(baseCAtom) + 1); }, - ) - derivedAtom.debugLabel = 'derived' + ); + derivedAtom.debugLabel = 'derived'; function Counter({ level, baseAtom, }: { - level: string - baseAtom: WritableAtom], void> + level: string; + baseAtom: WritableAtom], void>; }) { - const setBase = useSetAtom(baseAtom) - const [{ baseA, baseB, baseC }, increaseAll] = useAtom(derivedAtom) - const valueA = useAtomValue(baseAAtom) - const valueB = useAtomValue(baseBAtom) - const valueC = useAtomValue(baseCAtom) + const setBase = useSetAtom(baseAtom); + const [{ baseA, baseB, baseC }, increaseAll] = useAtom(derivedAtom); + const valueA = useAtomValue(baseAAtom); + const valueB = useAtomValue(baseBAtom); + const valueC = useAtomValue(baseCAtom); return (
baseA:{baseA} @@ -611,7 +680,7 @@ describe('Counter', () => { className={`${level} increaseBase`} type="button" onClick={() => { - setBase((c) => c + 1) + setBase((c) => c + 1); }} > increase base @@ -620,13 +689,13 @@ describe('Counter', () => { className={`${level} increaseAll`} type="button" onClick={() => { - increaseAll() + increaseAll(); }} > increase all
- ) + ); } function App() { @@ -642,12 +711,12 @@ describe('Counter', () => { - ) + ); } - const { container } = render() - expectAllZeroes(container) - clickButton(container, buttonSelector) - return getTextContents(container, atomValueSelectors).join('') + const { container } = render(); + expectAllZeroes(container); + clickButton(container, buttonSelector); + return getTextContents(container, atomValueSelectors).join(''); } function expectAllZeroes(container: HTMLElement) { expect(getTextContents(container, atomValueSelectors).join('')).toEqual( @@ -674,7 +743,7 @@ describe('Counter', () => { '0', // valueB1 '0', // valueC2 ].join(''), - ) + ); } const atomValueSelectors = [ '.level0.baseA', @@ -695,13 +764,13 @@ describe('Counter', () => { '.level2.valueA', '.level2.valueB', '.level2.valueC', - ] - const increaseLevel0BaseA = '.level0.increaseBase' - const increaseLevel1BaseB = '.level1.increaseBase' - const increaseLevel2BaseC = '.level2.increaseBase' - const increaseLevel0All = '.level0.increaseAll' - const increaseLevel1All = '.level1.increaseAll' - const increaseLevel2All = '.level2.increaseAll' + ]; + const increaseLevel0BaseA = '.level0.increaseBase'; + const increaseLevel1BaseB = '.level1.increaseBase'; + const increaseLevel2BaseC = '.level2.increaseBase'; + const increaseLevel0All = '.level0.increaseAll'; + const increaseLevel1All = '.level1.increaseAll'; + const increaseLevel2All = '.level2.increaseAll'; expect(clickButtonGetResults(increaseLevel0BaseA)).toEqual( [ @@ -727,7 +796,7 @@ describe('Counter', () => { '0', // valueB1 '0', // valueC2 ].join(''), - ) + ); expect(clickButtonGetResults(increaseLevel1BaseB)).toEqual( [ @@ -753,7 +822,7 @@ describe('Counter', () => { '1', // valueB1 '0', // valueC2 ].join(''), - ) + ); expect(clickButtonGetResults(increaseLevel2BaseC)).toEqual( [ @@ -779,7 +848,7 @@ describe('Counter', () => { '0', // valueB1 '1', // valueC2 ].join(''), - ) + ); expect(clickButtonGetResults(increaseLevel0All)).toEqual( [ @@ -805,7 +874,7 @@ describe('Counter', () => { '0', // valueB1 '0', // valueC2 ].join(''), - ) + ); expect(clickButtonGetResults(increaseLevel1All)).toEqual( [ @@ -831,7 +900,7 @@ describe('Counter', () => { '1', // valueB1 '0', // valueC2 ].join(''), - ) + ); expect(clickButtonGetResults(increaseLevel2All)).toEqual( [ @@ -857,8 +926,8 @@ describe('Counter', () => { '1', // valueB1 '1', // valueC2 ].join(''), - ) - }) + ); + }); /* baseA, baseB, derived(baseA + baseB) @@ -866,11 +935,11 @@ describe('Counter', () => { S2[baseB]: derived1(baseA1 + baseB2) */ test('10. inherited scoped derived atoms can read and write to scoped primitive atoms at every nested level', () => { - const baseAAtom = atomWithReducer(0, (v) => v + 1) - baseAAtom.debugLabel = 'baseA' + const baseAAtom = atomWithReducer(0, (v) => v + 1); + baseAAtom.debugLabel = 'baseA'; - const baseBAtom = atomWithReducer(0, (v) => v + 1) - baseBAtom.debugLabel = 'baseB' + const baseBAtom = atomWithReducer(0, (v) => v + 1); + baseBAtom.debugLabel = 'baseB'; const derivedAtom = atom( (get) => ({ @@ -878,23 +947,27 @@ describe('Counter', () => { baseB: get(baseBAtom), }), (_get, set) => { - set(baseAAtom) - set(baseBAtom) + set(baseAAtom); + set(baseBAtom); }, - ) - derivedAtom.debugLabel = 'derived' + ); + derivedAtom.debugLabel = 'derived'; function Counter({ level }: { level: string }) { - const [{ baseA, baseB }, increaseAll] = useAtom(derivedAtom) + const [{ baseA, baseB }, increaseAll] = useAtom(derivedAtom); return (
baseA:{baseA} baseB:{baseB} -
- ) + ); } function App() { @@ -909,13 +982,18 @@ describe('Counter', () => { - ) + ); } - const { container } = render() + const { container } = render(); - const increaseLevel1All = '.level1.increaseAll' - const increaseLevel2All = '.level2.increaseAll' - const atomValueSelectors = ['.level1.baseA', '.level1.baseB', '.level2.baseA', '.level2.baseB'] + const increaseLevel1All = '.level1.increaseAll'; + const increaseLevel2All = '.level2.increaseAll'; + const atomValueSelectors = [ + '.level1.baseA', + '.level1.baseB', + '.level2.baseA', + '.level2.baseB', + ]; /* baseA, baseB, derived(baseA + baseB) @@ -927,32 +1005,32 @@ describe('Counter', () => { '0', // level1 baseB1 '0', // level2 baseA1 '0', // level2 baseB2 - ]) + ]); /* baseA, baseB, derived(baseA + baseB) S1[baseB, derived]: derived1(baseA1 + baseB1) S2[baseB]: derived1(baseA1 + baseB2) */ - clickButton(container, increaseLevel1All) + clickButton(container, increaseLevel1All); expect(getTextContents(container, atomValueSelectors)).toEqual([ '1', // level1 baseA1 '1', // level1 baseB1 '1', // level2 baseA1 '0', // level2 baseB2 - ]) + ]); /* baseA, baseB, derived(baseA + baseB) S1[baseB, derived]: derived1(baseA1 + baseB1) S2[baseB]: derived1(baseA1 + baseB2) */ - clickButton(container, increaseLevel2All) + clickButton(container, increaseLevel2All); expect(getTextContents(container, atomValueSelectors)).toEqual([ '2', // level1 baseA1 '1', // level1 baseB1 '2', // level2 baseA1 '1', // level2 baseB2 - ]) - }) -}) + ]); + }); +}); diff --git a/__tests__/ScopeProvider/02_removeScope.test.tsx b/__tests__/ScopeProvider/02_removeScope.test.tsx new file mode 100644 index 0000000..872d3ce --- /dev/null +++ b/__tests__/ScopeProvider/02_removeScope.test.tsx @@ -0,0 +1,207 @@ +import { render } from '@testing-library/react'; +import type { PropsWithChildren } from 'react'; +import { atom, useAtom, useAtomValue } from '../../jotai'; +import { atomWithReducer } from '../../jotai/vanilla/utils'; +import { ScopeProvider } from '../../src/index'; +import { clickButton, getTextContents } from '../utils'; + +const baseAtom1 = atomWithReducer(0, (v) => v + 1); +const baseAtom2 = atomWithReducer(0, (v) => v + 1); +const shouldHaveScopeAtom = atom(true); + +function Counter({ counterClass }: { counterClass: string }) { + const [base1, increaseBase1] = useAtom(baseAtom1); + const [base2, increaseBase2] = useAtom(baseAtom2); + return ( + <> +
+ base1: {base1} + +
+
+ base2: {base2} + +
+ + ); +} + +function Wrapper({ children }: PropsWithChildren) { + const shouldHaveScope = useAtomValue(shouldHaveScopeAtom); + return shouldHaveScope ? ( + {children} + ) : ( + children + ); +} + +function ScopeButton() { + const [shouldHaveScope, setShouldHaveScope] = useAtom(shouldHaveScopeAtom); + return ( + + ); +} + +function App() { + return ( +
+

Unscoped

+ +

Scoped Provider

+ + + + +
+ ); +} + +describe('Counter', () => { + test('atom get correct value when ScopeProvider is added/removed', () => { + const { container } = render(); + const increaseUnscopedBase1 = '.unscoped.setBase1'; + const increaseUnscopedBase2 = '.unscoped.setBase2'; + const increaseScopedBase1 = '.scoped.setBase1'; + const increaseScopedBase2 = '.scoped.setBase2'; + const toggleScope = '#toggleScope'; + + const atomValueSelectors = [ + '.unscoped.base1', + '.unscoped.base2', + '.scoped.base1', + '.scoped.base2', + ]; + + expect(getTextContents(container, atomValueSelectors)).toEqual([ + '0', + '0', + '0', + '0', + ]); + + clickButton(container, increaseUnscopedBase1); + expect(getTextContents(container, atomValueSelectors)).toEqual([ + '1', + '0', + '1', + '0', + ]); + + clickButton(container, increaseUnscopedBase2); + expect(getTextContents(container, atomValueSelectors)).toEqual([ + '1', + '1', + '1', + '0', + ]); + + clickButton(container, increaseScopedBase1); + expect(getTextContents(container, atomValueSelectors)).toEqual([ + '2', + '1', + '2', + '0', + ]); + + clickButton(container, increaseScopedBase2); + clickButton(container, increaseScopedBase2); + clickButton(container, increaseScopedBase2); + + expect(getTextContents(container, atomValueSelectors)).toEqual([ + '2', + '1', + '2', + '3', + ]); + + clickButton(container, toggleScope); + expect(getTextContents(container, atomValueSelectors)).toEqual([ + '2', + '1', + '2', + '1', + ]); + + clickButton(container, increaseUnscopedBase1); + expect(getTextContents(container, atomValueSelectors)).toEqual([ + '3', + '1', + '3', + '1', + ]); + + clickButton(container, increaseUnscopedBase2); + expect(getTextContents(container, atomValueSelectors)).toEqual([ + '3', + '2', + '3', + '2', + ]); + + clickButton(container, increaseScopedBase1); + expect(getTextContents(container, atomValueSelectors)).toEqual([ + '4', + '2', + '4', + '2', + ]); + + clickButton(container, increaseScopedBase2); + expect(getTextContents(container, atomValueSelectors)).toEqual([ + '4', + '3', + '4', + '3', + ]); + + clickButton(container, toggleScope); + expect(getTextContents(container, atomValueSelectors)).toEqual([ + '4', + '3', + '4', + '0', + ]); + + clickButton(container, increaseScopedBase2); + expect(getTextContents(container, atomValueSelectors)).toEqual([ + '4', + '3', + '4', + '1', + ]); + + clickButton(container, increaseScopedBase2); + expect(getTextContents(container, atomValueSelectors)).toEqual([ + '4', + '3', + '4', + '2', + ]); + + clickButton(container, increaseScopedBase2); + expect(getTextContents(container, atomValueSelectors)).toEqual([ + '4', + '3', + '4', + '3', + ]); + }); +}); diff --git a/__tests__/ScopeProvider/02_removeScope.tsx b/__tests__/ScopeProvider/02_removeScope.tsx deleted file mode 100644 index 0f7c8f1..0000000 --- a/__tests__/ScopeProvider/02_removeScope.tsx +++ /dev/null @@ -1,129 +0,0 @@ -import { render } from '@testing-library/react' -import type { PropsWithChildren } from 'react' -import { atom, useAtom, useAtomValue } from 'jotai' -import { atomWithReducer } from 'jotai/vanilla/utils' -import { ScopeProvider } from '../../src/index' -import { clickButton, getTextContents } from '../utils' - -const baseAtom1 = atomWithReducer(0, (v) => v + 1) -const baseAtom2 = atomWithReducer(0, (v) => v + 1) -const shouldHaveScopeAtom = atom(true) - -function Counter({ counterClass }: { counterClass: string }) { - const [base1, increaseBase1] = useAtom(baseAtom1) - const [base2, increaseBase2] = useAtom(baseAtom2) - return ( - <> -
- base1: {base1} - -
-
- base2: {base2} - -
- - ) -} - -function Wrapper({ children }: PropsWithChildren) { - const shouldHaveScope = useAtomValue(shouldHaveScopeAtom) - return shouldHaveScope ? {children} : children -} - -function ScopeButton() { - const [shouldHaveScope, setShouldHaveScope] = useAtom(shouldHaveScopeAtom) - return ( - - ) -} - -function App() { - return ( -
-

Unscoped

- -

Scoped Provider

- - - - -
- ) -} - -describe('Counter', () => { - test('atom get correct value when ScopeProvider is added/removed', () => { - const { container } = render() - const increaseUnscopedBase1 = '.unscoped.setBase1' - const increaseUnscopedBase2 = '.unscoped.setBase2' - const increaseScopedBase1 = '.scoped.setBase1' - const increaseScopedBase2 = '.scoped.setBase2' - const toggleScope = '#toggleScope' - - const atomValueSelectors = [ - '.unscoped.base1', - '.unscoped.base2', - '.scoped.base1', - '.scoped.base2', - ] - - expect(getTextContents(container, atomValueSelectors)).toEqual(['0', '0', '0', '0']) - - clickButton(container, increaseUnscopedBase1) - expect(getTextContents(container, atomValueSelectors)).toEqual(['1', '0', '1', '0']) - - clickButton(container, increaseUnscopedBase2) - expect(getTextContents(container, atomValueSelectors)).toEqual(['1', '1', '1', '0']) - - clickButton(container, increaseScopedBase1) - expect(getTextContents(container, atomValueSelectors)).toEqual(['2', '1', '2', '0']) - - clickButton(container, increaseScopedBase2) - clickButton(container, increaseScopedBase2) - clickButton(container, increaseScopedBase2) - - expect(getTextContents(container, atomValueSelectors)).toEqual(['2', '1', '2', '3']) - - clickButton(container, toggleScope) - expect(getTextContents(container, atomValueSelectors)).toEqual(['2', '1', '2', '1']) - - clickButton(container, increaseUnscopedBase1) - expect(getTextContents(container, atomValueSelectors)).toEqual(['3', '1', '3', '1']) - - clickButton(container, increaseUnscopedBase2) - expect(getTextContents(container, atomValueSelectors)).toEqual(['3', '2', '3', '2']) - - clickButton(container, increaseScopedBase1) - expect(getTextContents(container, atomValueSelectors)).toEqual(['4', '2', '4', '2']) - - clickButton(container, increaseScopedBase2) - expect(getTextContents(container, atomValueSelectors)).toEqual(['4', '3', '4', '3']) - - clickButton(container, toggleScope) - expect(getTextContents(container, atomValueSelectors)).toEqual(['4', '3', '4', '0']) - - clickButton(container, increaseScopedBase2) - expect(getTextContents(container, atomValueSelectors)).toEqual(['4', '3', '4', '1']) - - clickButton(container, increaseScopedBase2) - expect(getTextContents(container, atomValueSelectors)).toEqual(['4', '3', '4', '2']) - - clickButton(container, increaseScopedBase2) - expect(getTextContents(container, atomValueSelectors)).toEqual(['4', '3', '4', '3']) - }) -}) diff --git a/__tests__/ScopeProvider/03_nested.tsx b/__tests__/ScopeProvider/03_nested.test.tsx similarity index 63% rename from __tests__/ScopeProvider/03_nested.tsx rename to __tests__/ScopeProvider/03_nested.test.tsx index 36e8562..6d81fee 100644 --- a/__tests__/ScopeProvider/03_nested.tsx +++ b/__tests__/ScopeProvider/03_nested.test.tsx @@ -1,25 +1,25 @@ -import { render } from '@testing-library/react' -import { atom, useAtom, useAtomValue, useSetAtom } from 'jotai' -import { atomWithReducer } from 'jotai/vanilla/utils' -import { clickButton, getTextContents } from '../utils' +import { render } from '@testing-library/react'; +import { atom, useAtom, useAtomValue, useSetAtom } from '../../jotai'; +import { atomWithReducer } from '../../jotai/vanilla/utils'; +import { clickButton, getTextContents } from '../utils'; -import { ScopeProvider } from '../../src/index' +import { ScopeProvider } from '../../src/index'; -const baseAtom1 = atomWithReducer(0, (v) => v + 1) -const baseAtom2 = atomWithReducer(0, (v) => v + 1) -const baseAtom = atom(0) +const baseAtom1 = atomWithReducer(0, (v) => v + 1); +const baseAtom2 = atomWithReducer(0, (v) => v + 1); +const baseAtom = atom(0); const writeProxyAtom = atom('unused', (get, set) => { - set(baseAtom, get(baseAtom) + 1) - set(baseAtom1) - set(baseAtom2) -}) + set(baseAtom, get(baseAtom) + 1); + set(baseAtom1); + set(baseAtom2); +}); function Counter({ counterClass }: { counterClass: string }) { - const [base1, increaseBase1] = useAtom(baseAtom1) - const [base2, increaseBase2] = useAtom(baseAtom2) - const base = useAtomValue(baseAtom) - const increaseAll = useSetAtom(writeProxyAtom) + const [base1, increaseBase1] = useAtom(baseAtom1); + const [base2, increaseBase2] = useAtom(baseAtom2); + const base = useAtomValue(baseAtom); + const increaseAll = useSetAtom(writeProxyAtom); return ( <>
@@ -45,11 +45,15 @@ function Counter({ counterClass }: { counterClass: string }) {
base: {base}
- - ) + ); } function App() { @@ -62,13 +66,16 @@ function App() {

Layer 2: Scope base 2

-

base 1 should be shared between layer 1 and layer 2, base should be globally shared

+

+ base 1 should be shared between layer 1 and layer 2, base should be + globally shared +

- ) + ); } describe('Counter', () => { @@ -78,16 +85,16 @@ describe('Counter', () => { S2[baseB]: baseA1 baseB2 baseC0 */ test('nested primitive atoms are correctly scoped', () => { - const { container } = render() - const increaseUnscopedBase1 = '.unscoped.setBase1' - const increaseUnscopedBase2 = '.unscoped.setBase2' - const increaseAllUnscoped = '.unscoped.setAll' - const increaseLayer1Base1 = '.layer1.setBase1' - const increaseLayer1Base2 = '.layer1.setBase2' - const increaseAllLayer1 = '.layer1.setAll' - const increaseLayer2Base1 = '.layer2.setBase1' - const increaseLayer2Base2 = '.layer2.setBase2' - const increaseAllLayer2 = '.layer2.setAll' + const { container } = render(); + const increaseUnscopedBase1 = '.unscoped.setBase1'; + const increaseUnscopedBase2 = '.unscoped.setBase2'; + const increaseAllUnscoped = '.unscoped.setAll'; + const increaseLayer1Base1 = '.layer1.setBase1'; + const increaseLayer1Base2 = '.layer1.setBase2'; + const increaseAllLayer1 = '.layer1.setAll'; + const increaseLayer2Base1 = '.layer2.setBase1'; + const increaseLayer2Base2 = '.layer2.setBase2'; + const increaseAllLayer2 = '.layer2.setAll'; const atomValueSelectors = [ '.unscoped.base1', @@ -99,7 +106,7 @@ describe('Counter', () => { '.layer2.base1', '.layer2.base2', '.layer2.base', - ] + ]; expect(getTextContents(container, atomValueSelectors)).toEqual([ '0', @@ -111,9 +118,9 @@ describe('Counter', () => { '0', '0', '0', - ]) + ]); - clickButton(container, increaseUnscopedBase1) + clickButton(container, increaseUnscopedBase1); expect(getTextContents(container, atomValueSelectors)).toEqual([ '1', '0', @@ -124,9 +131,9 @@ describe('Counter', () => { '0', '0', '0', - ]) + ]); - clickButton(container, increaseUnscopedBase2) + clickButton(container, increaseUnscopedBase2); expect(getTextContents(container, atomValueSelectors)).toEqual([ '1', '1', @@ -137,9 +144,9 @@ describe('Counter', () => { '0', '0', '0', - ]) + ]); - clickButton(container, increaseAllUnscoped) + clickButton(container, increaseAllUnscoped); expect(getTextContents(container, atomValueSelectors)).toEqual([ '2', '2', @@ -150,9 +157,9 @@ describe('Counter', () => { '0', '0', '1', - ]) + ]); - clickButton(container, increaseLayer1Base1) + clickButton(container, increaseLayer1Base1); expect(getTextContents(container, atomValueSelectors)).toEqual([ '2', '2', @@ -163,9 +170,9 @@ describe('Counter', () => { '1', '0', '1', - ]) + ]); - clickButton(container, increaseLayer1Base2) + clickButton(container, increaseLayer1Base2); expect(getTextContents(container, atomValueSelectors)).toEqual([ '2', '3', @@ -176,9 +183,9 @@ describe('Counter', () => { '1', '0', '1', - ]) + ]); - clickButton(container, increaseAllLayer1) + clickButton(container, increaseAllLayer1); expect(getTextContents(container, atomValueSelectors)).toEqual([ '2', '4', @@ -189,9 +196,9 @@ describe('Counter', () => { '2', '0', '2', - ]) + ]); - clickButton(container, increaseLayer2Base1) + clickButton(container, increaseLayer2Base1); expect(getTextContents(container, atomValueSelectors)).toEqual([ '2', '4', @@ -202,9 +209,9 @@ describe('Counter', () => { '3', '0', '2', - ]) + ]); - clickButton(container, increaseLayer2Base2) + clickButton(container, increaseLayer2Base2); expect(getTextContents(container, atomValueSelectors)).toEqual([ '2', '4', @@ -215,9 +222,9 @@ describe('Counter', () => { '3', '1', '2', - ]) + ]); - clickButton(container, increaseAllLayer2) + clickButton(container, increaseAllLayer2); expect(getTextContents(container, atomValueSelectors)).toEqual([ '2', '4', @@ -228,6 +235,6 @@ describe('Counter', () => { '4', '2', '3', - ]) - }) -}) + ]); + }); +}); diff --git a/__tests__/ScopeProvider/04_derived.tsx b/__tests__/ScopeProvider/04_derived.test.tsx similarity index 88% rename from __tests__/ScopeProvider/04_derived.tsx rename to __tests__/ScopeProvider/04_derived.test.tsx index a728bc2..4974114 100644 --- a/__tests__/ScopeProvider/04_derived.tsx +++ b/__tests__/ScopeProvider/04_derived.test.tsx @@ -1,7 +1,7 @@ -import { render } from '@testing-library/react' -import { atom, useAtom } from 'jotai' -import { clickButton, getTextContents } from '../utils' -import { ScopeProvider } from '../../src/index' +import { render } from '@testing-library/react'; +import { atom, useAtom } from '../../jotai'; +import { clickButton, getTextContents } from '../utils'; +import { ScopeProvider } from '../../src/index'; const atomValueSelectors = [ '.case1.base', @@ -16,28 +16,28 @@ const atomValueSelectors = [ '.layer2.base', '.layer2.derivedA', '.layer2.derivedB', -] +]; function clickButtonGetResults(buttonSelector: string) { - const baseAtom = atom(0) + const baseAtom = atom(0); const derivedAtomA = atom( (get) => get(baseAtom), (get, set) => { - set(baseAtom, get(baseAtom) + 1) + set(baseAtom, get(baseAtom) + 1); }, - ) + ); const derivedAtomB = atom( (get) => get(baseAtom), (get, set) => { - set(baseAtom, get(baseAtom) + 1) + set(baseAtom, get(baseAtom) + 1); }, - ) + ); function Counter({ counterClass }: { counterClass: string }) { - const [base, setBase] = useAtom(baseAtom) - const [derivedA, setDerivedA] = useAtom(derivedAtomA) - const [derivedB, setDerivedB] = useAtom(derivedAtomB) + const [base, setBase] = useAtom(baseAtom); + const [derivedA, setDerivedA] = useAtom(derivedAtomA); + const [derivedB, setDerivedB] = useAtom(derivedAtomB); return ( <>
@@ -73,7 +73,7 @@ function clickButtonGetResults(buttonSelector: string) {
- ) + ); } function App() { @@ -94,19 +94,22 @@ function clickButtonGetResults(buttonSelector: string) {

Layer2: Base and derivedB are scoped

-

derivedA should use layer2's atom, base and derivedB are layer 2 scoped

+

+ derivedA should use layer2's atom, base and derivedB are layer + 2 scoped +

- ) + ); } - const { container } = render() - expectAllZeroes(container) - clickButton(container, buttonSelector) - return getTextContents(container, atomValueSelectors) + const { container } = render(); + expectAllZeroes(container); + clickButton(container, buttonSelector); + return getTextContents(container, atomValueSelectors); } function expectAllZeroes(container: HTMLElement) { @@ -130,23 +133,23 @@ function expectAllZeroes(container: HTMLElement) { '0', // base '0', // derivedA '0', // derivedB - ]) + ]); } describe('Counter', () => { test("parent scope's derived atom is prior to nested scope's scoped base", () => { - const increaseCase1Base = '.case1.setBase' - const increaseCase1DerivedA = '.case1.setDerivedA' - const increaseCase1DerivedB = '.case1.setDerivedB' - const increaseCase2Base = '.case2.setBase' - const increaseCase2DerivedA = '.case2.setDerivedA' - const increaseCase2DerivedB = '.case2.setDerivedB' - const increaseLayer1Base = '.layer1.setBase' - const increaseLayer1DerivedA = '.layer1.setDerivedA' - const increaseLayer1DerivedB = '.layer1.setDerivedB' - const increaseLayer2Base = '.layer2.setBase' - const increaseLayer2DerivedA = '.layer2.setDerivedA' - const increaseLayer2DerivedB = '.layer2.setDerivedB' + const increaseCase1Base = '.case1.setBase'; + const increaseCase1DerivedA = '.case1.setDerivedA'; + const increaseCase1DerivedB = '.case1.setDerivedB'; + const increaseCase2Base = '.case2.setBase'; + const increaseCase2DerivedA = '.case2.setDerivedA'; + const increaseCase2DerivedB = '.case2.setDerivedB'; + const increaseLayer1Base = '.layer1.setBase'; + const increaseLayer1DerivedA = '.layer1.setDerivedA'; + const increaseLayer1DerivedB = '.layer1.setDerivedB'; + const increaseLayer2Base = '.layer2.setBase'; + const increaseLayer2DerivedA = '.layer2.setDerivedA'; + const increaseLayer2DerivedB = '.layer2.setDerivedB'; /* base, derivedA(base), derivedB(base) @@ -175,7 +178,7 @@ describe('Counter', () => { '0', // base '0', // derivedA '0', // derivedB - ]) + ]); /* base, derivedA(base), derivedB(base) @@ -204,7 +207,7 @@ describe('Counter', () => { '0', // base '0', // derivedA '0', // derivedB - ]) + ]); /* base, derivedA(base), derivedB(base) @@ -233,7 +236,7 @@ describe('Counter', () => { '0', // base '0', // derivedA '0', // derivedB - ]) + ]); /* base, derivedA(base), derivedB(base) @@ -262,7 +265,7 @@ describe('Counter', () => { '0', // base '0', // derivedA '0', // derivedB - ]) + ]); /* base, derivedA(base), derivedB(base) @@ -291,7 +294,7 @@ describe('Counter', () => { '0', // base actual: 0, '0', // derivedA actual: 0, '0', // derivedB actual: 0 - ]) + ]); /* base, derivedA(base), derivedB(base) @@ -320,7 +323,7 @@ describe('Counter', () => { '0', // base '0', // derivedA '0', // derivedB - ]) + ]); /* base, derivedA(base), derivedB(base) @@ -349,7 +352,7 @@ describe('Counter', () => { '0', // base '0', // derivedA '0', // derivedB - ]) + ]); /* base, derivedA(base), derivedB(base) @@ -378,7 +381,7 @@ describe('Counter', () => { '0', // base '0', // derivedA '0', // derivedB - ]) + ]); /* base, derivedA(base), derivedB(base) @@ -407,7 +410,7 @@ describe('Counter', () => { '0', // base '0', // derivedA '0', // derivedB - ]) + ]); /* base, derivedA(base), derivedB(base) @@ -436,7 +439,7 @@ describe('Counter', () => { '1', // base '1', // derivedA '1', // derivedB - ]) + ]); /* base, derivedA(base), derivedB(base) @@ -465,7 +468,7 @@ describe('Counter', () => { '1', // base '1', // derivedA '1', // derivedB - ]) + ]); /* base, derivedA(base), derivedB(base) @@ -494,6 +497,6 @@ describe('Counter', () => { '1', // base '1', // derivedA '1', // derivedB - ]) - }) -}) + ]); + }); +}); diff --git a/__tests__/ScopeProvider/05_derived_self.test.tsx b/__tests__/ScopeProvider/05_derived_self.test.tsx new file mode 100644 index 0000000..7a6117a --- /dev/null +++ b/__tests__/ScopeProvider/05_derived_self.test.tsx @@ -0,0 +1,63 @@ +import { render } from '@testing-library/react'; +import { atom, useAtom } from '../../jotai'; +import { useHydrateAtoms } from '../../jotai/utils'; +import { getTextContents } from '../utils'; +import { ScopeProvider } from '../../src/index'; + +const baseAtom = atom(0); +const derivedAtom1 = atom( + (get) => get(baseAtom), + (get): number => { + return get(derivedAtom1); + }, +); + +function Component({ + className, + initialValue = 0, +}: { + className: string; + initialValue?: number; +}) { + useHydrateAtoms([[baseAtom, initialValue]]); + const [atom1ReadValue, setAtom1Value] = useAtom(derivedAtom1); + const atom1WriteValue = setAtom1Value(); + return ( +
+ {atom1ReadValue} + {atom1WriteValue} +
+ ); +} + +function App() { + return ( + <> +

base component

+

derived1 should read itself from global scope

+ + +

scoped component

+

derived1 should read itself from scoped scope

+ +
+ + ); +} + +describe('Self', () => { + /* + baseA, derivedB(baseA, derivedB) + S1[baseA]: baseA1, derivedB0(baseA1, derivedB0) + */ + test('derived dep scope is preserved in self reference', () => { + const { container } = render(); + expect( + getTextContents(container, ['.unscoped .read', '.unscoped .write']), + ).toEqual(['0', '0']); + + expect( + getTextContents(container, ['.scoped .read', '.scoped .write']), + ).toEqual(['1', '1']); + }); +}); diff --git a/__tests__/ScopeProvider/05_derived_self.tsx b/__tests__/ScopeProvider/05_derived_self.tsx deleted file mode 100644 index 0f8e190..0000000 --- a/__tests__/ScopeProvider/05_derived_self.tsx +++ /dev/null @@ -1,53 +0,0 @@ -import { render } from '@testing-library/react' -import { atom, useAtom } from 'jotai' -import { useHydrateAtoms } from 'jotai/utils' -import { getTextContents } from '../utils' -import { ScopeProvider } from '../../src/index' - -const baseAtom = atom(0) -const derivedAtom1 = atom( - (get) => get(baseAtom), - (get): number => { - return get(derivedAtom1) - }, -) - -function Component({ className, initialValue = 0 }: { className: string; initialValue?: number }) { - useHydrateAtoms([[baseAtom, initialValue]]) - const [atom1ReadValue, setAtom1Value] = useAtom(derivedAtom1) - const atom1WriteValue = setAtom1Value() - return ( -
- {atom1ReadValue} - {atom1WriteValue} -
- ) -} - -function App() { - return ( - <> -

base component

-

derived1 should read itself from global scope

- - -

scoped component

-

derived1 should read itself from scoped scope

- -
- - ) -} - -describe('Self', () => { - /* - baseA, derivedB(baseA, derivedB) - S1[baseA]: baseA1, derivedB0(baseA1, derivedB0) - */ - test('derived dep scope is preserved in self reference', () => { - const { container } = render() - expect(getTextContents(container, ['.unscoped .read', '.unscoped .write'])).toEqual(['0', '0']) - - expect(getTextContents(container, ['.scoped .read', '.scoped .write'])).toEqual(['1', '1']) - }) -}) diff --git a/__tests__/ScopeProvider/06_implicit_parent.tsx b/__tests__/ScopeProvider/06_implicit_parent.test.tsx similarity index 50% rename from __tests__/ScopeProvider/06_implicit_parent.tsx rename to __tests__/ScopeProvider/06_implicit_parent.test.tsx index 95b1a10..366dab1 100644 --- a/__tests__/ScopeProvider/06_implicit_parent.tsx +++ b/__tests__/ScopeProvider/06_implicit_parent.test.tsx @@ -1,31 +1,35 @@ -import type { FC } from 'react' -import { render } from '@testing-library/react' -import { atom, useAtom, useAtomValue } from 'jotai' -import { atomWithReducer } from 'jotai/vanilla/utils' -import { clickButton, getTextContents } from '../utils' -import { ScopeProvider } from '../../src/index' +import type { FC } from 'react'; +import { render } from '@testing-library/react'; +import { atom, useAtom, useAtomValue } from '../../jotai'; +import { atomWithReducer } from '../../jotai/vanilla/utils'; +import { clickButton, getTextContents } from '../utils'; +import { ScopeProvider } from '../../src/index'; function renderWithOrder(level1: 'BD' | 'DB', level2: 'BD' | 'DB') { - const baseAtom = atomWithReducer(0, (v) => v + 1) - baseAtom.debugLabel = 'baseAtom' + const baseAtom = atomWithReducer(0, (v) => v + 1); + baseAtom.debugLabel = 'baseAtom'; baseAtom.toString = function toString() { - return this.debugLabel ?? 'Unknown Atom' - } + return this.debugLabel ?? 'Unknown Atom'; + }; - const derivedAtom = atom((get) => get(baseAtom)) - derivedAtom.debugLabel = 'derivedAtom' + const derivedAtom = atom((get) => get(baseAtom)); + derivedAtom.debugLabel = 'derivedAtom'; derivedAtom.toString = function toString() { - return this.debugLabel ?? 'Unknown Atom' - } + return this.debugLabel ?? 'Unknown Atom'; + }; function BaseThenDerived({ level }: { level: string }) { - const [base, increaseBase] = useAtom(baseAtom) - const derived = useAtomValue(derivedAtom) + const [base, increaseBase] = useAtom(baseAtom); + const derived = useAtomValue(derivedAtom); return ( <>
base: {base} -
@@ -33,17 +37,21 @@ function renderWithOrder(level1: 'BD' | 'DB', level2: 'BD' | 'DB') { derived:{derived} - ) + ); } function DerivedThenBase({ level }: { level: string }) { - const derived = useAtomValue(derivedAtom) - const [base, increaseBase] = useAtom(baseAtom) + const derived = useAtomValue(derivedAtom); + const [base, increaseBase] = useAtom(baseAtom); return ( <>
base:{base} -
@@ -51,13 +59,13 @@ function renderWithOrder(level1: 'BD' | 'DB', level2: 'BD' | 'DB') { derived:{derived} - ) + ); } function App(props: { - Level1Counter: FC<{ level: string }> - Level2Counter: FC<{ level: string }> + Level1Counter: FC<{ level: string }>; + Level2Counter: FC<{ level: string }>; }) { - const { Level1Counter, Level2Counter } = props + const { Level1Counter, Level2Counter } = props; return (

Layer 1: Scope derived

@@ -71,12 +79,17 @@ function renderWithOrder(level1: 'BD' | 'DB', level2: 'BD' | 'DB') {
- ) + ); } function getCounter(order: 'BD' | 'DB') { - return order === 'BD' ? BaseThenDerived : DerivedThenBase + return order === 'BD' ? BaseThenDerived : DerivedThenBase; } - return render() + return render( + , + ); } /* @@ -90,15 +103,20 @@ describe('Implicit parent does not affect unscoped', () => { ['BD', 'DB'], ['DB', 'BD'], ['DB', 'DB'], - ] as const + ] as const; test.each(cases)('level 1: %p and level 2: %p', (level1, level2) => { - const { container } = renderWithOrder(level1, level2) - const increaseLayer2Base = '.layer2.setBase' - const selectors = ['.layer1.base', '.layer1.derived', '.layer2.base', '.layer2.derived'] + const { container } = renderWithOrder(level1, level2); + const increaseLayer2Base = '.layer2.setBase'; + const selectors = [ + '.layer1.base', + '.layer1.derived', + '.layer2.base', + '.layer2.derived', + ]; - expect(getTextContents(container, selectors).join('')).toEqual('0000') + expect(getTextContents(container, selectors).join('')).toEqual('0000'); - clickButton(container, increaseLayer2Base) - expect(getTextContents(container, selectors).join('')).toEqual('1010') - }) -}) + clickButton(container, increaseLayer2Base); + expect(getTextContents(container, selectors).join('')).toEqual('1010'); + }); +}); diff --git a/__tests__/ScopeProvider/07_writable.tsx b/__tests__/ScopeProvider/07_writable.test.tsx similarity index 64% rename from __tests__/ScopeProvider/07_writable.tsx rename to __tests__/ScopeProvider/07_writable.test.tsx index b1e4c9f..f2fd868 100644 --- a/__tests__/ScopeProvider/07_writable.tsx +++ b/__tests__/ScopeProvider/07_writable.test.tsx @@ -1,33 +1,42 @@ -import { render } from '@testing-library/react' -import { type WritableAtom, type PrimitiveAtom, atom, useAtom } from 'jotai' -import { clickButton, getTextContents } from '../utils' -import { ScopeProvider } from '../../src/index' +import { render } from '@testing-library/react'; +import { + type WritableAtom, + type PrimitiveAtom, + atom, + useAtom, +} from '../../jotai'; +import { clickButton, getTextContents } from '../utils'; +import { ScopeProvider } from '../../src/index'; -let baseAtom: PrimitiveAtom +let baseAtom: PrimitiveAtom; -type WritableNumberAtom = WritableAtom +type WritableNumberAtom = WritableAtom; const writableAtom: WritableNumberAtom = atom(0, (get, set, value = 0) => { - set(writableAtom, get(writableAtom) + get(baseAtom) + value) -}) + set(writableAtom, get(writableAtom) + get(baseAtom) + value); +}); const thisWritableAtom: WritableNumberAtom = atom( 0, function write(this: WritableNumberAtom, get, set, value = 0) { - set(this, get(this) + get(baseAtom) + value) + set(this, get(this) + get(baseAtom) + value); }, -) +); function renderTest(targetAtom: WritableNumberAtom) { - baseAtom = atom(0) + baseAtom = atom(0); function Component({ level }: { level: string }) { - const [value, increaseWritable] = useAtom(targetAtom) - const [baseValue, increaseBase] = useAtom(baseAtom) + const [value, increaseWritable] = useAtom(targetAtom); + const [baseValue, increaseBase] = useAtom(baseAtom); return (
{value}
{baseValue}
-
- ) + ); } function App() { @@ -49,14 +58,15 @@ function renderTest(targetAtom: WritableNumberAtom) {

scoped

- writable atom should update its value in both scoped and unscoped and read scoped atom + writable atom should update its value in both scoped and unscoped + and read scoped atom

- ) + ); } - return render() + return render(); } /* @@ -68,15 +78,21 @@ describe('Self', () => { test.each(['writableAtom', 'thisWritableAtom'])( '%p updates its value in both scoped and unscoped and read scoped atom', (atomKey) => { - const target = atomKey === 'writableAtom' ? writableAtom : thisWritableAtom - const { container } = renderTest(target) - - const increaseLevel0BaseAtom = '.level0 .writeBase' - const increaseLevel0Writable = '.level0 .write' - const increaseLevel1BaseAtom = '.level1 .writeBase' - const increaseLevel1Writable = '.level1 .write' - - const selectors = ['.level0 .readBase', '.level0 .read', '.level1 .readBase', '.level1 .read'] + const target = + atomKey === 'writableAtom' ? writableAtom : thisWritableAtom; + const { container } = renderTest(target); + + const increaseLevel0BaseAtom = '.level0 .writeBase'; + const increaseLevel0Writable = '.level0 .write'; + const increaseLevel1BaseAtom = '.level1 .writeBase'; + const increaseLevel1Writable = '.level1 .write'; + + const selectors = [ + '.level0 .readBase', + '.level0 .read', + '.level1 .readBase', + '.level1 .read', + ]; // all initial values are zero expect(getTextContents(container, selectors)).toEqual([ @@ -84,63 +100,63 @@ describe('Self', () => { '0', // level0 read '0', // level1 readBase '0', // level1 read - ]) + ]); // level0 base atom updates its value to 1 - clickButton(container, increaseLevel0BaseAtom) + clickButton(container, increaseLevel0BaseAtom); expect(getTextContents(container, selectors)).toEqual([ '1', // level0 readBase '0', // level0 read '0', // level1 readBase '0', // level1 read - ]) + ]); // level0 writable atom increases its value, level1 writable atom shares the same value - clickButton(container, increaseLevel0Writable) + clickButton(container, increaseLevel0Writable); expect(getTextContents(container, selectors)).toEqual([ '1', // level0 readBase '1', // level0 read '0', // level1 readBase '1', // level1 read - ]) + ]); // level1 writable atom increases its value, // but since level1 base atom is zero, // level0 and level1 writable atoms value should not change - clickButton(container, increaseLevel1Writable) + clickButton(container, increaseLevel1Writable); expect(getTextContents(container, selectors)).toEqual([ '1', // level0 readBase '1', // level0 read '0', // level1 readBase '1', // level1 read - ]) + ]); // level1 base atom updates its value to 10 - clickButton(container, increaseLevel1BaseAtom) + clickButton(container, increaseLevel1BaseAtom); expect(getTextContents(container, selectors)).toEqual([ '1', // level0 readBase '1', // level0 read '10', // level1 readBase '1', // level1 read - ]) + ]); // level0 writable atom increases its value using level0 base atom - clickButton(container, increaseLevel0Writable) + clickButton(container, increaseLevel0Writable); expect(getTextContents(container, selectors)).toEqual([ '1', // level0 readBase '2', // level0 read '10', // level1 readBase '2', // level1 read - ]) + ]); // level1 writable atom increases its value using level1 base atom - clickButton(container, increaseLevel1Writable) + clickButton(container, increaseLevel1Writable); expect(getTextContents(container, selectors)).toEqual([ '1', // level0 readBase '12', // level0 read '10', // level1 readBase '12', // level1 read - ]) + ]); }, - ) -}) + ); +}); diff --git a/__tests__/ScopeProvider/08_family.tsx b/__tests__/ScopeProvider/08_family.test.tsx similarity index 71% rename from __tests__/ScopeProvider/08_family.tsx rename to __tests__/ScopeProvider/08_family.test.tsx index 67b5bcd..e00586b 100644 --- a/__tests__/ScopeProvider/08_family.tsx +++ b/__tests__/ScopeProvider/08_family.test.tsx @@ -1,8 +1,8 @@ -import { render, act } from '@testing-library/react' -import { useAtom, atom, useSetAtom } from 'jotai' -import { atomFamily, atomWithReducer } from 'jotai/utils' -import { ScopeProvider } from '../../src/index' -import { clickButton, getTextContents } from '../utils' +import { render, act } from '@testing-library/react'; +import { useAtom, atom, useSetAtom } from '../../jotai'; +import { atomFamily, atomWithReducer } from '../../jotai/utils'; +import { ScopeProvider } from '../../src/index'; +import { clickButton, getTextContents } from '../utils'; describe('AtomFamily with ScopeProvider', () => { /* @@ -11,13 +11,13 @@ describe('AtomFamily with ScopeProvider', () => { S1[aFamily]: a1 b1 */ test('01. Scoped atom families provide isolated state', () => { - const aFamily = atomFamily(() => atom(0)) - const aAtom = aFamily('a') - aAtom.debugLabel = 'aAtom' - const bAtom = aFamily('b') - bAtom.debugLabel = 'bAtom' + const aFamily = atomFamily(() => atom(0)); + const aAtom = aFamily('a'); + aAtom.debugLabel = 'aAtom'; + const bAtom = aFamily('b'); + bAtom.debugLabel = 'bAtom'; function Counter({ level, param }: { level: string; param: string }) { - const [value, setValue] = useAtom(aFamily(param)) + const [value, setValue] = useAtom(aFamily(param)); return (
{param}:{value} @@ -29,7 +29,7 @@ describe('AtomFamily with ScopeProvider', () => { increase
- ) + ); } function App() { @@ -44,43 +44,43 @@ describe('AtomFamily with ScopeProvider', () => { - ) + ); } - const { container } = render() - const selectors = ['.level0.a', '.level0.b', '.level1.a', '.level1.b'] + const { container } = render(); + const selectors = ['.level0.a', '.level0.b', '.level1.a', '.level1.b']; expect(getTextContents(container, selectors)).toEqual([ '0', // level0 a '0', // level0 b '0', // level1 a '0', // level1 b - ]) + ]); - clickButton(container, '.level0.set-a') + clickButton(container, '.level0.set-a'); expect(getTextContents(container, selectors)).toEqual([ '1', // level0 a '0', // level0 b '0', // level1 a '0', // level1 b - ]) + ]); - clickButton(container, '.level1.set-a') + clickButton(container, '.level1.set-a'); expect(getTextContents(container, selectors)).toEqual([ '1', // level0 a '0', // level0 b '1', // level1 a '0', // level1 b - ]) + ]); - clickButton(container, '.level1.set-b') + clickButton(container, '.level1.set-b'); expect(getTextContents(container, selectors)).toEqual([ '1', // level0 a '0', // level0 b '1', // level1 a '1', // level1 b - ]) - }) + ]); + }); /* aFamily('a'), aFamily.remove('a') @@ -89,14 +89,14 @@ describe('AtomFamily with ScopeProvider', () => { */ // TODO: refactor atomFamily to support descoping removing atoms test.skip('02. Removing atom from atomFamily does not affect scoped state', () => { - const aFamily = atomFamily(() => atom(0)) - const atomA = aFamily('a') - atomA.debugLabel = 'atomA' - const rerenderAtom = atomWithReducer(0, (s) => s + 1) - rerenderAtom.debugLabel = 'rerenderAtom' + const aFamily = atomFamily(() => atom(0)); + const atomA = aFamily('a'); + atomA.debugLabel = 'atomA'; + const rerenderAtom = atomWithReducer(0, (s) => s + 1); + rerenderAtom.debugLabel = 'rerenderAtom'; function Counter({ level, param }: { level: string; param: string }) { - const [value, setValue] = useAtom(atomA) - useAtom(rerenderAtom) + const [value, setValue] = useAtom(atomA); + useAtom(rerenderAtom); return (
{param}:{value} @@ -108,11 +108,11 @@ describe('AtomFamily with ScopeProvider', () => { increase
- ) + ); } function App() { - const rerender = useSetAtom(rerenderAtom) + const rerender = useSetAtom(rerenderAtom); return (

Unscoped

@@ -121,8 +121,8 @@ describe('AtomFamily with ScopeProvider', () => { className="remove-atom" type="button" onClick={() => { - aFamily.remove('a') - rerender() + aFamily.remove('a'); + rerender(); }} > remove a from atomFamily @@ -132,38 +132,38 @@ describe('AtomFamily with ScopeProvider', () => {
- ) + ); } - const { container } = render() - const selectors = ['.level0.a', '.level1.a'] + const { container } = render(); + const selectors = ['.level0.a', '.level1.a']; expect(getTextContents(container, selectors)).toEqual([ '0', // level0 a '0', // level1 a - ]) + ]); - clickButton(container, '.level0.set-a') + clickButton(container, '.level0.set-a'); expect(getTextContents(container, selectors)).toEqual([ '1', // level0 a '0', // level1 a - ]) + ]); act(() => { - clickButton(container, '.remove-atom') - }) + clickButton(container, '.remove-atom'); + }); expect(getTextContents(container, ['.level0.a', '.level1.a'])).toEqual([ '1', // level0 a '1', // level1 a // atomA is now unscoped - ]) + ]); - clickButton(container, '.level1.set-a') + clickButton(container, '.level1.set-a'); expect(getTextContents(container, ['.level0.a', '.level1.a'])).toEqual([ '2', // level0 a '2', // level1 a - ]) - }) + ]); + }); /* aFamily.setShouldRemove((createdAt, param) => param === 'b') @@ -172,17 +172,17 @@ describe('AtomFamily with ScopeProvider', () => { */ // TODO: refactor atomFamily to support descoping removing atoms test.skip('03. Scoped atom families respect custom removal conditions', () => { - const aFamily = atomFamily(() => atom(0)) - const atomA = aFamily('a') - atomA.debugLabel = 'atomA' - const atomB = aFamily('b') - atomB.debugLabel = 'atomB' - const rerenderAtom = atomWithReducer(0, (s) => s + 1) - rerenderAtom.debugLabel = 'rerenderAtom' + const aFamily = atomFamily(() => atom(0)); + const atomA = aFamily('a'); + atomA.debugLabel = 'atomA'; + const atomB = aFamily('b'); + atomB.debugLabel = 'atomB'; + const rerenderAtom = atomWithReducer(0, (s) => s + 1); + rerenderAtom.debugLabel = 'rerenderAtom'; function Counter({ level, param }: { level: string; param: string }) { - const [value, setValue] = useAtom(aFamily(param)) - useAtom(rerenderAtom) + const [value, setValue] = useAtom(aFamily(param)); + useAtom(rerenderAtom); return (
{param}:{value} @@ -194,19 +194,19 @@ describe('AtomFamily with ScopeProvider', () => { increase
- ) + ); } function App() { - const rerender = useSetAtom(rerenderAtom) + const rerender = useSetAtom(rerenderAtom); return (
- ) + ); } - const { container } = render() - const removeBButton = '.remove-b' - const selectors = ['.level0.a', '.level0.b', '.level1.a', '.level1.b'] + const { container } = render(); + const removeBButton = '.remove-b'; + const selectors = ['.level0.a', '.level0.b', '.level1.a', '.level1.b']; expect(getTextContents(container, selectors)).toEqual([ '0', // level0 a '0', // level0 b '0', // level1 a '0', // level1 b - ]) + ]); - clickButton(container, '.level0.set-a') - clickButton(container, '.level0.set-b') + clickButton(container, '.level0.set-a'); + clickButton(container, '.level0.set-b'); expect(getTextContents(container, selectors)).toEqual([ '1', // level0 a '1', // level0 b '0', // level1 a // a is scoped '0', // level1 b // b is scoped - ]) + ]); act(() => { - clickButton(container, removeBButton) - }) + clickButton(container, removeBButton); + }); expect(getTextContents(container, selectors)).toEqual([ '1', // level0 a '1', // level0 b '0', // level1 a // a is still scoped '1', // level1 b // b is no longer scoped - ]) - }) -}) + ]); + }); +}); diff --git a/__tests__/ScopeProvider/09_mount.test.tsx b/__tests__/ScopeProvider/09_mount.test.tsx new file mode 100644 index 0000000..4a034e7 --- /dev/null +++ b/__tests__/ScopeProvider/09_mount.test.tsx @@ -0,0 +1,77 @@ +import { useState } from 'react'; +import { render, act } from '@testing-library/react'; +import { atom, useAtomValue } from '../../jotai'; +import { ScopeProvider } from '../../src/index'; +import { clickButton } from '../utils'; + +describe('ScopeProvider', () => { + it('mounts and unmounts successfully', () => { + const baseAtom = atom(0); + function Component() { + const base = useAtomValue(baseAtom); + return
{base}
; + } + function App() { + const [isMounted, setIsMounted] = useState(false); + return ( + <> +
+ +
+ {isMounted && ( + + + + )} + + ); + } + const { unmount, container } = render(); + const mountButton = '.mount'; + const base = '.base'; + + act(() => clickButton(container, mountButton)); + expect(container.querySelector(base)).not.toBe(null); + act(() => clickButton(container, mountButton)); + expect(container.querySelector(base)).toBe(null); + act(() => clickButton(container, mountButton)); + unmount(); + expect(container.querySelector(base)).toBe(null); + }); +}); + +it('computed atom mounts once for the unscoped and once for the scoped', () => { + const baseAtom = atom(0); + const deriveAtom = atom( + (get) => get(baseAtom), + () => {}, + ); + const onUnmount = jest.fn(); + const onMount = jest.fn(() => onUnmount); + deriveAtom.onMount = onMount; + function Component() { + return useAtomValue(deriveAtom); + } + function App() { + return ( + <> + + + + + + + + ); + } + const { unmount } = render(); + expect(onMount).toHaveBeenCalledTimes(2); + unmount(); + expect(onUnmount).toHaveBeenCalledTimes(2); +}); diff --git a/__tests__/ScopeProvider/09_scope_provider.tsx b/__tests__/ScopeProvider/09_scope_provider.tsx deleted file mode 100644 index f223c20..0000000 --- a/__tests__/ScopeProvider/09_scope_provider.tsx +++ /dev/null @@ -1,43 +0,0 @@ -import { useState } from 'react' -import { render, act } from '@testing-library/react' -import { atom, useAtomValue } from 'jotai' -import { ScopeProvider } from '../../src/index' -import { clickButton } from '../utils' - -describe('ScopeProvider', () => { - it('mounts and unmounts successfully', () => { - const baseAtom = atom(0) - function Component() { - const base = useAtomValue(baseAtom) - return
{base}
- } - function App() { - const [isMounted, setIsMounted] = useState(false) - return ( - <> -
- -
- {isMounted && ( - - - - )} - - ) - } - const { unmount, container } = render() - const mountButton = '.mount' - const base = '.base' - - act(() => clickButton(container, mountButton)) - expect(container.querySelector(base)).not.toBe(null) - act(() => clickButton(container, mountButton)) - expect(container.querySelector(base)).toBe(null) - act(() => clickButton(container, mountButton)) - unmount() - expect(container.querySelector(base)).toBe(null) - }) -}) diff --git a/__tests__/createIsolation/01_basic_spec.test.tsx b/__tests__/createIsolation/01_basic_spec.test.tsx new file mode 100644 index 0000000..db63706 --- /dev/null +++ b/__tests__/createIsolation/01_basic_spec.test.tsx @@ -0,0 +1,7 @@ +import { createIsolation } from '../../src/index'; + +describe('basic spec', () => { + it('should export functions', () => { + expect(createIsolation).toBeDefined(); + }); +}); diff --git a/__tests__/createIsolation/01_basic_spec.tsx b/__tests__/createIsolation/01_basic_spec.tsx deleted file mode 100644 index f7f96da..0000000 --- a/__tests__/createIsolation/01_basic_spec.tsx +++ /dev/null @@ -1,7 +0,0 @@ -import { createIsolation } from '../../src/index' - -describe('basic spec', () => { - it('should export functions', () => { - expect(createIsolation).toBeDefined() - }) -}) diff --git a/__tests__/utils.ts b/__tests__/utils.ts index d08ffa0..aa3d10d 100644 --- a/__tests__/utils.ts +++ b/__tests__/utils.ts @@ -1,23 +1,67 @@ -import { fireEvent } from '@testing-library/react' +import { fireEvent } from '@testing-library/react'; +import { + INTERNAL_DevStoreRev4, + INTERNAL_PrdStore, +} from '../jotai/vanilla/store'; +import { Store } from 'src/ScopeProvider/types'; -function getElements(container: HTMLElement, querySelectors: string[]): Element[] { +function getElements( + container: HTMLElement, + querySelectors: string[], +): Element[] { return querySelectors.map((querySelector) => { - const element = container.querySelector(querySelector) + const element = container.querySelector(querySelector); if (!element) { - throw new Error(`Element not found: ${querySelector}`) + throw new Error(`Element not found: ${querySelector}`); } - return element - }) + return element; + }); } -export function getTextContents(container: HTMLElement, selectors: string[]): string[] { - return getElements(container, selectors).map((element) => element.textContent!) +export function getTextContents( + container: HTMLElement, + selectors: string[], +): string[] { + return getElements(container, selectors).map( + (element) => element.textContent!, + ); } export function clickButton(container: HTMLElement, querySelector: string) { - const button = container.querySelector(querySelector) + const button = container.querySelector(querySelector); if (!button) { - throw new Error(`Button not found: ${querySelector}`) + throw new Error(`Button not found: ${querySelector}`); } - fireEvent.click(button) + fireEvent.click(button); +} + +export function getDevStore( + store: Store, +): INTERNAL_PrdStore & INTERNAL_DevStoreRev4 { + if (!isDevStore(store)) { + throw new Error('Store is not a dev store'); + } + return store; +} + +export function isDevStore( + store: Store, +): store is INTERNAL_PrdStore & INTERNAL_DevStoreRev4 { + return ( + 'dev4_get_internal_weak_map' in store && + 'dev4_get_mounted_atoms' in store && + 'dev4_restore_atoms' in store + ); +} + +export function assertIsDevStore( + store: Store, +): asserts store is INTERNAL_PrdStore & INTERNAL_DevStoreRev4 { + if (!isDevStore(store)) { + throw new Error('Store is not a dev store'); + } +} + +export function delay(ms: number) { + return new Promise((resolve) => setTimeout(resolve, ms)); } diff --git a/package.json b/package.json index 354addb..4869ae4 100644 --- a/package.json +++ b/package.json @@ -44,8 +44,8 @@ "jest": { "testEnvironment": "jsdom", "preset": "ts-jest/presets/js-with-ts", - "testPathIgnorePatterns": [ - "utils.ts" + "testMatch": [ + "**/__tests__/**/*.test.*" ] }, "keywords": [ @@ -59,6 +59,7 @@ "@testing-library/react": "^14.0.0", "@testing-library/user-event": "^14.5.1", "@types/jest": "^29.5.6", + "@types/minimalistic-assert": "^1.0.3", "@types/node": "^20.8.7", "@types/react": "^18.2.31", "@types/react-dom": "^18.2.14", @@ -75,8 +76,8 @@ "html-webpack-plugin": "^5.5.3", "jest": "^29.7.0", "jest-environment-jsdom": "^29.7.0", - "jotai": "2.9.2", "microbundle": "^0.15.1", + "minimalistic-assert": "^1.0.1", "npm-run-all": "^4.1.5", "prettier": "^3.0.3", "react": "^18.2.0", @@ -90,7 +91,7 @@ "webpack-dev-server": "^4.15.1" }, "peerDependencies": { - "jotai": ">=2.9.2", + "jotai": ">=2.9.3", "react": ">=17.0.0" } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 6b6cbad..4a0209d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -4,6 +4,11 @@ settings: autoInstallPeers: true excludeLinksFromLockfile: false +dependencies: + jotai: + specifier: '>=2.9.3' + version: 2.9.3(@types/react@18.2.31)(react@18.2.0) + devDependencies: '@testing-library/dom': specifier: ^9.3.3 @@ -17,6 +22,9 @@ devDependencies: '@types/jest': specifier: ^29.5.6 version: 29.5.6 + '@types/minimalistic-assert': + specifier: ^1.0.3 + version: 1.0.3 '@types/node': specifier: ^20.8.7 version: 20.8.7 @@ -65,12 +73,12 @@ devDependencies: jest-environment-jsdom: specifier: ^29.7.0 version: 29.7.0 - jotai: - specifier: 2.9.2 - version: 2.9.2(@types/react@18.2.31)(react@18.2.0) microbundle: specifier: ^0.15.1 version: 0.15.1 + minimalistic-assert: + specifier: ^1.0.1 + version: 1.0.1 npm-run-all: specifier: ^4.1.5 version: 4.1.5 @@ -2102,6 +2110,10 @@ packages: resolution: {integrity: sha512-i8MBln35l856k5iOhKk2XJ4SeAWg75mLIpZB4v6imOagKL6twsukBZGDMNhdOVk7yRFTMPpfILocMos59Q1otQ==} dev: true + /@types/minimalistic-assert@1.0.3: + resolution: {integrity: sha512-Ku87cam4YxiXcEpeUemo+vO8QWGQ7U2CwEEcLcYFhxG8b4CK8gWxSX/oWjePWKwqPaWWxxVqXAdAjGdlJtVzDA==} + dev: true + /@types/node@20.8.7: resolution: {integrity: sha512-21TKHHh3eUHIi2MloeptJWALuCu5H7HQTdTrWIFReA8ad+aggoX+lRes3ex7/FtpC+sVUpFMQ+QTfYr74mruiQ==} dependencies: @@ -2114,7 +2126,6 @@ packages: /@types/prop-types@15.7.9: resolution: {integrity: sha512-n1yyPsugYNSmHgxDFjicaI2+gCNjsBck8UX9kuofAKlc0h1bL+20oSF72KeNaW2DUlesbEVCFgyV2dPGTiY42g==} - dev: true /@types/qs@6.9.9: resolution: {integrity: sha512-wYLxw35euwqGvTDx6zfY1vokBFnsK0HNrzc6xNHchxfO2hpuRg74GbkEW7e3sSmPvj0TjCDT1VCa6OtHXnubsg==} @@ -2136,7 +2147,6 @@ packages: '@types/prop-types': 15.7.9 '@types/scheduler': 0.16.5 csstype: 3.1.2 - dev: true /@types/resolve@1.17.1: resolution: {integrity: sha512-yy7HuzQhj0dhGpD8RLXSZWEkLsV9ibvxvi6EiJ3bkqLAO1RGo0WbkWQiwpRlSFymTJRz0d3k5LM3kkx8ArDbLw==} @@ -2150,7 +2160,6 @@ packages: /@types/scheduler@0.16.5: resolution: {integrity: sha512-s/FPdYRmZR8SjLWGMCuax7r3qCWQw9QKHzXVukAuuIJkXkDRwp+Pu5LMIVFi0Fxbav35WURicYr8u1QsoybnQw==} - dev: true /@types/semver@7.5.4: resolution: {integrity: sha512-MMzuxN3GdFwskAnb6fz0orFvhfqi752yjaXylr0Rp4oDg5H0Zn1IuyRhDVvYOwAXoJirx2xuS16I3WjxnAIHiQ==} @@ -3521,7 +3530,6 @@ packages: /csstype@3.1.2: resolution: {integrity: sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==} - dev: true /damerau-levenshtein@1.0.8: resolution: {integrity: sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==} @@ -5938,8 +5946,8 @@ packages: - ts-node dev: true - /jotai@2.9.2(@types/react@18.2.31)(react@18.2.0): - resolution: {integrity: sha512-jIBXEadOHCziOuMY6HAy2KQcHipGhnsbF+twqh8Lcmcz/Yei0gdBtW5mOYdKmbQxGqkvfvXM3w/oHtJ2WNGSFg==} + /jotai@2.9.3(@types/react@18.2.31)(react@18.2.0): + resolution: {integrity: sha512-IqMWKoXuEzWSShjd9UhalNsRGbdju5G2FrqNLQJT+Ih6p41VNYe2sav5hnwQx4HJr25jq9wRqvGSWGviGG6Gjw==} engines: {node: '>=12.20.0'} peerDependencies: '@types/react': '>=17.0.0' @@ -5952,11 +5960,10 @@ packages: dependencies: '@types/react': 18.2.31 react: 18.2.0 - dev: true + dev: false /js-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} - dev: true /js-yaml@3.14.1: resolution: {integrity: sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==} @@ -6203,7 +6210,6 @@ packages: hasBin: true dependencies: js-tokens: 4.0.0 - dev: true /lower-case@2.0.2: resolution: {integrity: sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==} @@ -7439,7 +7445,6 @@ packages: engines: {node: '>=0.10.0'} dependencies: loose-envify: 1.4.0 - dev: true /read-pkg@3.0.0: resolution: {integrity: sha512-BLq/cCO9two+lBgiTYNqD6GdtK8s4NpaWrl6/rCO9w0TUS8oJl7cmToOZfRYllKTISY6nt1U7jQ53brmKqY6BA==}