recoil
Version:
Recoil - A state management library for React
206 lines (189 loc) • 7.68 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+recoil
* @flow strict-local
* @format
*/
;
const {
getRecoilTestFn
} = require('recoil-shared/__test_utils__/Recoil_TestingUtils');
let React, useState, useTransition, act, useRecoilValue, useRecoilState, useRecoilValue_TRANSITION_SUPPORT_UNSTABLE, useRecoilState_TRANSITION_SUPPORT_UNSTABLE, atom, selectorFamily, renderElements, reactMode, flushPromisesAndTimers;
const testRecoil = getRecoilTestFn(() => {
React = require('react');
({
useState,
useTransition
} = React);
({
act
} = require('ReactTestUtils'));
({
useRecoilValue,
useRecoilState,
useRecoilValue_TRANSITION_SUPPORT_UNSTABLE,
useRecoilState_TRANSITION_SUPPORT_UNSTABLE,
atom,
selectorFamily
} = require('../../Recoil_index'));
({
renderElements,
flushPromisesAndTimers
} = require('recoil-shared/__test_utils__/Recoil_TestingUtils'));
({
reactMode
} = require('../../core/Recoil_ReactMode'));
});
let nextID = 0;
testRecoil('Works with useTransition', async ({
concurrentMode
}) => {
if (!reactMode().concurrent || !concurrentMode) {
return;
}
const indexAtom = atom({
key: `index${nextID++}`,
default: 0
}); // Basic implementation of a cache that suspends:
const cache = new Map();
const resolvers = [];
declare function getItem(index: any): any;
declare function ItemContents(arg0: any): any;
declare function Item(arg0: any): any;
let incrementIndex;
declare function Main(): any;
const c = renderElements(<Main />); // Initial load:
expect(c.textContent).toEqual('Index: 0 - Suspended');
act(() => resolvers[0]());
await flushPromisesAndTimers();
expect(c.textContent).toEqual('Index: 0 - Item 0 = v0'); // Increment index a single time; see previous item in transition, then once
// the new item resolves, see the new item:
act(() => incrementIndex());
expect(c.textContent).toEqual('Index: 0 - In transition - Item 0 = v0');
act(() => resolvers[1]());
await flushPromisesAndTimers();
expect(c.textContent).toEqual('Index: 1 - Item 1 = v1'); // Increment index a second time during transition; see previous item in
// transition, then once the new _second_ item resolves, see that new item:
act(() => incrementIndex());
expect(c.textContent).toEqual('Index: 1 - In transition - Item 1 = v1');
act(() => incrementIndex());
expect(c.textContent).toEqual('Index: 1 - In transition - Item 1 = v1');
act(() => resolvers[2]());
await flushPromisesAndTimers();
expect(c.textContent).toEqual('Index: 1 - In transition - Item 1 = v1');
act(() => resolvers[3]());
await flushPromisesAndTimers();
expect(c.textContent).toEqual('Index: 3 - Item 3 = v3');
});
testRecoil('useRecoilValue()', async ({
concurrentMode
}) => {
if (useTransition == null) {
return;
}
const myAtom = atom({
key: 'useRecoilValue atom',
default: 0
});
let resolvers = [];
declare function resolveSelectors(): any;
const query = selectorFamily({
key: 'useRecoilValue selector',
get: param => ({
get
}) => {
const value = get(myAtom);
return new Promise(resolve => {
resolvers.push(resolve);
}).then(str => `${param} ${value} ${str}`);
}
});
declare function Component(arg0: any): any;
let startReactTransition, startRecoilTransition, startBothTransition;
declare function Main(): any;
const c = renderElements(<Main />);
expect(c.textContent).toBe('React:0 Recoil:0 | LOADING');
act(resolveSelectors);
await flushPromisesAndTimers();
expect(c.textContent).toBe('React:0 Recoil:0 | 0 0 0 RESOLVED'); // Transition changing React State
act(startReactTransition);
expect(c.textContent).toBe(concurrentMode ? 'React:0 Recoil:0 [IN TRANSITION] | 0 0 0 RESOLVED' : 'React:1 Recoil:0 | LOADING');
act(resolveSelectors);
await flushPromisesAndTimers();
expect(c.textContent).toBe('React:1 Recoil:0 | 1 1 0 RESOLVED'); // Transition changing Recoil State
act(startRecoilTransition);
expect(c.textContent).toBe(concurrentMode && reactMode().concurrent ? 'React:1 Recoil:0 [IN TRANSITION] | 1 1 0 RESOLVED' : 'React:1 Recoil:1 | LOADING');
act(resolveSelectors);
await flushPromisesAndTimers();
expect(c.textContent).toBe('React:1 Recoil:1 | 1 1 1 RESOLVED'); // Second transition changing Recoil State
act(startRecoilTransition);
expect(c.textContent).toBe(concurrentMode && reactMode().concurrent ? 'React:1 Recoil:1 [IN TRANSITION] | 1 1 1 RESOLVED' : 'React:1 Recoil:2 | LOADING');
act(resolveSelectors);
await flushPromisesAndTimers();
expect(c.textContent).toBe('React:1 Recoil:2 | 1 1 2 RESOLVED'); // Transition with both React and Recoil state
act(startBothTransition);
expect(c.textContent).toBe(concurrentMode && (reactMode().concurrent || reactMode().mode === 'MUTABLE_SOURCE') ? 'React:1 Recoil:2 [IN TRANSITION] | 1 1 2 RESOLVED' : 'React:2 Recoil:3 | LOADING');
act(resolveSelectors);
await flushPromisesAndTimers();
act(resolveSelectors);
await flushPromisesAndTimers();
expect(c.textContent).toBe('React:2 Recoil:3 | 2 2 3 RESOLVED');
});
testRecoil('useRecoilValue_TRANSITION_SUPPORT_UNSTABLE()', async ({
concurrentMode
}) => {
if (useTransition == null) {
return;
}
const myAtom = atom({
key: 'TRANSITION_SUPPORT_UNSTABLE atom',
default: 0
});
let resolvers = [];
declare function resolveSelectors(): any;
const query = selectorFamily({
key: 'TRANSITION_SUPPORT_UNSTABLE selector',
get: param => ({
get
}) => {
const value = get(myAtom);
return new Promise(resolve => {
resolvers.push(resolve);
}).then(str => `${param} ${value} ${str}`);
}
});
declare function Component(arg0: any): any;
let startReactTransition, startRecoilTransition, startBothTransition;
declare function Main(): any;
const c = renderElements(<Main />);
expect(c.textContent).toBe('React:0 Recoil:0 | LOADING');
act(resolveSelectors);
await flushPromisesAndTimers();
expect(c.textContent).toBe('React:0 Recoil:0 | 0 0 0 RESOLVED'); // Transition changing React State
act(startReactTransition);
expect(c.textContent).toBe(concurrentMode ? 'React:0 Recoil:0 [IN TRANSITION] | 0 0 0 RESOLVED' : 'React:1 Recoil:0 | LOADING');
act(resolveSelectors);
await flushPromisesAndTimers();
expect(c.textContent).toBe('React:1 Recoil:0 | 1 1 0 RESOLVED'); // Transition changing Recoil State
act(startRecoilTransition);
expect(c.textContent).toBe(concurrentMode && reactMode().early ? 'React:1 Recoil:0 [IN TRANSITION] | 1 1 0 RESOLVED' : 'React:1 Recoil:1 | LOADING');
act(resolveSelectors);
await flushPromisesAndTimers();
expect(c.textContent).toBe('React:1 Recoil:1 | 1 1 1 RESOLVED'); // Second transition changing Recoil State
act(startRecoilTransition);
expect(c.textContent).toBe(concurrentMode && reactMode().early ? 'React:1 Recoil:1 [IN TRANSITION] | 1 1 1 RESOLVED' : 'React:1 Recoil:2 | LOADING');
act(resolveSelectors);
await flushPromisesAndTimers();
expect(c.textContent).toBe('React:1 Recoil:2 | 1 1 2 RESOLVED'); // Transition with both React and Recoil State
act(startBothTransition);
expect(c.textContent).toBe(concurrentMode ? 'React:1 Recoil:2 [IN TRANSITION] | 1 1 2 RESOLVED' : 'React:2 Recoil:3 | LOADING');
act(resolveSelectors);
await flushPromisesAndTimers();
act(resolveSelectors);
await flushPromisesAndTimers();
expect(c.textContent).toBe('React:2 Recoil:3 | 2 2 3 RESOLVED');
});