UNPKG

@mic-rexjs/usecases-react

Version:

React-based solution for use usecases of Clean Architecture

2,838 lines 90.6 kB
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);
    });
  });
});