@mic-rexjs/usecases-react
Version:
React-based solution for use usecases of Clean Architecture
2,838 lines • 90.6 kB
JavaScript
import _slicedToArray from "@babel/runtime/helpers/slicedToArray";
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
import { useUseCase } from '.';
import { useDepsKey } from '../useDepsKey';
import { beforeEach, describe, expect, jest, test } from '@jest/globals';
import { entityUseCase } from '@mic-rexjs/usecases';
import { fireEvent, screen } from '@testing-library/dom';
import { act, render, renderHook } from '@testing-library/react';
import { useDeepCompareEffect, useMemoizedFn, useMount, useUpdate, useUpdateEffect } from 'ahooks';
import { Fragment, useEffect, useMemo, useRef, useState } from 'react';
import { createPortal } from 'react-dom';
const EXT_1 = '.png';
const EXT_2 = '.jpg';
const PATH_1 = `hello${EXT_1}`;
const PATH_2 = `hello${EXT_2}`;
const PARENT_BUTTON_TEXT = 'parent button';
const CHILD_BUTTON_TEXT = 'child button';
const defaultFile = {
path: '',
ext: '',
size: 0
};
const fileUseCase = function () {
let options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
const entityReducers = entityUseCase();
const _options$pathPrefix = options.pathPrefix,
pathPrefix = _options$pathPrefix === void 0 ? '' : _options$pathPrefix,
onPathChange = options.onPathChange;
const init = function (entity) {
let newEntity = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : defaultFile;
return function* () {
yield () => {
return newEntity;
};
}();
};
const setPath = function* (entity, path) {
var _path$match;
const oldPath = entity.path;
const newPath = pathPrefix + path;
yield {
...entity,
path: newPath,
ext: ((_path$match = path.match(/\.[^.]+$/)) === null || _path$match === void 0 ? void 0 : _path$match[0]) || ''
};
onPathChange === null || onPathChange === void 0 || onPathChange(newPath, oldPath);
};
const readFile = async function* (entity, path) {
const size = 2000;
yield* setPath(entity, path);
await Promise.resolve(null);
yield oldEntity => {
return {
...oldEntity,
size
};
};
return size;
};
const isImage = entity => {
const ext = entity.ext;
return [EXT_1, EXT_2].includes(ext);
};
return {
...entityReducers,
init,
setPath,
readFile,
isImage
};
};
const subtractionReducer = jest.fn((value1, value2) => {
return value1 - value2;
});
const mathUseCase = function () {
let options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
const _options$extraValue = options.extraValue,
extraValue = _options$extraValue === void 0 ? 0 : _options$extraValue;
const add = (value1, value2) => {
return value1 + value2 + extraValue;
};
return {
add,
subtraction: subtractionReducer
};
};
const fieldPathUseCase = () => {
return entityUseCase();
};
const Child = _ref => {
let _ref$textPrefix = _ref.textPrefix,
textPrefix = _ref$textPrefix === void 0 ? '' : _ref$textPrefix,
onUpdate = _ref.onUpdate,
onPathChange = _ref.onPathChange,
onUndefinedEntity = _ref.onUndefinedEntity;
const _useUseCase = useUseCase(fileUseCase),
_useUseCase2 = _slicedToArray(_useUseCase, 2),
file = _useUseCase2[0],
setPath = _useUseCase2[1].setPath;
const _ref2 = file || {},
path = _ref2.path,
ext = _ref2.ext;
const onClick = () => {
setPath(PATH_2);
};
const undefinedEntity = useMemoizedFn(() => {
onUndefinedEntity === null || onUndefinedEntity === void 0 || onUndefinedEntity();
});
useUpdateEffect(() => {
onUpdate === null || onUpdate === void 0 || onUpdate();
});
useEffect(() => {
if (file === void 0) {
undefinedEntity();
}
}, [file, undefinedEntity]);
const pathChange = useMemoizedFn(() => {
onPathChange === null || onPathChange === void 0 || onPathChange(path);
});
useUpdateEffect(pathChange, [path, pathChange]);
return _jsxs("div", {
children: [_jsxs("main", {
children: ["child path: ", path, _jsx("br", {}), "child ext: ", ext]
}), _jsx("footer", {
children: _jsx("button", {
onClick: onClick,
children: textPrefix + CHILD_BUTTON_TEXT
})
})]
});
};
const Parent = _ref3 => {
let children = _ref3.children,
onUpdate = _ref3.onUpdate,
onSetPath = _ref3.onSetPath;
const _useUseCase3 = useUseCase(defaultFile, fileUseCase),
_useUseCase4 = _slicedToArray(_useUseCase3, 3),
_useUseCase4$ = _useUseCase4[0],
path = _useUseCase4$.path,
ext = _useUseCase4$.ext,
setPath = _useUseCase4[1].setPath,
Provider = _useUseCase4[2];
const onClick = () => {
setPath((onSetPath === null || onSetPath === void 0 ? void 0 : onSetPath()) || '');
};
useUpdateEffect(() => {
onUpdate === null || onUpdate === void 0 || onUpdate();
});
return _jsx(Provider, {
children: _jsxs("div", {
children: [_jsxs("header", {
children: ["parent path: ", path, _jsx("br", {}), "parent ext: ", ext]
}), _jsx("main", {
children: children
}), _jsx("footer", {
children: _jsx("button", {
onClick: onClick,
children: PARENT_BUTTON_TEXT
})
})]
})
});
};
const MathParent = _ref4 => {
let _ref4$extraValue = _ref4.extraValue,
extraValue = _ref4$extraValue === void 0 ? 0 : _ref4$extraValue,
onUpdate = _ref4.onUpdate;
const _useUseCase5 = useUseCase(mathUseCase, {
extraValue
}, [extraValue]),
add = _useUseCase5.add;
const onClick = () => {
add(1, 2);
};
useUpdateEffect(() => {
onUpdate === null || onUpdate === void 0 || onUpdate();
});
return _jsx("div", {
children: _jsx("button", {
onClick: onClick,
children: PARENT_BUTTON_TEXT
})
});
};
const useDeepCompareUpdate = (callback, deps) => {
const isFirstRef = useRef(true);
const memoizedCallback = useMemoizedFn(callback);
const depsKey = useDepsKey(deps);
useEffect(() => {
return () => {
isFirstRef.current = false;
};
});
useDeepCompareEffect(() => {
if (isFirstRef.current) {
return;
}
memoizedCallback();
}, [depsKey, memoizedCallback]);
};
beforeEach(() => {
subtractionReducer.mockClear();
});
describe('useUseCase', () => {
describe('`useUseCase` should work the same as `useRootCoreCollection`', () => {
test('check context type', () => {
const _renderHook = renderHook(() => {
return useUseCase(defaultFile, fileUseCase);
}),
result = _renderHook.result;
const cores = result.current;
expect(Array.isArray(cores)).toBe(true);
expect(cores).toHaveLength(3);
});
test('`entity` should equal `defaultFile`', () => {
const _renderHook2 = renderHook(() => {
return useUseCase(defaultFile, fileUseCase);
}),
result = _renderHook2.result;
const cores = result.current;
const _cores = _slicedToArray(cores, 1),
entity = _cores[0];
expect(entity).toEqual(defaultFile);
});
test('Should return the `EntityStore.value` if root entity has not changed', () => {
const _renderHook3 = renderHook(() => {
return useUseCase(defaultFile, fileUseCase);
}),
result = _renderHook3.result;
const collection = result.current;
const _collection = _slicedToArray(collection, 2),
setPath = _collection[1].setPath;
act(() => {
setPath(PATH_1);
});
expect(result.current[0]).toEqual({
...defaultFile,
path: PATH_1,
ext: EXT_1
});
});
test('Should return the root entity if root entity has changed', () => {
let file = defaultFile;
const _renderHook4 = renderHook(() => {
return useUseCase(file, fileUseCase);
}),
result = _renderHook4.result,
rerender = _renderHook4.rerender;
file = {
...defaultFile,
path: PATH_2
};
rerender();
expect(result.current[0]).toEqual({
...defaultFile,
path: PATH_2
});
// 渲染多一次,避免第二次渲染的 `entity` 没变化而取错缓存的情况
rerender();
expect(result.current[0]).toEqual({
...defaultFile,
path: PATH_2
});
});
test('Should return the root entity if root entity has changed even with entity state changed', () => {
let file = defaultFile;
const _renderHook5 = renderHook(() => {
return useUseCase(file, fileUseCase);
}),
result = _renderHook5.result;
const collection = result.current;
const _collection2 = _slicedToArray(collection, 2),
setPath = _collection2[1].setPath;
act(() => {
file = {
...defaultFile,
path: PATH_2
};
setPath(PATH_1);
});
expect(result.current[0]).toEqual({
...defaultFile,
path: PATH_2
});
});
test('Should support entity initializer', () => {
const file = defaultFile;
const _renderHook6 = renderHook(() => {
return useUseCase(() => {
return file;
}, fileUseCase);
}),
result = _renderHook6.result;
const _result$current = _slicedToArray(result.current, 1),
currentFile = _result$current[0];
expect(currentFile).toEqual(file);
});
test('Should support entity initializer with generator', () => {
const file = defaultFile;
const onChange = jest.fn();
const _renderHook7 = renderHook(() => {
return useUseCase(function () {
let currentFile = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : defaultFile;
return function* () {
yield {
...currentFile,
size: 5000
};
}();
}, fileUseCase, {
onChange
});
}),
result = _renderHook7.result;
const _result$current2 = _slicedToArray(result.current, 1),
currentFile = _result$current2[0];
expect(currentFile).toEqual({
...file,
size: 5000
});
expect(onChange).toHaveBeenCalledTimes(0);
});
test('Should support entity initializer with async callback generator', async () => {
const file = defaultFile;
const onChange = jest.fn();
const onUpate = jest.fn();
const _Promise$withResolver = Promise.withResolvers(),
promise = _Promise$withResolver.promise,
resolve = _Promise$withResolver.resolve;
const _renderHook8 = renderHook(() => {
useEffect(() => {
onUpate();
});
return useUseCase(function () {
let entity = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : defaultFile;
return function* () {
const newEntity1 = yield {
...entity,
size: 1
};
// 在异步之前,就算多次 `yield`,也不会触发 `onChange`
yield {
...newEntity1,
size: newEntity1.size + 1
};
const newEntity2 = yield async currentEntity => {
await promise;
return {
...currentEntity,
size: currentEntity.size + 1
};
};
yield {
...newEntity2,
size: newEntity2.size + 1
};
}();
}, fileUseCase, {
onChange
});
}),
result = _renderHook8.result;
const _result$current3 = _slicedToArray(result.current, 1),
currentFile1 = _result$current3[0];
const newFile1 = {
...file,
size: 2
};
expect(currentFile1).toEqual(newFile1);
expect(onChange).toHaveBeenCalledTimes(0);
expect(onUpate).toHaveBeenCalledTimes(1);
await act(async () => {
resolve();
await promise;
});
const newFile2 = {
...file,
size: 3
};
const newFile3 = {
...file,
size: 4
};
expect(onChange).toHaveBeenCalledTimes(2);
expect(onUpate).toHaveBeenCalledTimes(2);
expect(onChange).toHaveBeenNthCalledWith(1, newFile2, newFile1);
expect(onChange).toHaveBeenNthCalledWith(2, newFile3, newFile2);
});
test('Entity initializer should provide the current entity', () => {
let updateIndex = 0;
const file = defaultFile;
const onFile = jest.fn();
const _renderHook9 = renderHook(() => {
return useUseCase(currentFile => {
onFile(currentFile);
return currentFile !== null && currentFile !== void 0 ? currentFile : file;
}, fileUseCase, {}, [updateIndex++]);
}),
result = _renderHook9.result;
const _result$current4 = _slicedToArray(result.current, 2),
setPath1 = _result$current4[1].setPath;
expect(onFile).toHaveBeenCalledTimes(1);
expect(onFile).toHaveBeenCalledWith(void 0);
act(() => {
setPath1(PATH_1);
});
const _result$current5 = _slicedToArray(result.current, 2),
setPath2 = _result$current5[1].setPath;
expect(onFile).toHaveBeenCalledTimes(2);
expect(onFile).toHaveBeenCalledWith({
...file,
ext: EXT_1,
path: PATH_1
});
act(() => {
setPath2(PATH_2);
});
expect(onFile).toHaveBeenCalledTimes(3);
expect(onFile).toHaveBeenCalledWith({
...file,
ext: EXT_2,
path: PATH_2
});
});
test('Do not update entity when the initializer has yielded some constant entity properties', () => {
let updateIndex = 0;
const _renderHook0 = renderHook(() => {
return useUseCase(function* () {
yield defaultFile;
yield {
path: PATH_1
};
}, fileUseCase, {}, [updateIndex++]);
}),
result = _renderHook0.result;
const current1 = result.current;
const _current = _slicedToArray(current1, 2),
entity1 = _current[0],
setEntity = _current[1].setEntity;
expect(entity1).toEqual({
...defaultFile,
path: PATH_1
});
act(() => {
setEntity({
path: PATH_2
});
});
const current2 = result.current;
const _current2 = _slicedToArray(current2, 1),
entity2 = _current2[0];
expect(entity2).toEqual({
...defaultFile,
path: PATH_2
});
});
test('Should update entity when the initializer has returned some cconstant entity properties after `setEntity`', () => {
let updateIndex = 0;
const _renderHook1 = renderHook(() => {
return useUseCase(function* () {
yield defaultFile;
return {
path: PATH_1
};
}, fileUseCase, {}, [updateIndex++]);
}),
result = _renderHook1.result;
const current1 = result.current;
const _current3 = _slicedToArray(current1, 2),
entity1 = _current3[0],
setEntity = _current3[1].setEntity;
expect(entity1).toEqual({
...defaultFile,
path: PATH_1
});
act(() => {
setEntity({
path: PATH_2
});
});
const current2 = result.current;
const _current4 = _slicedToArray(current2, 1),
entity2 = _current4[0];
expect(entity2).toEqual({
...defaultFile,
path: PATH_1
});
});
test('Entity property accessors should be reserved in initializer', () => {
let updateIndex = 0;
const defaultFileWithAccessors = {
...defaultFile,
get text() {
const path = this.path;
return `xyz-${path}`;
}
};
const _renderHook10 = renderHook(() => {
return useUseCase(function* () {
yield defaultFileWithAccessors;
yield {
path: PATH_1
};
}, fileUseCase, {}, [updateIndex++]);
}),
result = _renderHook10.result;
const current1 = result.current;
const _current5 = _slicedToArray(current1, 2),
entity1 = _current5[0],
setEntity = _current5[1].setEntity;
expect(entity1).toEqual({
...defaultFile,
path: PATH_1,
text: `xyz-${PATH_1}`
});
act(() => {
setEntity({
path: PATH_2
});
});
const current2 = result.current;
const _current6 = _slicedToArray(current2, 1),
entity2 = _current6[0];
expect(entity2).toEqual({
...defaultFile,
path: PATH_2,
text: `xyz-${PATH_2}`
});
});
test('Entity property getter should always return the same object if nothing changed after component update', () => {
let updateTimes = 0;
const data = {
x: 1,
get obj() {
return {};
}
};
const _renderHook11 = renderHook(() => {
const collection = useUseCase(data, entityUseCase);
const _collection3 = _slicedToArray(collection, 1),
obj = _collection3[0].obj;
useMemo(() => {
void obj;
updateTimes++;
}, [obj]);
return collection;
}),
rerender = _renderHook11.rerender;
expect(updateTimes).toEqual(1);
act(() => {
rerender();
});
expect(updateTimes).toEqual(1);
});
test('Entity property getter should always return a new object if something changed after component update', () => {
let updateTimes = 0;
const data = {
x: 1,
get obj() {
return {};
}
};
const _renderHook12 = renderHook(() => {
const collection = useUseCase(data, entityUseCase);
const _collection4 = _slicedToArray(collection, 1),
obj = _collection4[0].obj;
useMemo(() => {
void obj;
updateTimes++;
}, [obj]);
return collection;
}),
result = _renderHook12.result;
const current = result.current;
const _current7 = _slicedToArray(current, 2),
setEntity = _current7[1].setEntity;
expect(updateTimes).toEqual(1);
act(() => {
setEntity({
x: 2
});
});
expect(updateTimes).toEqual(2);
});
test('Entity initializer should provide the first argument with current entity when deps has change.', () => {
let entityArg;
let times = 0;
const file = defaultFile;
const _renderHook13 = renderHook(() => {
return useUseCase(function () {
let entity = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : file;
const path = entity.path;
entityArg = entity;
return {
...entity,
path: path + '1'
};
}, fileUseCase, {}, [times++]);
}),
result = _renderHook13.result;
const collection1 = result.current;
const _collection5 = _slicedToArray(collection1, 2),
currentFile1 = _collection5[0],
setPath = _collection5[1].setPath;
expect(entityArg).toEqual(defaultFile);
expect(currentFile1).toEqual({
...defaultFile,
path: '1'
});
act(() => {
setPath('2');
});
const collection2 = result.current;
const _collection6 = _slicedToArray(collection2, 1),
currentFile2 = _collection6[0];
expect(entityArg).toEqual({
...defaultFile,
path: '2'
});
expect(currentFile2).toEqual({
...defaultFile,
path: '21'
});
});
test('`reducers` should be returned as an object', () => {
const _renderHook14 = renderHook(() => {
return useUseCase(defaultFile, fileUseCase);
}),
result = _renderHook14.result;
const cores = result.current;
const _cores2 = _slicedToArray(cores, 2),
reducers = _cores2[1];
expect(typeof reducers).toBe('object');
});
test('`Provider` should be returned as a function', () => {
const _renderHook15 = renderHook(() => {
return useUseCase(defaultFile, fileUseCase);
}),
result = _renderHook15.result;
const cores = result.current;
const _cores3 = _slicedToArray(cores, 3),
Provider = _cores3[2];
expect(typeof Provider).toBe('function');
});
test('`usecase` should be called only once if deps has not provided', () => {
const mockUseCase = jest.fn(fileUseCase);
const _renderHook16 = renderHook(() => {
return useUseCase(defaultFile, mockUseCase);
}),
result = _renderHook16.result,
rerender = _renderHook16.rerender;
const cores = result.current;
const _cores4 = _slicedToArray(cores, 2),
setEntity = _cores4[1].setEntity;
expect(mockUseCase).toHaveBeenCalledTimes(1);
act(() => {
setEntity({
...defaultFile
});
});
rerender();
expect(mockUseCase).toHaveBeenCalledTimes(1);
});
test('`usecase` should be called only once if deps has not changed', () => {
const mockUseCase = jest.fn(fileUseCase);
const _renderHook17 = renderHook(() => {
return useUseCase(defaultFile, mockUseCase, {}, [1, 'x']);
}),
result = _renderHook17.result,
rerender = _renderHook17.rerender;
const cores = result.current;
const _cores5 = _slicedToArray(cores, 2),
setEntity = _cores5[1].setEntity;
expect(mockUseCase).toHaveBeenCalledTimes(1);
act(() => {
setEntity({
...defaultFile
});
});
rerender();
expect(mockUseCase).toHaveBeenCalledTimes(1);
});
test('`usecase` should be called if deps has changed', () => {
let i = 0;
const mockUseCase = jest.fn(fileUseCase);
const _renderHook18 = renderHook(() => {
return useUseCase(defaultFile, mockUseCase, {}, [i++, 'x']);
}),
result = _renderHook18.result,
rerender = _renderHook18.rerender;
const cores = result.current;
const _cores6 = _slicedToArray(cores, 2),
setEntity = _cores6[1].setEntity;
expect(mockUseCase).toHaveBeenCalledTimes(1);
act(() => {
setEntity({
...defaultFile
});
});
expect(mockUseCase).toHaveBeenCalledTimes(1);
rerender();
expect(mockUseCase).toHaveBeenCalledTimes(2);
});
test('after call non-generator reducer, it should not trigger update', () => {
const onUpdate = jest.fn();
const onDeepUpdate = jest.fn();
const _renderHook19 = renderHook(() => {
const context = useUseCase(defaultFile, fileUseCase);
const _context = _slicedToArray(context, 2),
reducers = _context[1];
const isImage = reducers.isImage;
const update = useUpdate();
useUpdateEffect(() => {
onUpdate();
}, [context]);
useDeepCompareUpdate(() => {
onDeepUpdate();
}, [context]);
return shouldUpdate => {
isImage();
if (!shouldUpdate) {
return;
}
update();
};
}),
result = _renderHook19.result;
act(() => {
result.current();
});
expect(onUpdate).toHaveBeenCalledTimes(0);
expect(onDeepUpdate).toHaveBeenCalledTimes(0);
act(() => {
result.current(true);
});
expect(onUpdate).toHaveBeenCalledTimes(0);
expect(onDeepUpdate).toHaveBeenCalledTimes(0);
});
test('after `yield entity`, it should trigger update', () => {
const onUpdate = jest.fn();
const _renderHook20 = renderHook(() => {
useUpdateEffect(() => {
onUpdate();
});
return useUseCase(defaultFile, fileUseCase);
}),
result = _renderHook20.result;
const cores = result.current;
const _cores7 = _slicedToArray(cores, 2),
entity = _cores7[0],
reducers = _cores7[1];
const setPath = reducers.setPath,
setEntity = reducers.setEntity;
act(() => {
setPath(PATH_1);
setEntity({
size: 5000
});
});
expect(result.current).not.toBe(cores);
expect(result.current[0]).not.toBe(entity);
expect(result.current[1]).not.toBe(reducers);
expect(result.current[1].setPath).not.toBe(setPath);
expect(result.current[0]).toEqual({
...defaultFile,
path: PATH_1,
ext: EXT_1,
size: 5000
});
expect(onUpdate).toHaveBeenCalledTimes(1);
});
test('after `yield entity` on async mode, it should trigger update', async () => {
const onUpdate = jest.fn();
const _renderHook21 = renderHook(() => {
useUpdateEffect(() => {
onUpdate();
});
return useUseCase(defaultFile, fileUseCase);
}),
result = _renderHook21.result;
const cores = result.current;
const _cores8 = _slicedToArray(cores, 2),
entity = _cores8[0],
reducers = _cores8[1];
const readFile = reducers.readFile;
const promise = act(() => {
return readFile(PATH_1);
});
expect(result.current).toBe(cores);
expect(result.current).toEqual(cores);
expect(onUpdate).toHaveBeenCalledTimes(0);
await promise;
expect(result.current[0]).not.toBe(entity);
expect(result.current[1]).not.toBe(reducers);
expect(result.current[1].readFile).not.toBe(readFile);
expect(result.current[0].path).toBe(PATH_1);
expect(result.current[0].ext).toBe(EXT_1);
expect(result.current[0].size).toBe(2000);
expect(onUpdate).toHaveBeenCalledTimes(1);
});
test('after `yield entity` callbacks, it should trigger update', () => {
const onUpdate = jest.fn();
const _renderHook22 = renderHook(() => {
useUpdateEffect(() => {
onUpdate();
});
return useUseCase(defaultFile, fileUseCase);
}),
result = _renderHook22.result;
const cores = result.current;
const _cores9 = _slicedToArray(cores, 2),
reducers = _cores9[1];
const init = reducers.init;
act(() => {
init({
...defaultFile,
size: 5000
});
});
expect(result.current[0].size).toBe(5000);
expect(result.current[1]).not.toBe(reducers);
expect(result.current[1].init).not.toBe(init);
expect(onUpdate).toHaveBeenCalledTimes(1);
});
test('reducer call should be cached if component is at rendering phase', () => {
const buttonText = 'button';
const addReducer = jest.fn((entity, substring) => {
return entity + substring;
});
const stringUseCase = () => {
const entityReducers = entityUseCase();
return {
...entityReducers,
add: addReducer
};
};
const CacheTestComponent = () => {
const _useUseCase6 = useUseCase('', stringUseCase),
_useUseCase7 = _slicedToArray(_useUseCase6, 2),
add = _useUseCase7[1].add;
const update = useUpdate();
const onClick = () => {
update();
};
add('x');
return _jsx("button", {
onClick: onClick,
children: buttonText
});
};
render(_jsx(CacheTestComponent, {}));
expect(addReducer).toHaveBeenCalledTimes(1);
expect(addReducer).toHaveBeenCalledWith('', 'x');
expect(addReducer).toHaveReturnedWith('x');
fireEvent.click(screen.getByText(buttonText));
expect(addReducer).toHaveBeenCalledTimes(1);
});
test('reducer call should not be cached if component is not at rendering phase', () => {
const buttonText = 'button';
const addReducer = jest.fn((entity, substring) => {
return entity + substring;
});
const stringUseCase = () => {
const entityReducers = entityUseCase();
return {
...entityReducers,
add: addReducer
};
};
const CacheTestComponent = () => {
const _useUseCase8 = useUseCase('', stringUseCase),
_useUseCase9 = _slicedToArray(_useUseCase8, 2),
add = _useUseCase9[1].add;
const onClick = () => {
add('x');
};
useMount(() => {
add('x');
add('x');
});
return _jsx("button", {
onClick: onClick,
children: buttonText
});
};
render(_jsx(CacheTestComponent, {}));
expect(addReducer).toHaveBeenCalledTimes(2);
expect(addReducer).toHaveBeenNthCalledWith(1, '', 'x');
expect(addReducer).toHaveBeenNthCalledWith(2, '', 'x');
fireEvent.click(screen.getByText(buttonText));
expect(addReducer).toHaveBeenCalledTimes(3);
expect(addReducer).toHaveBeenNthCalledWith(3, '', 'x');
});
test('reducer call should not be cached if entity or parameters have changed', () => {
let count = 0;
let addedText = 'x';
const setButtonText = 'button';
const updateButtonText = 'update';
const addReducer = jest.fn((entity, substring) => {
return entity + substring;
});
const stringUseCase = () => {
const entityReducers = entityUseCase();
return {
...entityReducers,
add: addReducer
};
};
const CacheTestComponent = () => {
const _useUseCase0 = useUseCase('', stringUseCase),
_useUseCase1 = _slicedToArray(_useUseCase0, 2),
_useUseCase1$ = _useUseCase1[1],
add = _useUseCase1$.add,
setEntity = _useUseCase1$.setEntity;
const update = useUpdate();
add(addedText);
const onSet = () => {
setEntity(`${count++}`);
};
const onUpdate = () => {
update();
};
return _jsxs("div", {
children: [_jsx("button", {
onClick: onSet,
children: setButtonText
}), _jsx("button", {
onClick: onUpdate,
children: updateButtonText
})]
});
};
render(_jsx(CacheTestComponent, {}));
expect(addReducer).toHaveBeenCalledTimes(1);
expect(addReducer).toHaveBeenCalledWith('', 'x');
fireEvent.click(screen.getByText(setButtonText));
expect(addReducer).toHaveBeenCalledTimes(2);
expect(addReducer).toHaveBeenCalledWith('0', 'x');
expect(addReducer).toHaveReturnedWith('0x');
addedText = 'y';
fireEvent.click(screen.getByText(updateButtonText));
expect(addReducer).toHaveBeenCalledTimes(3);
expect(addReducer).toHaveBeenCalledWith('0', 'y');
expect(addReducer).toHaveReturnedWith('0y');
fireEvent.click(screen.getByText(updateButtonText));
expect(addReducer).toHaveBeenCalledTimes(3);
});
test('`options.onChange` should be trigger when entity has changed', () => {
const onChange = jest.fn();
const _renderHook23 = renderHook(() => {
return useUseCase(defaultFile, fileUseCase, {
onChange
});
}),
result = _renderHook23.result;
const cores = result.current;
const _cores0 = _slicedToArray(cores, 2),
reducers = _cores0[1];
const setPath = reducers.setPath;
expect(onChange).toHaveBeenCalledTimes(0);
act(() => {
setPath(PATH_1);
});
expect(onChange).toHaveBeenCalledTimes(1);
expect(onChange).toHaveBeenLastCalledWith({
...defaultFile,
path: PATH_1,
ext: EXT_1
}, defaultFile);
});
test('`options.onChange` should always trigger the latest one', () => {
const onChange1 = jest.fn();
const onChange2 = jest.fn();
let onChange = onChange1;
const _renderHook24 = renderHook(() => {
return useUseCase(defaultFile, fileUseCase, {
onChange
}, []);
}),
result = _renderHook24.result;
const cores = result.current;
const _cores1 = _slicedToArray(cores, 2),
reducers = _cores1[1];
const setPath = reducers.setPath;
expect(onChange1).toHaveBeenCalledTimes(0);
act(() => {
setPath(PATH_1);
onChange = onChange2;
});
expect(onChange2).toHaveBeenCalledTimes(0);
expect(onChange1).toHaveBeenCalledTimes(1);
expect(onChange1).toHaveBeenLastCalledWith({
...defaultFile,
path: PATH_1,
ext: EXT_1
}, defaultFile);
act(() => {
setPath(PATH_2);
});
expect(onChange1).toHaveBeenCalledTimes(1);
expect(onChange2).toHaveBeenCalledTimes(1);
expect(onChange2).toHaveBeenLastCalledWith({
...defaultFile,
path: PATH_2,
ext: EXT_2
}, {
...defaultFile,
path: PATH_1,
ext: EXT_1
});
});
test('`options.options` should override rest options', () => {
const _renderHook25 = renderHook(() => {
return useUseCase(defaultFile, fileUseCase, {
pathPrefix: 'xyz/',
options: {
pathPrefix: '123/'
}
});
}),
result = _renderHook25.result;
const cores = result.current;
const _cores10 = _slicedToArray(cores, 2),
path = _cores10[0].path,
reducers = _cores10[1];
const setPath = reducers.setPath;
expect(path).toBe('');
act(() => {
setPath(PATH_1);
});
expect(result.current[0].path).toBe('123/' + PATH_1);
});
test('Option functions: use `T` (not `S`) when `S extends T`', () => {
const testUseCase = function () {
let options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
const entityReducers = entityUseCase();
void options;
return {
...entityReducers
};
};
renderHook(() => {
useUseCase(defaultFile, testUseCase, {
onUpdate(data) {
void data;
return Promise.reject();
}
});
});
// 这个测试仅仅是为了确保类型不会报错,不验证实际逻辑
expect(1).toBe(1);
});
test('when `options` has changed, it should not trigger update', () => {
const onUpdate = jest.fn();
const onDeepUpdate = jest.fn();
const _renderHook26 = renderHook(() => {
const _useState = useState('user/'),
_useState2 = _slicedToArray(_useState, 2),
pathPrefix = _useState2[0],
setPathPrefix = _useState2[1];
const context = useUseCase(defaultFile, fileUseCase, {
pathPrefix
});
useUpdateEffect(() => {
onUpdate();
}, [context]);
useDeepCompareUpdate(() => {
onDeepUpdate();
}, [context]);
return setPathPrefix;
}),
result = _renderHook26.result;
expect(onUpdate).toHaveBeenCalledTimes(0);
expect(onDeepUpdate).toHaveBeenCalledTimes(0);
act(() => {
result.current('my/');
});
expect(onUpdate).toHaveBeenCalledTimes(0);
expect(onDeepUpdate).toHaveBeenCalledTimes(0);
});
test('when `deps` has changed, it should trigger update', () => {
const onUpdate = jest.fn();
const onDeepUpdate = jest.fn();
const _renderHook27 = renderHook(() => {
const _useState3 = useState('user/'),
_useState4 = _slicedToArray(_useState3, 2),
pathPrefix = _useState4[0],
setPathPrefix = _useState4[1];
const context = useUseCase(defaultFile, fileUseCase, {
pathPrefix
}, [pathPrefix]);
useUpdateEffect(() => {
onUpdate();
}, [context]);
useDeepCompareUpdate(() => {
onDeepUpdate();
}, [context]);
return setPathPrefix;
}),
result = _renderHook27.result;
expect(onUpdate).toHaveBeenCalledTimes(0);
expect(onDeepUpdate).toHaveBeenCalledTimes(0);
act(() => {
result.current('my/');
});
expect(onUpdate).toHaveBeenCalledTimes(1);
expect(onDeepUpdate).toHaveBeenCalledTimes(1);
});
test('empty `deps` should not trigger update', () => {
const onUpdate = jest.fn();
const onDeepUpdate = jest.fn();
const _renderHook28 = renderHook(() => {
const _useState5 = useState('user/'),
_useState6 = _slicedToArray(_useState5, 2),
pathPrefix = _useState6[0],
setPathPrefix = _useState6[1];
const context = useUseCase(defaultFile, fileUseCase, {
pathPrefix
}, []);
useUpdateEffect(() => {
onUpdate();
}, [context]);
useDeepCompareUpdate(() => {
onDeepUpdate();
}, [context]);
return setPathPrefix;
}),
result = _renderHook28.result;
expect(onUpdate).toHaveBeenCalledTimes(0);
expect(onDeepUpdate).toHaveBeenCalledTimes(0);
act(() => {
result.current('my/');
});
expect(onUpdate).toHaveBeenCalledTimes(0);
expect(onDeepUpdate).toHaveBeenCalledTimes(0);
});
test(`should call latest functions of usecase options even deps has not change`, () => {
const onPathChange1 = jest.fn();
const onPathChange2 = jest.fn();
let onPathChange = onPathChange1;
const _renderHook29 = renderHook(() => {
return useUseCase(defaultFile, fileUseCase, {
onPathChange
});
}),
result = _renderHook29.result;
const context = result.current;
const _context2 = _slicedToArray(context, 2),
setPath = _context2[1].setPath;
act(() => {
setPath(PATH_1);
onPathChange = onPathChange2;
});
expect(onPathChange1).toHaveBeenCalledTimes(1);
expect(onPathChange2).toHaveBeenCalledTimes(0);
expect(onPathChange1).toHaveBeenLastCalledWith(PATH_1, '');
act(() => {
setPath(PATH_2);
});
expect(onPathChange1).toHaveBeenCalledTimes(1);
expect(onPathChange2).toHaveBeenCalledTimes(1);
expect(onPathChange2).toHaveBeenLastCalledWith(PATH_2, PATH_1);
});
test('`options.watch` should be trigger after entity changed', () => {
const onPathChange = jest.fn();
const onSizeChange = jest.fn();
const _renderHook30 = renderHook(() => {
return useUseCase(defaultFile, fileUseCase, {
watch: {
path: onPathChange,
size: onSizeChange
}
});
}),
result = _renderHook30.result;
const cores = result.current;
const _cores11 = _slicedToArray(cores, 2),
setPath = _cores11[1].setPath;
expect(onPathChange).toHaveBeenCalledTimes(0);
act(() => {
setPath(PATH_1);
});
expect(onPathChange).toHaveBeenCalledTimes(1);
expect(onSizeChange).toHaveBeenCalledTimes(0);
expect(onPathChange).toHaveBeenLastCalledWith({
fieldPaths: ['path'],
newEntity: {
...defaultFile,
path: PATH_1,
ext: EXT_1
},
oldEntity: defaultFile,
newValue: PATH_1,
oldValue: ''
});
});
test('`options.watch` should be trigger after update entity by entity initializer', () => {
const onPathChange = jest.fn();
const _renderHook31 = renderHook(function () {
let path = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : '';
return useUseCase(function () {
let currentFile = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : defaultFile;
return {
...currentFile,
path
};
}, fileUseCase, {
watch: {
path: onPathChange
}
}, [path]);
}),
rerender = _renderHook31.rerender;
expect(onPathChange).toHaveBeenCalledTimes(0);
act(() => {
rerender(PATH_1);
});
expect(onPathChange).toHaveBeenCalledTimes(1);
expect(onPathChange).toHaveBeenLastCalledWith({
fieldPaths: ['path'],
newEntity: {
...defaultFile,
path: PATH_1
},
oldEntity: defaultFile,
newValue: PATH_1,
oldValue: ''
});
act(() => {
rerender(PATH_2);
});
expect(onPathChange).toHaveBeenLastCalledWith({
fieldPaths: ['path'],
newEntity: {
...defaultFile,
path: PATH_2
},
oldEntity: {
...defaultFile,
path: PATH_1
},
newValue: PATH_2,
oldValue: PATH_1
});
});
test('`options.watch` should be trigger after update entity by entity initializer at child component', () => {
const onPathChange = jest.fn();
const TestChild = () => {
useUseCase(fileUseCase, {
watch: {
path: onPathChange
}
});
return _jsx(Fragment, {});
};
const TestParent = _ref5 => {
let file = _ref5.file;
const path = file.path;
const _useUseCase10 = useUseCase(function () {
let currentFile = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : defaultFile;
return {
...currentFile,
path
};
}, fileUseCase, {}, [path]),
_useUseCase11 = _slicedToArray(_useUseCase10, 3),
Provider = _useUseCase11[2];
return _jsx(Provider, {
children: _jsx(TestChild, {})
});
};
const _render = render(_jsx(TestParent, {
file: {
...defaultFile
}
})),
rerender = _render.rerender;
expect(onPathChange).toHaveBeenCalledTimes(0);
act(() => {
rerender(_jsx(TestParent, {
file: {
...defaultFile,
path: PATH_1
}
}));
});
expect(onPathChange).toHaveBeenCalledTimes(1);
expect(onPathChange).toHaveBeenLastCalledWith({
fieldPaths: ['path'],
newEntity: {
...defaultFile,
path: PATH_1
},
oldEntity: defaultFile,
newValue: PATH_1,
oldValue: ''
});
act(() => {
rerender(_jsx(TestParent, {
file: {
...defaultFile,
path: PATH_2
}
}));
});
expect(onPathChange).toHaveBeenLastCalledWith({
fieldPaths: ['path'],
newEntity: {
...defaultFile,
path: PATH_2
},
oldEntity: {
...defaultFile,
path: PATH_1
},
newValue: PATH_2,
oldValue: PATH_1
});
});
test('`options.watch` should trigger the latest one', () => {
const onPathChange1 = jest.fn();
const onPathChange2 = jest.fn();
let onPathChange = onPathChange1;
const _renderHook32 = renderHook(() => {
return useUseCase(defaultFile, fileUseCase, {
watch: {
path: onPathChange
}
});
}),
result = _renderHook32.result;
const cores = result.current;
const _cores12 = _slicedToArray(cores, 2),
_cores12$ = _cores12[1],
setPath = _cores12$.setPath,
setEntity = _cores12$.setEntity;
expect(onPathChange1).toHaveBeenCalledTimes(0);
act(() => {
setPath(PATH_1);
setEntity({
size: 5000
});
onPathChange = onPathChange2;
});
expect(onPathChange1).toHaveBeenCalledTimes(1);
expect(onPathChange1).toHaveBeenLastCalledWith({
fieldPaths: ['path'],
newEntity: {
...defaultFile,
path: PATH_1,
ext: EXT_1
},
oldEntity: defaultFile,
newValue: PATH_1,
oldValue: ''
});
act(() => {
setPath('');
});
expect(onPathChange1).toHaveBeenCalledTimes(1);
expect(onPathChange2).toHaveBeenCalledTimes(1);
expect(onPathChange2).toHaveBeenLastCalledWith({
fieldPaths: ['path'],
newEntity: {
...defaultFile,
size: 5000
},
oldEntity: {
...defaultFile,
path: PATH_1,
ext: EXT_1,
size: 5000
},
newValue: '',
oldValue: PATH_1
});
});
test('`options.watch` should work with array field path of array', () => {
const initialData = {};
const onExtChange = jest.fn();
const onSizeChange = jest.fn();
const _renderHook33 = renderHook(() => {
return useUseCase(initialData, fieldPathUseCase, {
watch: {
'list.ext': onExtChange,
'list.size': onSizeChange
}
});
}),
result = _renderHook33.result;
const cores = result.current;
const _cores13 = _slicedToArray(cores, 2),
setEntity = _cores13[1].setEntity;
act(() => {
setEntity({
list: [defaultFile]
});
});
expect(onExtChange).toHaveBeenCalledTimes(1);
expect(onSizeChange).toHaveBeenCalledTimes(1);
expect(onExtChange).toHaveBeenCalledWith({
fieldPaths: ['list', '0', 'ext'],
newEntity: {
list: [defaultFile]
},
oldEntity: {},
newValue: '',
oldValue: void 0
});
expect(onSizeChange).toHaveBeenCalledWith({
fieldPaths: ['list', '0', 'size'],
newEntity: {
list: [defaultFile]
},
oldEntity: {},
newValue: 0,
oldValue: void 0
});
act(() => {
setEntity({
list: [{
...defaultFile,
ext: EXT_1
}]
});
});
expect(onExtChange).toHaveBeenCalledTimes(2);
expect(onSizeChange).toHaveBeenCalledTimes(1);
expect(onExtChange).toHaveBeenCalledWith({
fieldPaths: ['list', '0', 'ext'],
newEntity: {
list: [{
...defaultFile,
ext: EXT_1
}]
},
oldEntity: {
list: [defaultFile]
},
newValue: EXT_1,
oldValue: ''
});
act(() => {
setEntity({
list: [{
...defaultFile,
ext: EXT_1
}, {
...defaultFile,
ext: EXT_2
}]
});
});
expect(onExtChange).toHaveBeenCalledTimes(3);
expect(onSizeChange).toHaveBeenCalledTimes(2);
expect(onExtChange).toHaveBeenCalledWith({
fieldPaths: ['list', '1', 'ext'],
newEntity: {
list: [{
...defaultFile,
ext: EXT_1
}, {
...defaultFile,
ext: EXT_2
}]
},
oldEntity: {
list: [{
...defaultFile,
ext: EXT_1
}]
},
newValue: EXT_2,
oldValue: void 0
});
expect(onSizeChange).toHaveBeenCalledWith({
fieldPaths: ['list', '1', 'size'],
newEntity: {
list: [{
...defaultFile,
ext: EXT_1
}, {
...defaultFile,
ext: EXT_2
}]
},
oldEntity: {
list: [{
...defaultFile,
ext: EXT_1
}]
},
newValue: 0,
oldValue: void 0
});
act(() => {
setEntity({
nestedList: []
});
});
expect(onExtChange).toHaveBeenCalledTimes(3);
expect(onSizeChange).toHaveBeenCalledTimes(2);
act(() => {
setEntity(() => {
return {
list: [{
...defaultFile,
ext: EXT_2
}, {
...defaultFile,
ext: EXT_1
}, defaultFile]
};
});
});
expect(onExtChange).toHaveBeenCalledTimes(6);
expect(onSizeChange).toHaveBeenCalledTimes(3);
expect(onExtChange).toHaveBeenNthCalledWith(4, {
fieldPaths: ['list', '0', 'ext'],
newEntity: {
list: [{
...defaultFile,
ext: EXT_2
}, {
...defaultFile,
ext: EXT_1
}, defaultFile],
nestedList: []
},
oldEntity: {
list: [{
...defaultFile,
ext: EXT_1
}, {
...defaultFile,
ext: EXT_2
}],
nestedList: []
},
newValue: EXT_2,
oldValue: EXT_1
});
expect(onExtChange).toHaveBeenNthCalledWith(5, {
fieldPaths: ['list', '1', 'ext'],
newEntity: {
list: [{
...defaultFile,
ext: EXT_2
}, {
...defaultFile,
ext: EXT_1
}, defaultFile],
nestedList: []
},
oldEntity: {
list: [{
...defaultFile,
ext: EXT_1
}, {
...defaultFile,
ext: EXT_2
}],
nestedList: []
},
newValue: EXT_1,
oldValue: EXT_2
});
expect(onExtChange).toHaveBeenNthCalledWith(6, {
fieldPaths: ['list', '2', 'ext'],
newEntity: {
list: [{
...defaultFile,
ext: EXT_2
}, {
...defaultFile,
ext: EXT_1
}, defaultFile],
nestedList: []
},
oldEntity: {
list: [{
...defaultFile,
ext: EXT_1
}, {
...defaultFile,
ext: EXT_2
}],
nestedList: []
},
newValue: '',
oldValue: void 0
});
expect(onSizeChange).toHaveBeenCalledWith({
fieldPaths: ['list', '2', 'size'],
newEntity: {
list: [{
...defaultFile,
ext: EXT_2
}, {
...defaultFile,
ext: EXT_1
}, defaultFile],
nestedList: []
},
oldEntity: {
list: [{
...defaultFile,
ext: EXT_1
}, {
...defaultFile,
ext: EXT_2
}],
nestedList: []
},
newValue: 0,
oldValue: void 0
});
act(() => {
setEntity(() => {
return {
list: []
};
});
});
expect(onExtChange).toHaveBeenCalledTimes(9);
expect(onSizeChange).toHaveBeenCalledTimes(6);
const newEntity1 = {
list: [],
nestedList: []
};
expect(onExtChange).toHaveBeenNthCalledWith(7, {
fieldPaths: ['list', '0', 'ext'],
newEntity: newEntity1,
oldEntity: {
...newEntity1,
list: [{
...defaultFile,
ext: EXT_2
}, {
...defaultFile,
ext: EXT_1
}, defaultFile]
},
newValue: void 0,
oldValue: EXT_2
});
expect(onExtChange).toHaveBeenNthCalledWith(8, {
fieldPaths: ['list', '1', 'ext'],
newEntity: newEntity1,
oldEntity: {
...newEntity1,
list: [{
...defaultFile,
ext: EXT_2
}, {
...defaultFile,
ext: EXT_1
}, defaultFile]
},
newValue: void 0,
oldValue: EXT_1
});
expect(onExtChange).toHaveBeenNthCalledWith(9, {
fieldPaths: ['list', '2', 'ext'],
newEntity: newEntity1,
oldEntity: {
...newEntity1,
list: [{
...defaultFile,
ext: EXT_2
}, {
...defaultFile,
ext: EXT_1
}, defaultFile]
},
newValue: void 0,
oldValue: ''
});
expect(onSizeChange).toHaveBeenNthCalledWith(4, {
fieldPaths: ['list', '0', 'size'],
newEntity: newEntity1,
oldEntity: {
...newEntity1,
list: [{
...defaultFile,
ext: EXT_2
}, {
...defaultFile,
ext: EXT_1
}, defaultFile]
},
newValue: void 0,
oldValue: 0
});
expect(onSizeChange).toHaveBeenNthCalledWith(5, {
fieldPaths: ['list', '1', 'size'],
newEntity: newEntity1,
oldEntity: {
...newEntity1,
list: [{
...defaultFile,
ext: EXT_2
}, {
...defaultFile,
ext: EXT_1
}, defaultFile]
},
newValue: void 0,
oldValue: 0
});
expect(onSizeChange).toHaveBeenNthCalledWith(6, {
fieldPaths: ['list', '2', 'size'],
newEntity: newEntity1,
oldEntity: {
...newEntity1,
list: [{
...defaultFile,
ext: EXT_2
}, {
...defaultFile,
ext: EXT_1
}, defaultFile]
},
newValue: void 0,
oldValue: 0
});
});
test('`options.watch` should work with field path of array length', () => {
const defaultList = [];
const onEntityLengthChange = jest.fn();
const onListLengthChange = jest.fn();
const lengthUseCase = () => {
return entityUseCase();
};
const _renderHook34 = renderHook(() => {
return useUseCase(defaultList, lengthUseCase, {
watch: {
length: onEntityLengthChange,
'list.length': onListLengthChange
}
});
}),
result = _renderHook34.result;
const cores = result.current;
const _cores14 = _slicedToArray(cores, 2),
setEntity = _cores14[1].setEntity;
expect(onEntityLengthChange).toHaveBeenCalledTimes(0);
expect(onListLengthChange).toHaveBeenCalledTimes(0);
act(() => {
setEntity([{
list: [defaultFile, defaultFile]
}]);
});
expect(onEntityLengthChange).toHaveBeenCalledTimes(1);
expect(onListLengthChange).toHaveBeenCalledTimes(1);
expect(onEntityLengthChange).toHaveBeenCalledWith({
fieldPaths: ['length'],
newEntity: [{
list: [defaultFile, defaultFile]
}],
oldEntity: [],
newValue: 1,
oldValue: 0
});
expect(onListLengthChange).toHaveBeenCalledWith({
fieldPaths: ['0', 'list', 'length'],
newEntity: [{
list: [defaultFile, defaultFile]
}],
oldEntity: [],
newValue: 2,
oldValue: void 0
});
});
test('`options.watch` should work with field path of nested array', () => {
const initialData = {};
const onChange = jest.fn();
const _renderHook35 = renderHook(() => {
return useUseCase(initialData, fieldPathUseCase, {
watch: {
'nestedList.size': onChange
}
});
}),
result = _renderHook35.result;
const cores = result.current;
const _cores15 = _slicedToArray(cores, 2),
setEntity = _cores15[1].setEntity;
expect(onChange).toHaveBeenCalledTimes(0);
act(() => {
setEntity({
nestedList: [[[defaultFile]]]
});
});
expect(onChange).toHaveBeenCalledTimes(1);
expect(onChange).toHaveBeenCalledWith({
fieldPaths: ['nestedList', '0', '0', '0', 'size'],
newEntity: {
nestedList: [[[defaultFile]]]
},
oldEntity: {},
newValue: 0,
oldValue: void 0
});
act(() => {
setEntity({
nestedList: [[[{
...defaultFile,
size: 100
}, {
...defaultFile,
size: 200
}]], [[{
...defaultFile,
size: 300
}]]]
});
});
expect(onChange).toHaveBeenCalledTimes(4);
expect(onChange).toHaveBeenNthCalledWith(2, {
fieldPaths: ['nestedList', '0', '0', '0', 'size'],
newEntity: {
nestedList: [[[{
...defaultFile,
size: 100
}, {
...defaultFile,
size: 200
}]], [[{
...defaultFile,
size: 300
}]]]
},
oldEntity: {
nestedList: [[[defaultFile]]]
},
newValue: 100,
oldValue: 0
});
expect(onChange).toHaveBeenNthCalledWith(3, {
fieldPaths: ['nestedList', '0', '0', '1', 'size'],
newEntity: {
nestedList: [[[{
...defaultFile,
size: 100
}, {
...defaultFile,
size: 200
}]], [[{
...defaultFile,
size: 300
}]]]
},
oldEntity: {
nestedList: [[[defaultFile]]]
},
newValue: 200,
oldValue: void 0
});
expect(onChange).toHaveBeenNthCalledWith(4, {
fieldPaths: ['nestedList', '1', '0', '0', 'size'],
newEntity: {
nestedList: [[[{
...defaultFile,
size: 100
}, {
...defaultFile,
size: 200
}]], [[{
...defaultFile,
size: 300
}]]]
},
oldEntity: {
nestedList: [[[defaultFile]]]
},
newValue: 300,
oldValue: void 0
});
});
test('`options.watch` should work with field path of object', () => {
const initialData = {};
const onExtChange = jest.fn();
const onSizeChange = jest.fn();
const _renderHook36 = renderHook(() => {
return useUseCase(initialData, fieldPathUseCase, {
watch: {
'obj.file.ext': onExtChange,
'obj.file.size': onSizeChange
}
});
}),
result = _renderHook36.result;
const cores = result.current;
const _cores16 = _slicedToArray(cores, 2),
setEntity = _cores16[1].setEntity;
expect(onExtChange).toHaveBeenCalledTimes(0);
act(() => {
setEntity({
obj: {
file: {
ext: EXT_1
}
}
});
});
expect(onExtChange).toHaveBeenCalledTimes(1);
expect(onSizeChange).toHaveBeenCalledTimes(0);
expect(onExtChange).toHaveBeenCalledWith({
fieldPaths: ['obj', 'file', 'ext'],
newEntity: {
obj: {
file: {
ext: EXT_1
}
}
},
oldEntity: {},
newValue: EXT_1,
oldValue: void 0
});
act(() => {
setEntity(() => {
return {
obj: {
file: {}
}
};
});
});
expect(onExtChange).toHaveBeenCalledTimes(2);
expect(onSizeChange).toHaveBeenCalledTimes(0);
expect(onExtChange).toHaveBeenCalledWith({
fieldPaths: ['obj', 'file', 'ext'],
newEntity: {
obj: {
file: {}
}
},
oldEntity: {
obj: {
file: {
ext: EXT_1
}
}
},
newValue: void 0,
oldValue: EXT_1
});
});
test('multiple `Provider` should work', () => {
const onNumberMount = jest.fn();
const onStringMount = jest.fn();
const onBooleanMount = jest.fn();
const numberUseCase = () => {
return entityUseCase();
};
const stringUseCase = () => {
return entityUseCase();
};
const booleanUseCase = () => {
return entityUseCase();
};
const TestChildComponent = () => {
const _useUseCase12 = useUseCase(numberUseCase),
_useUseCase13 = _slicedToArray(_useUseCase12, 1),
numberValue = _useUseCase13[0];
const _useUseCase14 = useUseCase(stringUseCase),
_useUseCase15 = _slicedToArray(_useUseCase14, 1),
stringValue = _useUseCase15[0];
const _useUseCase16 = useUseCase(booleanUseCase),
_useUseCase17 = _slicedToArray(_useUseCase16, 1),
booleanValue = _useUseCase17[0];
useMount(() => {
onNumberMount(numberValue);
onStringMount(stringValue);
onBooleanMount(booleanValue);
});
return null;
};
const TestParentComponent = () => {
const _useUseCase18 = useUseCase(11, numberUseCase),
_useUseCase19 = _slicedToArray(_useUseCase18, 3),
NumberProvider = _useUseCase19[2];
const _useUseCase20 = useUseCase('hello', stringUseCase),
_useUseCase21 = _slicedToArray(_useUseCase20, 3),
StringProvider = _useUseCase21[2];
const _useUseCase22 = useUseCase(true, booleanUseCase),
_useUseCase23 = _slicedToArray(_useUseCase22, 3),
BooleanProvider = _useUseCase23[2];
return _jsx(NumberProvider, {
with: [StringProvider, BooleanProvider],
children: _jsx(TestChildComponent, {})
});
};
render(_jsx(TestParentComponent, {}));
expect(onNumberMount).toHaveBeenCalledWith(11);
expect(onStringMount).toHaveBeenCalledWith('hello');
expect(onBooleanMount).toHaveBeenCalledWith(true);
});
test('`bindEntity` should be called when component mount', () => {
const bindEntity = jest.fn(entity => {
void entity;
});
const testUseCase = () => {
const entityReducers = entityUseCase();
return {
...entityReducers,
bindEntity
};
};
renderHook(() => {
const _useUseCase24 = useUseCase(5, testUseCase),
_useUseCase25 = _slicedToArray(_useUseCase24, 2),
setEntity = _useUseCase25[1].setEntity;
useMount(() => {
setEntity(100);
});
});
expect(bindEntity).toHaveBeenCalledTimes(1);
expect(bindEntity).toHaveBeenCalledWith(5);
});
test('`unbindEntity` should be called when component unmount', () => {
const unbindEntity = jest.fn(entity => {
void entity;
});
const testUseCase = () => {
const entityReducers = entityUseCase();
return {
...entityReducers,
unbindEntity
};
};
const _renderHook37 = renderHook(() => {
const _useUseCase26 = useUseCase(5, testUseCase),
_useUseCase27 = _slicedToArray(_useUseCase26, 2),
setEntity = _useUseCase27[1].setEntity;
useMount(() => {
setEntity(100);
});
}),
unmount = _renderHook37.unmount;
expect(unbindEntity).toHaveBeenCalledTimes(0);
unmount();
expect(unbindEntity).toHaveBeenCalledTimes(1);
expect(unbindEntity).toHaveBeenCalledWith(100);
});
});
describe('`useUseCase` should work the same as `useContextualCoreCollection`', () => {
test('Provider should be returned in child component', () => {
const onCore = jest.fn();
const TestChild = () => {
const core = useUseCase(fileUseCase);
const length = core.length;
const _core = _slicedToArray(core, 3),
file = _core[0],
Provider = _core[2];
useEffect(() => {
onCore(file, length, typeof Provider);
});
return _jsx(Fragment, {});
};
const TestParent = () => {
const _useUseCase28 = useUseCase(() => {
return {
...defaultFile,
ext: EXT_1
};
}, fileUseCase),
_useUseCase29 = _slicedToArray(_useUseCase28, 3),
Provider = _useUseCase29[2];
return _jsx(Provider, {
children: _jsx(TestChild, {})
});
};
render(_jsx(TestParent, {}));
expect(onCore).toHaveBeenCalledWith({
...defaultFile,
ext: EXT_1
}, 3, 'function');
});
test('Provider should work in child component or popup component', () => {
const onChange = jest.fn();
const onRootFile = jest.fn();
const onPopupFile = jest.fn();
const buttonText = 'button';
const Popup = () => {
const _useUseCase30 = useUseCase(fileUseCase),
_useUseCase31 = _slicedToArray(_useUseCase30, 2),
file = _useUseCase31[0],
setEntity = _useUseCase31[1].setEntity;
const onClick = () => {
setEntity({
ext: EXT_2
});
};
useEffect(() => {
onPopupFile(file);
}, [file]);
return _jsx("button", {
onClick: onClick,
children: buttonText
});
};
const TestChild = () => {
const _useUseCase32 = useUseCase(fileUseCase),
_useUseCase33 = _slicedToArray(_useUseCase32, 3),
Provider = _useUseCase33[2];
return createPortal(_jsx(Provider, {
children: _jsx(Popup, {})
}), document.body);
};
const TestParent = () => {
const _useUseCase34 = useUseCase(() => {
return {
...defaultFile,
ext: EXT_1
};
}, fileUseCase, {
onChange
}),
_useUseCase35 = _slicedToArray(_useUseCase34, 3),
file = _useUseCase35[0],
Provider = _useUseCase35[2];
useEffect(() => {
onRootFile(file);
}, [file]);
return _jsx(Provider, {
children: _jsx(TestChild, {})
});
};
render(_jsx(TestParent, {}));
expect(onRootFile).toHaveBeenCalledWith({
...defaultFile,
ext: EXT_1
});
expect(onPopupFile).toHaveBeenCalledWith({
...defaultFile,
ext: EXT_1
});
fireEvent.click(screen.getByText(buttonText));
expect(onRootFile).toHaveBeenCalledWith({
...defaultFile,
ext: EXT_2
});
expect(onPopupFile).toHaveBeenCalledWith({
...defaultFile,
ext: EXT_2
});
});
test('Should trigger Parent & Child to update', () => {
const onParentUpdate = jest.fn();
const onChildUpdate = jest.fn();
const onPathChange = jest.fn();
const onUndefinedEntity = jest.fn();
const onSetPath = jest.fn(() => {
return PATH_1;
});
render(_jsx("div", {
children: _jsx(Parent, {
onSetPath: onSetPath,
onUpdate: onParentUpdate,
children: _jsx(Child, {
onUpdate: onChildUpdate,
onPathChange: onPathChange,
onUndefinedEntity: onUndefinedEntity
})
})
}));
fireEvent.click(screen.getByText(PARENT_BUTTON_TEXT));
expect(onSetPath).toHaveBeenCalledTimes(1);
expect(onParentUpdate).toHaveBeenCalledTimes(1);
expect(onChildUpdate).toHaveBeenCalledTimes(1);
expect(onPathChange).toHaveBeenCalledWith(PATH_1);
fireEvent.click(screen.getByText(CHILD_BUTTON_TEXT));
expect(onSetPath).toHaveBeenCalledTimes(1);
expect(onParentUpdate).toHaveBeenCalledTimes(2);
expect(onChildUpdate).toHaveBeenCalledTimes(2);
expect(onPathChange).toHaveBeenCalledWith(PATH_2);
expect(onUndefinedEntity).toHaveBeenCalledTimes(0);
});
test('`usecase` should not be called at child component', () => {
const mockUseCase = jest.fn(fileUseCase);
const A = () => {
useUseCase(mockUseCase);
return _jsx(Fragment, {});
};
const B = () => {
const _useUseCase36 = useUseCase(defaultFile, mockUseCase),
_useUseCase37 = _slicedToArray(_useUseCase36, 3),
Provider = _useUseCase37[2];
return _jsx(Provider, {
children: _jsx(A, {})
});
};
render(_jsx(B, {}));
expect(mockUseCase).toHaveBeenCalledTimes(1);
});
test('reducer call should be cached if component is at rendering phase in child components', () => {
const buttonText = 'button';
const addReducer = jest.fn((entity, substring) => {
return entity + substring;
});
const stringUseCase = () => {
const entityReducers = entityUseCase();
return {
...entityReducers,
add: addReducer
};
};
const A = () => {
const _useUseCase38 = useUseCase(stringUseCase),
_useUseCase39 = _slicedToArray(_useUseCase38, 2),
add = _useUseCase39[1].add;
const update = useUpdate();
const onClick = () => {
update();
};
add('x');
return _jsx("button", {
onClick: onClick,
children: buttonText
});
};
const B = () => {
const _useUseCase40 = useUseCase('', stringUseCase),
_useUseCase41 = _slicedToArray(_useUseCase40, 3),
Provider = _useUseCase41[2];
return _jsx(Provider, {
children: _jsx(A, {})
});
};
render(_jsx(B, {}));
expect(addReducer).toHaveBeenCalledTimes(1);
expect(addReducer).toHaveBeenCalledWith('', 'x');
expect(addReducer).toHaveReturnedWith('x');
fireEvent.click(screen.getByText(buttonText));
expect(addReducer).toHaveBeenCalledTimes(1);
});
test('reducer call should not be cached if component is not at rendering phase in child components', () => {
const buttonText = 'button';
const addReducer = jest.fn((entity, substring) => {
return entity + substring;
});
const stringUseCase = () => {
const entityReducers = entityUseCase();
return {
...entityReducers,
add: addReducer
};
};
const A = () => {
const _useUseCase42 = useUseCase(stringUseCase),
_useUseCase43 = _slicedToArray(_useUseCase42, 2),
add = _useUseCase43[1].add;
const onClick = () => {
add('x');
};
useMount(() => {
add('x');
add('x');
});
return _jsx("button", {
onClick: onClick,
children: buttonText
});
};
const B = () => {
const _useUseCase44 = useUseCase('', stringUseCase),
_useUseCase45 = _slicedToArray(_useUseCase44, 3),
Provider = _useUseCase45[2];
return _jsx(Provider, {
children: _jsx(A, {})
});
};
render(_jsx(B, {}));
expect(addReducer).toHaveBeenCalledTimes(2);
expect(addReducer).toHaveBeenNthCalledWith(1, '', 'x');
expect(addReducer).toHaveBeenNthCalledWith(2, '', 'x');
fireEvent.click(screen.getByText(buttonText));
expect(addReducer).toHaveBeenCalledTimes(3);
expect(addReducer).toHaveBeenNthCalledWith(3, '', 'x');
});
test('reducer call should not be cached if entity or parameters have changed in child components', () => {
let count = 0;
let addedText = 'x';
const setButtonText = 'button';
const updateButtonText = 'update';
const addReducer = jest.fn((entity, substring) => {
return entity + substring;
});
const stringUseCase = () => {
const entityReducers = entityUseCase();
return {
...entityReducers,
add: addReducer
};
};
const A = () => {
const _useUseCase46 = useUseCase(stringUseCase),
_useUseCase47 = _slicedToArray(_useUseCase46, 2),
_useUseCase47$ = _useUseCase47[1],
add = _useUseCase47$.add,
setEntity = _useUseCase47$.setEntity;
const update = useUpdate();
add(addedText);
const onSet = () => {
setEntity(`${count++}`);
};
const onUpdate = () => {
update();
};
return _jsxs("div", {
children: [_jsx("button", {
onClick: onSet,
children: setButtonText
}), _jsx("button", {
onClick: onUpdate,
children: updateButtonText
})]
});
};
const B = () => {
const _useUseCase48 = useUseCase('', stringUseCase),
_useUseCase49 = _slicedToArray(_useUseCase48, 3),
Provider = _useUseCase49[2];
return _jsx(Provider, {
children: _jsx(A, {})
});
};
render(_jsx(B, {}));
expect(addReducer).toHaveBeenCalledTimes(1);
expect(addReducer).toHaveBeenCalledWith('', 'x');
fireEvent.click(screen.getByText(setButtonText));
expect(addReducer).toHaveBeenCalledTimes(2);
expect(addReducer).toHaveBeenCalledWith('0', 'x');
expect(addReducer).toHaveReturnedWith('0x');
addedText = 'y';
fireEvent.click(screen.getByText(updateButtonText));
expect(addReducer).toHaveBeenCalledTimes(3);
expect(addReducer).toHaveBeenCalledWith('0', 'y');
expect(addReducer).toHaveReturnedWith('0y');
fireEvent.click(screen.getByText(updateButtonText));
expect(addReducer).toHaveBeenCalledTimes(3);
});
test('`options.onChange` event should be trigger in orders', () => {
let changeTimes = 0;
const updateChangeTimes = () => {
return ++changeTimes;
};
const onChangeA = jest.fn(updateChangeTimes);
const onChangeB = jest.fn(updateChangeTimes);
const onChangeC = jest.fn(updateChangeTimes);
const buttonText = 'my button';
const A = _ref6 => {
let children = _ref6.children;
useUseCase(fileUseCase, {
onChange: onChangeA
});
return _jsx(Fragment, {
children: children
});
};
const B = () => {
useUseCase(fileUseCase, {
onChange: onChangeB
});
return _jsx(Fragment, {});
};
const C = () => {
const _useUseCase50 = useUseCase(defaultFile, fileUseCase, {
onChange: onChangeC
}),
_useUseCase51 = _slicedToArray(_useUseCase50, 3),
setPath = _useUseCase51[1].setPath,
Provider = _useUseCase51[2];
const onFirstClick = () => {
setPath(PATH_1);
};
return _jsxs(Provider, {
children: [_jsx(A, {
children: _jsx(B, {})
}), _jsx("button", {
onClick: onFirstClick,
children: buttonText
})]
});
};
render(_jsx(C, {}));
expect(changeTimes).toBe(0);
fireEvent.click(screen.getByText(buttonText));
expect(onChangeA).toHaveReturnedWith(2);
expect(onChangeB).toHaveReturnedWith(3);
expect(onChangeC).toHaveReturnedWith(1);
});
test('`options.onChange` should always trigger the latest one in child components', () => {
const onFirstChange = jest.fn();
const onSecondChange = jest.fn();
let onChange = onFirstChange;
const firstButtonText = 'first button';
const secondButtonText = 'second button';
const A = () => {
const _useUseCase52 = useUseCase(fileUseCase, {
onChange
}),
_useUseCase53 = _slicedToArray(_useUseCase52, 2),
setPath = _useUseCase53[1].setPath;
const onFirstClick = () => {
setPath(PATH_1);
onChange = onSecondChange;
};
const onSecondClick = () => {
setPath('');
};
return _jsxs(Fragment, {
children: [_jsx("button", {
onClick: onFirstClick,
children: firstButtonText
}), _jsx("button", {
onClick: onSecondClick,
children: secondButtonText
})]
});
};
const B = () => {
const _useUseCase54 = useUseCase(defaultFile, fileUseCase),
_useUseCase55 = _slicedToArray(_useUseCase54, 3),
Provider = _useUseCase55[2];
return _jsx(Provider, {
children: _jsx(A, {})
});
};
render(_jsx(B, {}));
expect(onFirstChange).toHaveBeenCalledTimes(0);
fireEvent.click(screen.getByText(firstButtonText));
expect(onFirstChange).toHaveBeenCalledTimes(1);
expect(onSecondChange).toHaveBeenCalledTimes(0);
fireEvent.click(screen.getByText(secondButtonText));
expect(onFirstChange).toHaveBeenCalledTimes(1);
expect(onSecondChange).toHaveBeenCalledTimes(1);
expect(onSecondChange).toHaveBeenLastCalledWith(defaultFile, {
...defaultFile,
ext: EXT_1,
path: PATH_1
});
});
test('`options.watch` should be trigger at child elements', () => {
const onExtChange = jest.fn();
const firstButtonText = 'first button';
const A = () => {
const _useUseCase56 = useUseCase(fileUseCase, {
watch: {
ext: onExtChange
}
}),
_useUseCase57 = _slicedToArray(_useUseCase56, 2),
setPath = _useUseCase57[1].setPath;
const onFirstClick = () => {
setPath(PATH_1);
};
return _jsx("button", {
onClick: onFirstClick,
children: firstButtonText
});
};
const B = () => {
const _useUseCase58 = useUseCase(defaultFile, fileUseCase, {}),
_useUseCase59 = _slicedToArray(_useUseCase58, 3),
Provider = _useUseCase59[2];
return _jsx(Provider, {
children: _jsx(A, {})
});
};
render(_jsx(B, {}));
expect(onExtChange).toHaveBeenCalledTimes(0);
fireEvent.click(screen.getByText(firstButtonText));
expect(onExtChange).toHaveBeenCalledTimes(1);
expect(onExtChange).toHaveBeenLastCalledWith({
fieldPaths: ['ext'],
newEntity: {
...defaultFile,
ext: EXT_1,
path: PATH_1
},
oldEntity: defaultFile,
newValue: EXT_1,
oldValue: ''
});
});
test('`options.watch` should be trigger in orders', () => {
let changeTimes = 0;
const updateChangeTimes = e => {
void e;
return ++changeTimes;
};
const onChangeA = jest.fn(updateChangeTimes);
const onChangeB = jest.fn(updateChangeTimes);
const onChangeC = jest.fn(updateChangeTimes);
const buttonText = 'my button';
const A = _ref7 => {
let children = _ref7.children;
useUseCase(fileUseCase, {
watch: {
ext: onChangeA
}
});
return _jsx(Fragment, {
children: children
});
};
const B = () => {
useUseCase(fileUseCase, {
watch: {
ext: onChangeB
}
});
return _jsx(Fragment, {});
};
const C = () => {
const _useUseCase60 = useUseCase(defaultFile, fileUseCase, {
watch: {
ext: onChangeC
}
}),
_useUseCase61 = _slicedToArray(_useUseCase60, 3),
setPath = _useUseCase61[1].setPath,
Provider = _useUseCase61[2];
const onFirstClick = () => {
setPath(PATH_1);
};
return _jsxs(Provider, {
children: [_jsx(A, {
children: _jsx(B, {})
}), _jsx("button", {
onClick: onFirstClick,
children: buttonText
})]
});
};
render(_jsx(C, {}));
expect(changeTimes).toBe(0);
fireEvent.click(screen.getByText(buttonText));
expect(onChangeA).toHaveReturnedWith(2);
expect(onChangeB).toHaveReturnedWith(3);
expect(onChangeC).toHaveReturnedWith(1);
});
test('`options.watch` should trigger the latest one in child components', () => {
const onFirstExtChange = jest.fn();
const onSecondExtChange = jest.fn();
let onExtChange = onFirstExtChange;
const firstButtonText = 'first button';
const secondButtonText = 'second button';
const A = () => {
const _useUseCase62 = useUseCase(fileUseCase, {
watch: {
ext: onExtChange
}
}),
_useUseCase63 = _slicedToArray(_useUseCase62, 2),
setPath = _useUseCase63[1].setPath;
const onFirstClick = () => {
setPath(PATH_1);
onExtChange = onSecondExtChange;
};
const onSecondClick = () => {
setPath('');
};
return _jsxs(Fragment, {
children: [_jsx("button", {
onClick: onFirstClick,
children: firstButtonText
}), _jsx("button", {
onClick: onSecondClick,
children: secondButtonText
})]
});
};
const B = () => {
const _useUseCase64 = useUseCase(defaultFile, fileUseCase),
_useUseCase65 = _slicedToArray(_useUseCase64, 3),
Provider = _useUseCase65[2];
return _jsx(Provider, {
children: _jsx(A, {})
});
};
render(_jsx(B, {}));
expect(onFirstExtChange).toHaveBeenCalledTimes(0);
fireEvent.click(screen.getByText(firstButtonText));
expect(onFirstExtChange).toHaveBeenCalledTimes(1);
expect(onSecondExtChange).toHaveBeenCalledTimes(0);
fireEvent.click(screen.getByText(secondButtonText));
expect(onFirstExtChange).toHaveBeenCalledTimes(1);
expect(onSecondExtChange).toHaveBeenCalledTimes(1);
expect(onSecondExtChange).toHaveBeenLastCalledWith({
fieldPaths: ['ext'],
newEntity: defaultFile,
oldEntity: {
...defaultFile,
ext: EXT_1,
path: PATH_1
},
newValue: '',
oldValue: EXT_1
});
});
});
describe('`useUseCase` should work the same as `useReducers`', () => {
test('`entity` should equal `defaultFile`', () => {
const _renderHook38 = renderHook(() => {
return useUseCase(mathUseCase);
}),
result = _renderHook38.result;
const reducers = result.current;
expect(Array.isArray(reducers)).toBe(false);
});
test('returned reducer should work', () => {
const _renderHook39 = renderHook(() => {
return useUseCase(mathUseCase);
}),
result = _renderHook39.result;
const reducers = result.current;
const add = reducers.add;
expect(add(1, 2)).toBe(3);
});
test('`usecase` should be called only once if deps has not provided', () => {
const mockUseCase = jest.fn(mathUseCase);
const _renderHook40 = renderHook(() => {
return useUseCase(mockUseCase);
}),
rerender = _renderHook40.rerender;
expect(mockUseCase).toHaveBeenCalledTimes(1);
rerender();
expect(mockUseCase).toHaveBeenCalledTimes(1);
});
test('`usecase` should be called only once if deps has not changed', () => {
const mockUseCase = jest.fn(mathUseCase);
const _renderHook41 = renderHook(() => {
return useUseCase(mockUseCase, {}, [1, 'x']);
}),
rerender = _renderHook41.rerender;
expect(mockUseCase).toHaveBeenCalledTimes(1);
rerender();
expect(mockUseCase).toHaveBeenCalledTimes(1);
});
test('`usecase` should be called if deps has changed', () => {
let i = 0;
const mockUseCase = jest.fn(mathUseCase);
const _renderHook42 = renderHook(() => {
return useUseCase(mockUseCase, {}, [i++, 'x']);
}),
rerender = _renderHook42.rerender;
expect(mockUseCase).toHaveBeenCalledTimes(1);
rerender();
expect(mockUseCase).toHaveBeenCalledTimes(2);
});
test('reducer call should not be cached if component is at rendering phase', () => {
const buttonText = 'button';
const CacheTestComponent = () => {
const _useUseCase66 = useUseCase(mathUseCase),
subtraction = _useUseCase66.subtraction;
const update = useUpdate();
const onClick = () => {
update();
};
subtraction(5, 3);
return _jsx("button", {
onClick: onClick,
children: buttonText
});
};
render(_jsx(CacheTestComponent, {}));
expect(subtractionReducer).toHaveBeenCalledTimes(1);
expect(subtractionReducer).toHaveBeenCalledWith(5, 3);
expect(subtractionReducer).toHaveReturnedWith(2);
fireEvent.click(screen.getByText(buttonText));
expect(subtractionReducer).toHaveBeenCalledTimes(2);
fireEvent.click(screen.getByText(buttonText));
expect(subtractionReducer).toHaveBeenCalledTimes(3);
});
test('reducer call should not be cached if component is not at rendering phase', () => {
const buttonText = 'button';
const CacheTestComponent = () => {
const _useUseCase67 = useUseCase(mathUseCase),
subtraction = _useUseCase67.subtraction;
const onClick = () => {
subtraction(5, 3);
};
useMount(() => {
subtraction(5, 3);
subtraction(5, 3);
});
return _jsx("button", {
onClick: onClick,
children: buttonText
});
};
render(_jsx(CacheTestComponent, {}));
expect(subtractionReducer).toHaveBeenCalledTimes(2);
expect(subtractionReducer).toHaveBeenNthCalledWith(1, 5, 3);
expect(subtractionReducer).toHaveNthReturnedWith(1, 2);
expect(subtractionReducer).toHaveBeenNthCalledWith(2, 5, 3);
expect(subtractionReducer).toHaveNthReturnedWith(2, 2);
fireEvent.click(screen.getByText(buttonText));
expect(subtractionReducer).toHaveBeenCalledTimes(3);
expect(subtractionReducer).toHaveBeenNthCalledWith(3, 5, 3);
expect(subtractionReducer).toHaveNthReturnedWith(3, 2);
});
test('reducer call should not be cached if parameters have changed', () => {
let count = 0;
const buttonText = 'button';
const StringUseCaseTestComponent = () => {
const _useUseCase68 = useUseCase(mathUseCase),
subtraction = _useUseCase68.subtraction;
const update = useUpdate();
subtraction(5, count++);
const onUpdate = () => {
update();
};
return _jsx("div", {
children: _jsx("button", {
onClick: onUpdate,
children: buttonText
})
});
};
render(_jsx(StringUseCaseTestComponent, {}));
expect(subtractionReducer).toHaveBeenCalledTimes(1);
expect(subtractionReducer).toHaveBeenCalledWith(5, 0);
fireEvent.click(screen.getByText(buttonText));
expect(subtractionReducer).toHaveBeenCalledTimes(2);
expect(subtractionReducer).toHaveBeenCalledWith(5, 1);
expect(subtractionReducer).toHaveReturnedWith(4);
});
test('`options.extraValue` should be added after call `add` method', () => {
const _renderHook43 = renderHook(() => {
return useUseCase(mathUseCase, {
extraValue: 5
});
}),
result = _renderHook43.result;
const reducers = result.current;
const add = reducers.add;
expect(add(1, 2)).toBe(8);
});
test('when `options` has changed, it should not trigger update', () => {
const onUpdate = jest.fn();
const onDeepUpdate = jest.fn();
const _renderHook44 = renderHook(() => {
const _useState7 = useState(0),
_useState8 = _slicedToArray(_useState7, 2),
extraValue = _useState8[0],
setExtraValue = _useState8[1];
const reducers = useUseCase(mathUseCase, {
extraValue
});
useUpdateEffect(() => {
onUpdate();
}, [reducers]);
useDeepCompareUpdate(() => {
onDeepUpdate();
}, [reducers]);
return setExtraValue;
}),
result = _renderHook44.result;
expect(onUpdate).toHaveBeenCalledTimes(0);
expect(onDeepUpdate).toHaveBeenCalledTimes(0);
act(() => {
result.current(3);
});
expect(onUpdate).toHaveBeenCalledTimes(0);
expect(onDeepUpdate).toHaveBeenCalledTimes(0);
});
test('when `deps` change, it should update reducers', () => {
const onUpdate = jest.fn();
const onDeepUpdate = jest.fn();
const _renderHook45 = renderHook(() => {
const _useState9 = useState(0),
_useState0 = _slicedToArray(_useState9, 2),
extraValue = _useState0[0],
setExtraValue = _useState0[1];
const reducers = useUseCase(mathUseCase, {
extraValue
}, [extraValue]);
useUpdateEffect(() => {
onUpdate();
}, [reducers]);
useDeepCompareUpdate(() => {
onDeepUpdate();
}, [reducers]);
return setExtraValue;
}),
result = _renderHook45.result;
expect(onUpdate).toHaveBeenCalledTimes(0);
expect(onDeepUpdate).toHaveBeenCalledTimes(0);
act(() => {
result.current(3);
});
expect(onUpdate).toHaveBeenCalledTimes(1);
expect(onDeepUpdate).toHaveBeenCalledTimes(1);
});
test('empty `deps` should not update reducers', () => {
const onUpdate = jest.fn();
const onDeepUpdate = jest.fn();
const _renderHook46 = renderHook(() => {
const _useState1 = useState(0),
_useState10 = _slicedToArray(_useState1, 2),
extraValue = _useState10[0],
setExtraValue = _useState10[1];
const reducers = useUseCase(mathUseCase, {
extraValue
}, []);
useUpdateEffect(() => {
onUpdate();
}, [reducers]);
useDeepCompareUpdate(() => {
onDeepUpdate();
}, [reducers]);
return setExtraValue;
}),
result = _renderHook46.result;
expect(onUpdate).toHaveBeenCalledTimes(0);
expect(onDeepUpdate).toHaveBeenCalledTimes(0);
act(() => {
result.current(3);
});
expect(onUpdate).toHaveBeenCalledTimes(0);
expect(onDeepUpdate).toHaveBeenCalledTimes(0);
});
test('should not trigger update', () => {
const onUpdate = jest.fn();
render(_jsx(MathParent, {
onUpdate: onUpdate
}));
fireEvent.click(screen.getByText(PARENT_BUTTON_TEXT));
expect(onUpdate).toHaveBeenCalledTimes(0);
});
});
});