UNPKG

recoil

Version:

Recoil - A state management library for React

206 lines (189 loc) 7.68 kB
/** * 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 */ 'use strict'; 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'); });