diff --git a/tests/vanilla/store.test.tsx b/tests/vanilla/store.test.tsx index a1f693a0d2..e60b547776 100644 --- a/tests/vanilla/store.test.tsx +++ b/tests/vanilla/store.test.tsx @@ -557,6 +557,10 @@ describe('aborting atoms', () => { }) describe('unstable_derive for scoping atoms', () => { + /** + * a + * S1[a]: a1 + */ it('primitive atom', async () => { const a = atom('a') a.onMount = (setSelf) => setSelf((v) => v + ':mounted') @@ -594,9 +598,14 @@ describe('unstable_derive for scoping atoms', () => { expect(derivedStore.get(a)).toBe('a:mounted:updated') }) + /** + * a, b, c(a + b) + * S1[a]: a1, b0, c0(a1 + b0) + */ it('derived atom (scoping primitive)', async () => { const a = atom('a') - const b = atom((get) => get(a)) + const b = atom('b') + const c = atom((get) => get(a) + get(b)) const scopedAtoms = new Set>([a]) const store = createStore() @@ -617,14 +626,18 @@ describe('unstable_derive for scoping atoms', () => { ] }) - expect(store.get(b)).toBe('a') - expect(derivedStore.get(b)).toBe('a') - derivedStore.set(a, 'b') + expect(store.get(c)).toBe('ab') + expect(derivedStore.get(c)).toBe('ab') + derivedStore.set(a, 'a2') await new Promise((resolve) => setTimeout(resolve)) - expect(store.get(b)).toBe('a') - expect(derivedStore.get(b)).toBe('b') + expect(store.get(c)).toBe('ab') + expect(derivedStore.get(c)).toBe('a2b') }) + /** + * a, b(a) + * S1[b]: a0, b1(a1) + */ it('derived atom (scoping derived)', async () => { const a = atom('a') const b = atom( @@ -691,4 +704,154 @@ describe('unstable_derive for scoping atoms', () => { expect(derivedStore.get(a)).toBe('a4') expect(derivedStore.get(b)).toBe('a5') }) + + /** + * a, b, c(a), d(c), e(d + b) + * S1[d]: a0, b0, c0(a0), d1(c1(a1)), e0(d1(c1(a1)) + b0) + */ + it('derived atom (scoping derived chain)', async () => { + const a = atom('a') + const b = atom('b') + const c = atom( + (get) => get(a), + (_get, set, v: string) => set(a, v), + ) + const d = atom( + (get) => get(c), + (_get, set, v: string) => set(c, v), + ) + const e = atom( + (get) => get(d) + get(b), + (_get, set, av: string, bv: string) => { + set(d, av) + set(b, bv) + }, + ) + const scopedAtoms = new Set>([d]) + + function makeStores() { + const baseStore = createStore() + const deriStore = baseStore.unstable_derive((getAtomState) => { + const scopedAtomStateMap = new WeakMap() + const scopedAtomStateSet = new WeakSet() + return [ + (atom, originAtomState) => { + if ( + scopedAtomStateSet.has(originAtomState as never) || + scopedAtoms.has(atom) + ) { + let atomState = scopedAtomStateMap.get(atom) + if (!atomState) { + atomState = { d: new Map(), p: new Set(), n: 0 } + scopedAtomStateMap.set(atom, atomState) + scopedAtomStateSet.add(atomState) + } + return atomState + } + return getAtomState(atom, originAtomState) + }, + ] + }) + expect(getAtoms(baseStore)).toEqual(['a', 'b', 'a', 'a', 'ab']) + expect(getAtoms(deriStore)).toEqual(['a', 'b', 'a', 'a', 'ab']) + return { baseStore, deriStore } + } + type Store = ReturnType + function getAtoms(store: Store) { + return [ + store.get(a), + store.get(b), + store.get(c), + store.get(d), + store.get(e), + ] + } + + /** + * base[d]: a0, b0, c0(a0), d0(c0(a0)), e0(d0(c0(a0)) + b0) + * deri[d]: a0, b0, c0(a0), d1(c1(a1)), e0(d1(c1(a1)) + b0) + */ + { + // UPDATE a0 + // NOCHGE b0 and a1 + const { baseStore, deriStore } = makeStores() + baseStore.set(a, '*') + expect(getAtoms(baseStore)).toEqual(['*', 'b', '*', '*', '*b']) + expect(getAtoms(deriStore)).toEqual(['*', 'b', '*', 'a', 'ab']) + } + { + // UPDATE b0 + // NOCHGE a0 and a1 + const { baseStore, deriStore } = makeStores() + baseStore.set(b, '*') + expect(getAtoms(baseStore)).toEqual(['a', '*', 'a', 'a', 'a*']) + expect(getAtoms(deriStore)).toEqual(['a', '*', 'a', 'a', 'a*']) + } + { + // UPDATE c0, c0 -> a0 + // NOCHGE b0 and a1 + const { baseStore, deriStore } = makeStores() + baseStore.set(c, '*') + expect(getAtoms(baseStore)).toEqual(['*', 'b', '*', '*', '*b']) + expect(getAtoms(deriStore)).toEqual(['*', 'b', '*', 'a', 'ab']) + } + { + // UPDATE d0, d0 -> c0 -> a0 + // NOCHGE b0 and a1 + const { baseStore, deriStore } = makeStores() + baseStore.set(d, '*') + expect(getAtoms(baseStore)).toEqual(['*', 'b', '*', '*', '*b']) + expect(getAtoms(deriStore)).toEqual(['*', 'b', '*', 'a', 'ab']) + } + { + // UPDATE e0, e0 -> d0 -> c0 -> a0 + // └--------------> b0 + // NOCHGE a1 + const { baseStore, deriStore } = makeStores() + baseStore.set(e, '*', '*') + expect(getAtoms(baseStore)).toEqual(['*', '*', '*', '*', '**']) + expect(getAtoms(deriStore)).toEqual(['*', '*', '*', 'a', 'a*']) + } + { + // UPDATE a0 + // NOCHGE b0 and a1 + const { baseStore, deriStore } = makeStores() + deriStore.set(a, '*') + expect(getAtoms(baseStore)).toEqual(['*', 'b', '*', '*', '*b']) + expect(getAtoms(deriStore)).toEqual(['*', 'b', '*', 'a', 'ab']) + } + { + // UPDATE b0 + // NOCHGE a0 and a1 + const { baseStore, deriStore } = makeStores() + deriStore.set(b, '*') + expect(getAtoms(baseStore)).toEqual(['a', '*', 'a', 'a', 'a*']) + expect(getAtoms(deriStore)).toEqual(['a', '*', 'a', 'a', 'a*']) + } + { + // UPDATE c0, c0 -> a0 + // NOCHGE b0 and a1 + const { baseStore, deriStore } = makeStores() + deriStore.set(c, '*') + expect(getAtoms(baseStore)).toEqual(['*', 'b', '*', '*', '*b']) + expect(getAtoms(deriStore)).toEqual(['*', 'b', '*', 'a', 'ab']) + } + { + // UPDATE d1, d1 -> c1 -> a1 + // NOCHGE b0 and a0 + const { baseStore, deriStore } = makeStores() + deriStore.set(d, '*') + expect(getAtoms(baseStore)).toEqual(['a', 'b', 'a', 'a', 'ab']) + expect(getAtoms(deriStore)).toEqual(['a', 'b', 'a', '*', '*b']) + } + { + // UPDATE e0, e0 -> d1 -> c1 -> a1 + // └--------------> b0 + // NOCHGE a0 + const { baseStore, deriStore } = makeStores() + deriStore.set(e, '*', '*') + expect(getAtoms(baseStore)).toEqual(['a', '*', 'a', 'a', 'a*']) + expect(getAtoms(deriStore)).toEqual(['a', '*', 'a', '*', '**']) + } + }) })