recoil
Version:
Recoil - A state management library for React
210 lines (201 loc) • 7.63 kB
Flow
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @emails oncall+perf_viz
* @flow strict-local
* @format
*/
;
const {
getRecoilTestFn
} = require('../../testing/Recoil_TestingUtils');
let React, act, freshSnapshot, atom, atomFamily, selector, ReadsAtom, asyncSelector, componentThatReadsAndWritesAtom, renderElements, useRecoilTransactionObserver;
const testRecoil = getRecoilTestFn(() => {
React = require('React');
({
act
} = require('ReactTestUtils'));
({
freshSnapshot
} = require('../../core/Recoil_Snapshot'));
atom = require('../../recoil_values/Recoil_atom');
atomFamily = require('../../recoil_values/Recoil_atomFamily');
selector = require('../../recoil_values/Recoil_selector');
({
ReadsAtom,
asyncSelector,
componentThatReadsAndWritesAtom,
renderElements
} = require('../../testing/Recoil_TestingUtils'));
({
useRecoilTransactionObserver
} = require('../Recoil_Hooks'));
});
declare function TransactionObserver(arg0: any): any; // Run test first since it deals with all registered atoms
testRecoil('getNodes', () => {
let snapshot = freshSnapshot();
declare function UseRecoilTransactionObserver(): any;
const atoms = atomFamily<string, string>({
key: 'useRecoilTransactionObserver getNodes atom',
default: x => x
});
const [ReadsAtomA, setAtomA, resetAtomA] = componentThatReadsAndWritesAtom(atoms('A'));
const [ReadsAtomB, setAtomB] = componentThatReadsAndWritesAtom(atoms('B'));
const selectorA = selector({
key: 'useRecoilTransactionObserver getNodes selector',
get: ({
get
}) => get(atoms('A')) + '-SELECTOR'
});
const c = renderElements(<>
<ReadsAtomA />
<ReadsAtomB />
<ReadsAtom atom={selectorA} />
<UseRecoilTransactionObserver />
</>);
expect(c.textContent).toEqual('"A""B""A-SELECTOR"');
expect(Array.from(snapshot.getNodes_UNSTABLE({
isInitialized: true
})).length).toEqual(0);
act(() => setAtomA('A')); // Greater than 3 because we expect at least nodes for atom's A and B from
// the family and selectorA. In reality we currenlty get 8 due to internal
// helper selectors and default fallback atoms.
expect(Array.from(snapshot.getNodes_UNSTABLE({
isInitialized: true
})).length).toBeGreaterThan(3);
const nodes = Array.from(snapshot.getNodes_UNSTABLE({
isInitialized: true
}));
expect(nodes).toEqual(expect.arrayContaining([atoms('A'), atoms('B'), selectorA])); // Test atom A is set
const aDirty = Array.from(snapshot.getNodes_UNSTABLE({
isModified: true
}));
expect(aDirty.length).toEqual(1);
expect(snapshot.getLoadable(aDirty[0]).contents).toEqual('A'); // Test atom B is set
act(() => setAtomB('B'));
const bDirty = Array.from(snapshot.getNodes_UNSTABLE({
isModified: true
}));
expect(bDirty.length).toEqual(1);
expect(snapshot.getLoadable(bDirty[0]).contents).toEqual('B'); // Test atoms
const atomNodes = Array.from(snapshot.getNodes_UNSTABLE({
isInitialized: true
}));
expect(atomNodes.map(atom => snapshot.getLoadable(atom).contents)).toEqual(expect.arrayContaining(['A', 'B'])); // Test selector
const selectorNodes = Array.from(snapshot.getNodes_UNSTABLE({
isInitialized: true
}));
expect(selectorNodes.map(atom => snapshot.getLoadable(atom).contents)).toEqual(expect.arrayContaining(['A-SELECTOR'])); // Test Reset
act(resetAtomA);
const resetDirty = Array.from(snapshot.getNodes_UNSTABLE({
isModified: true
}));
expect(resetDirty.length).toEqual(1);
expect(resetDirty[0]).toBe(aDirty[0]); // TODO Test dirty selectors
});
testRecoil('Can observe atom value', async () => {
const atomA = atom({
key: 'Observe Atom A',
default: 'DEFAULT A'
});
const atomB = atom({
key: 'Observe Atom B',
default: 'DEFAULT B'
});
const [WriteAtomA, setAtomA, resetA] = componentThatReadsAndWritesAtom(atomA);
const [WriteAtomB, setAtomB] = componentThatReadsAndWritesAtom(atomB);
const transactions = [];
renderElements(<>
<TransactionObserver callback={({
snapshot,
previousSnapshot
}) => {
transactions.push({
snapshot,
previousSnapshot
});
}} />
<WriteAtomA />
<WriteAtomB />
</>);
act(() => setAtomB('SET B'));
expect(transactions.length).toEqual(1);
await expect(transactions[0].snapshot.getPromise(atomA)).resolves.toEqual('DEFAULT A');
await expect(transactions[0].previousSnapshot.getPromise(atomA)).resolves.toEqual('DEFAULT A');
await expect(transactions[0].snapshot.getPromise(atomB)).resolves.toEqual('SET B');
await expect(transactions[0].previousSnapshot.getPromise(atomB)).resolves.toEqual('DEFAULT B');
act(() => setAtomA('SET A'));
expect(transactions.length).toEqual(2);
await expect(transactions[1].snapshot.getPromise(atomA)).resolves.toEqual('SET A');
await expect(transactions[1].previousSnapshot.getPromise(atomA)).resolves.toEqual('DEFAULT A');
await expect(transactions[1].snapshot.getPromise(atomB)).resolves.toEqual('SET B');
await expect(transactions[1].previousSnapshot.getPromise(atomB)).resolves.toEqual('SET B');
act(() => resetA());
expect(transactions.length).toEqual(3);
await expect(transactions[2].snapshot.getPromise(atomA)).resolves.toEqual('DEFAULT A');
await expect(transactions[2].previousSnapshot.getPromise(atomA)).resolves.toEqual('SET A');
await expect(transactions[2].snapshot.getPromise(atomB)).resolves.toEqual('SET B');
await expect(transactions[2].previousSnapshot.getPromise(atomB)).resolves.toEqual('SET B');
});
testRecoil('Can observe selector value', async () => {
const atomA = atom({
key: 'Observe Atom for Selector',
default: 'DEFAULT'
});
const selectorA = selector({
key: 'Observer Selector As',
get: ({
get
}) => `SELECTOR ${get(atomA)}`
});
const [WriteAtom, setAtom] = componentThatReadsAndWritesAtom(atomA);
const transactions = [];
renderElements(<>
<TransactionObserver callback={({
snapshot,
previousSnapshot
}) => {
transactions.push({
snapshot,
previousSnapshot
});
}} />
<WriteAtom />
</>);
act(() => setAtom('SET'));
expect(transactions.length).toEqual(1);
await expect(transactions[0].snapshot.getPromise(atomA)).resolves.toEqual('SET');
await expect(transactions[0].previousSnapshot.getPromise(atomA)).resolves.toEqual('DEFAULT');
await expect(transactions[0].snapshot.getPromise(selectorA)).resolves.toEqual('SELECTOR SET');
await expect(transactions[0].previousSnapshot.getPromise(selectorA)).resolves.toEqual('SELECTOR DEFAULT');
});
testRecoil('Can observe async selector value', async () => {
const atomA = atom({
key: 'Observe Atom for Async Selector',
default: 'DEFAULT'
});
const [WriteAtom, setAtom] = componentThatReadsAndWritesAtom(atomA);
const [selectorA, resolveA] = asyncSelector();
const transactions = [];
renderElements(<>
<TransactionObserver callback={({
snapshot,
previousSnapshot
}) => {
transactions.push({
snapshot,
previousSnapshot
});
}} />
<WriteAtom />
</>);
act(() => setAtom('SET'));
expect(transactions.length).toEqual(1);
expect(transactions[0].snapshot.getLoadable(selectorA).state).toEqual('loading');
act(() => resolveA('RESOLVE'));
expect(transactions.length).toEqual(1);
await expect(transactions[0].snapshot.getPromise(selectorA)).resolves.toEqual('RESOLVE');
});