@testing-library/react-hooks#act JavaScript Examples

The following examples show how to use @testing-library/react-hooks#act. You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: use-wishlist.spec.js    From horondi_client_fe with MIT License 6 votes vote down vote up
describe('use-wishlist tests', () => {
  let wrap;
  let res;
  beforeEach(() => {
    wrap = renderHook(useWishlist);
  });

  it('should add item to wishlist', () => {
    act(() => {
      wrap.result.current.wishlistOperations.addToWishlist(mockItem);
    });

    expect(wrap.result.current.wishlist).toContain(mockItem);
  });

  it('should check is item in wishlist', () => {
    act(() => {
      res = wrap.result.current.isInWishlist(mockItem);
    });

    expect(res).toEqual(mockItem);
  });

  it('should remove item from wishlist', () => {
    act(() => {
      wrap.result.current.wishlistOperations.removeFromWishlist(mockItem);
    });

    expect(wrap.result.current.wishlist).toHaveLength(0);
  });
});
Example #2
Source File: index.test.js    From react-fluid-table with MIT License 6 votes vote down vote up
describe('useMyHook', () => {
  it('updates every second', () => {
    const { result } = renderHook(() => useMyHook());

    expect(result.current).toBe(0);

    // Fast-forward 1sec
    act(() => {
      jest.advanceTimersByTime(1000);
    });

    // Check after total 1 sec
    expect(result.current).toBe(1);

    // Fast-forward 1 more sec
    act(() => {
      jest.advanceTimersByTime(1000);
    });

    // Check after total 2 sec
    expect(result.current).toBe(2);
  })
})
Example #3
Source File: multiStepIndicator-tests.jest.js    From monday-ui-react-core with MIT License 6 votes vote down vote up
describe("MultiStepIndicator tests", () => {
  it("onClick works and is called once", () => {
    const exampleSteps = [
      {
        status: MultiStepIndicator.stepStatuses.FULFILLED,
        titleText: "Title",
        subtitleText: "Subtitle"
      },
      {
        status: MultiStepIndicator.stepStatuses.ACTIVE,
        titleText: "Active",
        subtitleText: "Active Subtitle"
      }
    ];

    const stepClickMock = jest.fn();

    const multiStepIndicatorComponent = render(
      <MultiStepIndicator type={MultiStepIndicator.types.SUCCESS} steps={exampleSteps} onClick={stepClickMock} />
    );

    const step = multiStepIndicatorComponent.getByText("Title");
    act(() => {
      fireEvent.click(step);
    });
    expect(stepClickMock.mock.calls.length).toBe(1);
  });
});
Example #4
Source File: useDisclosure.spec.js    From rainbow-modules with MIT License 6 votes vote down vote up
describe('useDisclosure', () => {
    it('should set isOpen to true when open function is called', () => {
        const hook = renderHook((value) => useDisclosure(value), {
            initialProps: false,
        });
        expect(hook.result.current.isOpen).toBe(false);
        act(() => {
            hook.result.current.open();
        });
        expect(hook.result.current.isOpen).toBe(true);
    });
    it('should set isOpen to false when close function is called', () => {
        const hook = renderHook((value) => useDisclosure(value), {
            initialProps: true,
        });
        expect(hook.result.current.isOpen).toBe(true);
        act(() => {
            hook.result.current.close();
        });
        expect(hook.result.current.isOpen).toBe(false);
    });
    it('should toggle  value of isOpen when toggle function is called', () => {
        const hook = renderHook((value) => useDisclosure(value), {
            initialProps: false,
        });
        act(() => {
            hook.result.current.toggle();
        });
        expect(hook.result.current.isOpen).toBe(true);
        act(() => {
            hook.result.current.toggle();
        });
        expect(hook.result.current.isOpen).toBe(false);
    });
});
Example #5
Source File: usePrevious.test.js    From tonic-ui with MIT License 6 votes vote down vote up
describe('usePrevious', () => {
  it('should be defined', () => {
    expect(usePrevious).toBeDefined();
  });

  it('should return previous state', () => {
    const { result } = renderHook(() => {
      const [count, setCount] = useState(0);
      return {
        count,
        setCount,
        prevCount: usePrevious(count),
      };
    });
    expect(result.current.prevCount).toBe(undefined);

    act(() => {
      result.current.setCount(2);
    });
    expect(result.current.prevCount).toBe(0);

    act(() => {
      result.current.setCount(4);
    });
    expect(result.current.prevCount).toBe(2);

    act(() => {
      result.current.setCount(6);
    });
    expect(result.current.prevCount).toBe(4);
  });
});
Example #6
Source File: useAuth.js    From plataforma-sabia with MIT License 6 votes vote down vote up
describe('userAuth', () => {
	it('can setUser and logout', () => {
		const { result } = renderHook(() => useAuth(), { wrapper });

		expect(result.current.user).toEqual({});

		act(() => {
			result.current.setUser(testUser);
		});

		expect(result.current.user).toEqual(testUser);

		act(() => {
			result.current.logout();
		});

		expect(result.current.user).toEqual({});
	});
});
Example #7
Source File: useAppReview-test.js    From react-native-in-app-review with MIT License 5 votes vote down vote up
describe('App Review Hook Behavoir', () => {
  const currentDate = new Date('2021-01-10T11:01:58.135Z');
  global.Date = class extends Date {
    constructor(date) {
      if (date) {
        return super(date);
      }

      return currentDate;
    }
  };
  afterAll(() => {
    jest.resetModule();
  });

  beforeEach(() => {
    jest.clearAllMocks();
  });

  it('should trigger InAppReview in cross platform in first time', async () => {
    const expectItemSavedToAsync = ['in_App_Review', new Date().toString()];

    const {result} = renderHook(() => useAppReview());

    await act(() => result.current.onReview());
    expect(AsyncStorage.getItem).toBeCalledWith('in_App_Review');
    expect(AsyncStorage.setItem).toHaveBeenCalledWith(
      ...expectItemSavedToAsync,
    );

    expect(InAppReview.RequestInAppReview).toHaveBeenCalled();
  });

  it('should not trigger InAppReview before 15 days user already get InAppReview', async () => {
    const expectedItem = '2021-01-05';
    jest
      .spyOn(AsyncStorage, 'getItem')
      .mockReturnValueOnce(Promise.resolve(expectedItem));

    const {result} = renderHook(() => useAppReview());

    await act(() => result.current.onReview());
    expect(AsyncStorage.getItem).toBeCalledWith('in_App_Review');

    expect(InAppReview.RequestInAppReview).not.toHaveBeenCalled();
  });

  it('should trigger InAppReview after 15 days user get InAppReview and save Date to async Storage', async () => {
    const expectedItem = '2021-01-26';
    const expectItemSavedToAsync = ['in_App_Review', new Date().toString()];

    jest
      .spyOn(AsyncStorage, 'getItem')
      .mockReturnValueOnce(Promise.resolve(expectedItem));

    jest.spyOn(AsyncStorage, 'setItem');

    const {result} = renderHook(() => useAppReview());

    await act(() => result.current.onReview());

    expect(AsyncStorage.getItem).toBeCalledWith('in_App_Review');
    expect(AsyncStorage.setItem).toHaveBeenCalledWith(
      ...expectItemSavedToAsync,
    );
    expect(InAppReview.RequestInAppReview).toHaveBeenCalled();
  });
});
Example #8
Source File: useExport.test.js    From tasks-frontend with Apache License 2.0 5 votes vote down vote up
describe('useExport', () => {
  let workingExporter = jest.fn(() => Promise.resolve(items));
  let defaultOptions;

  beforeEach(() => {
    defaultOptions = {
      columns,
    };
  });

  it('returns an export config toolbar config', () => {
    defaultOptions.exporter = workingExporter;
    const { result } = renderHook(() => useExport(defaultOptions));
    expect(result.current.toolbarProps.exportConfig).toBeDefined();
    expect(result).toMatchSnapshot();
  });

  it('returns an export config toolbar config', () => {
    defaultOptions.exporter = workingExporter;
    const { result } = renderHook(() =>
      useExport({
        ...defaultOptions,
        isDisabled: true,
      })
    );
    expect(result.current.toolbarProps.exportConfig.isDisabled).toBe(true);
  });

  it('calls the exporter via onSelect', () => {
    defaultOptions.exporter = workingExporter;
    const { result } = renderHook(() => useExport(defaultOptions));

    act(() => {
      result.current.toolbarProps.exportConfig.onSelect(null, 'csv');
    });

    expect(defaultOptions.exporter).toHaveBeenCalled();

    act(() => {
      result.current.toolbarProps.exportConfig.onSelect(null, 'json');
    });

    expect(defaultOptions.exporter).toHaveBeenCalled();
  });
});
Example #9
Source File: index.test.js    From use-infinite-scroll with MIT License 5 votes vote down vote up
describe('useInfiniteScroll', () => {
  let hook;
  let scrollerNode = document.createElement('div');
  let loaderNode = document.createElement('div');

  beforeEach(async () => {
    hook = renderHook(({ hasMore }) => useInfiniteScroll({ hasMore }), {
      initialProps: { hasMore: false },
    });
    const [, loaderRef, scrollerRef] = hook.result.current;
    loaderRef.current = loaderNode;
    scrollerRef.current = scrollerNode;

    hook.rerender({ hasMore: true });
  });

  it('first page should be 0', () => {
    const [page] = hook.result.current;
    expect(page).toBe(0);
  });

  it('should observe the loader node', () => {
    const observer = intersectionMockInstance(loaderNode);
    expect(observer).toBeDefined();
    expect(observer.observe).toHaveBeenCalledWith(loaderNode);
  });

  it('should switch to next page when reaching loaderNode intersection', () => {
    act(() => mockIsIntersecting(loaderNode, true));
    const [page] = hook.result.current;
    expect(page).toBe(1);
  });

  it('should disconnect when there are no more results', () => {
    const observer = intersectionMockInstance(loaderNode);
    expect(observer.disconnect).not.toHaveBeenCalled();

    hook.rerender({ hasMore: false });
    expect(observer.disconnect).toHaveBeenCalled();
  });
});
Example #10
Source File: debugCart.test.js    From use-shopping-cart with MIT License 5 votes vote down vote up
describe('<DebugCart>', () => {
  beforeEach(() => {
    const Wrapper = createWrapper()
    act(() => {
      render(
        <Wrapper>
          <DebugCart />
        </Wrapper>
      )
      return undefined
    })
  })

  it('should make a table of properties and values from the cart', async () => {
    const { cartDetails, ...remainingState } = expectedInitialCartState
    // console.log(cartDetails)
    const tableElement = await screen.findByRole('table')
    expect(tableElement).toBeVisible()
    const cartDetailsCell = await screen.findByRole('cell', {
      name: 'cartDetails'
    })
    expect(cartDetailsCell).toBeVisible()

    const logButton = await findByRole(
      cartDetailsCell.parentElement,
      'button',
      { name: /log value/i }
    )
    expect(logButton).toBeVisible()

    for (const property in remainingState) {
      const keyCell = await screen.findByRole('cell', { name: property })
      const valueCell = await getByRole(keyCell.parentElement, 'cell', {
        name: JSON.stringify(remainingState[property])
      })

      expect(keyCell).toBeVisible()
      expect(valueCell).toBeVisible()
    }
  })
})
Example #11
Source File: menu.jest.js    From monday-ui-react-core with MIT License 5 votes vote down vote up
describe.skip("<Menu />", () => {
  afterEach(() => {
    cleanup();
  });

  it("calls onClick only for the selected menu item when using the mouse", () => {
    const menuComponent = renderComponent();

    const menuItem = menuComponent.getByText(menuItem1Name);

    act(() => {
      fireEvent.mouseOver(menuItem);
      jest.advanceTimersByTime(1000);
      fireEvent.click(menuItem);
    });

    jest.advanceTimersByTime(1000);
    expect(menuItem1OnClickMock.mock.calls.length).toBe(1);
    expect(menuItem2OnClickMock.mock.calls.length).toBe(0);
  });

  it("calls onClick only for the selected menu item when using the enter", () => {
    const menuComponent = renderComponent();
    const menuItem = menuComponent.getByText(menuItem1Name);

    act(() => {
      fireEvent.mouseOver(menuItem);
      jest.advanceTimersByTime(1000);
      fireEvent.keyUp(menuItem, { key: "Enter" });
    });

    jest.advanceTimersByTime(1000);
    expect(menuItem1OnClickMock.mock.calls.length).toBe(1);
    expect(menuItem2OnClickMock.mock.calls.length).toBe(0);
  });

  it("calls onClick only for the selected menu item when using keyboard", () => {
    const menuComponent = renderComponent();
    const menuElement = menuComponent.getByLabelText("menu");

    act(() => {
      fireEvent.keyUp(menuElement, { key: "ArrowDown" });
      jest.advanceTimersByTime(1000);
      fireEvent.keyUp(menuElement, { key: "Enter" });
    });

    jest.advanceTimersByTime(1000);
    expect(menuItem1OnClickMock.mock.calls.length).toBe(1);
    expect(menuItem2OnClickMock.mock.calls.length).toBe(0);
  });
});
Example #12
Source File: index.test.js    From react-use-opentok with MIT License 5 votes vote down vote up
describe('session initialization and connection', () => {
  it('initSession', async () => {
    const { result } = renderHook(() => reactUseOpentok());
    let [opentokProps, opentokMethods] = result.current;

    expect(opentokProps.session).toBeUndefined();
    expect(opentokProps.isSessionInitialized).toBeFalsy();
    await act(() => opentokMethods.initSession(MOCK_CREDENTIALS));

    [opentokProps, opentokMethods] = result.current;
    expect(opentokProps.session).toBeDefined();
    expect(opentokProps.isSessionInitialized).toBeTruthy();
  });

  it('connectSession', async () => {
    const { result } = renderHook(() => reactUseOpentok());
    let [opentokProps, opentokMethods] = result.current;

    try {
      await act(() => opentokMethods.connectSession(MOCK_CREDENTIALS.token));
    } catch (error) {
      expect(error).toMatch(/session/);
    }

    await act(() => opentokMethods.initSession(MOCK_CREDENTIALS));

    [opentokProps, opentokMethods] = result.current;
    expect(opentokProps.isSessionConnected).toBeFalsy();
    expect(opentokProps.connectionId).toBeUndefined();

    try {
      await act(() => opentokMethods.connectSession());
    } catch (error) {
      expect(error).toMatch(/token/);
    }

    [opentokProps, opentokMethods] = result.current;
    await act(() =>
      opentokMethods.connectSession(
        MOCK_CREDENTIALS.token,
        opentokProps.session
      )
    );
    [opentokProps, opentokMethods] = result.current;
    expect(opentokProps.isSessionConnected).toBeTruthy();
    expect(opentokProps.connectionId).toEqual(expect.any(String));
  });

  it('initSessionAndConnect', async () => {
    const { result } = renderHook(() => reactUseOpentok());
    let [opentokProps, opentokMethods] = result.current;
    // expect(1).toBe(1);
    expect(opentokProps.session).toBeUndefined();
    expect(opentokProps.isSessionConnected).toBeFalsy();
    expect(opentokProps.connectionId).toBeUndefined();

    await act(() => opentokMethods.initSessionAndConnect(MOCK_CREDENTIALS));
    // [opentokProps, opentokMethods] = result.current;

    // expect(opentokProps.session).toBeDefined();
    // expect(opentokProps.isSessionConnected).toBeTruthy();
    // expect(opentokProps.connectionId).toEqual(expect.any(String));
  });
});
Example #13
Source File: useGeoLocation.test.js    From js-miniapp with MIT License 5 votes vote down vote up
describe('useGeoLocation', () => {
  let result;
  const dummyCoordinates = {
    latitude: 51.1,
    longitude: 45.3,
  };
  const mockGeolocation = {
    watchPosition: jest.fn(),
    clearWatch: jest.fn(),
    getCurrentPosition: jest.fn().mockImplementation((success) =>
      Promise.resolve(
        success({
          coords: dummyCoordinates,
        })
      )
    ),
  };
  beforeEach(() => {
    result = renderHook(() => useGeoLocation()).result;
    navigator.geolocation = mockGeolocation;
    MiniApp.requestLocationPermission = jest.fn().mockResolvedValue('');
  });

  test('should initialize location hook', () => {
    const [state] = result.current;
    expect(state.isWatching).toEqual(false);
    expect(state.location).toBeUndefined();
  });

  test('should watch location coordinates when permission granted', async () => {
    MiniApp.requestLocationPermission = jest.fn().mockResolvedValue('');

    let [state, watch] = result.current;
    await act(() => watch());
    [state] = result.current;

    expect(state.isWatching).toEqual(true);
    expect(state.location).toEqual(dummyCoordinates);
  });

  test('should not watch location when permission not granted', async () => {
    MiniApp.requestLocationPermission = jest.fn().mockRejectedValue('');

    let [state, watch] = result.current;
    await act(() => watch());
    [state] = result.current;

    expect(state.isWatching).toEqual(false);
    expect(state.location).not.toEqual(dummyCoordinates);
  });

  test('should stop watching location coordinates', async () => {
    let [state, watch, unwatch] = result.current;
    await act(() => watch());
    [state] = result.current;
    expect(state.isWatching).toEqual(true);
    expect(state.location).toEqual(dummyCoordinates);
    act(() => unwatch());
    [state] = result.current;
    expect(state.isWatching).toEqual(false);
    expect(state.location).toBeUndefined();
  });
});
Example #14
Source File: useSnackbar.native.test.js    From blade with MIT License 5 votes vote down vote up
describe('useSnackbar hook', () => {
  it('show the snackbar', () => {
    const { result } = renderHook(() => useSnackbar(), {
      wrapper: Wrapper,
    });
    expect(result.current.isVisible).toBe(false);
    act(() => {
      result.current.show({
        title: 'Snackbar text here',
        autoHide: false,
      });
    });
    expect(result.current.isVisible).toBe(true);
  });

  it('close the snackbar', () => {
    const { result } = renderHook(() => useSnackbar(), {
      wrapper: Wrapper,
    });
    expect(result.current.isVisible).toBe(false);
    act(() => {
      result.current.show({
        title: 'Snackbar text here',
        autoHide: false,
      });
    });
    expect(result.current.isVisible).toBe(true);
    act(() => {
      result.current.close();
    });
    expect(result.current.isVisible).toBe(false);
  });

  it('auto-hide the snackbar', () => {
    jest.useFakeTimers(); // Uses fake timer to resolve setTimeout
    const { result } = renderHook(() => useSnackbar(), {
      wrapper: Wrapper,
    });
    expect(result.current.isVisible).toBe(false);
    act(() => {
      result.current.show({
        title: 'Snackbar text here',
        autoHide: true,
      });
    });
    expect(result.current.isVisible).toBe(true);
    act(() => {
      jest.runAllTimers(); // Resolve auto hide timer
    });
    expect(result.current.isVisible).toBe(false);
  });
});
Example #15
Source File: useCopyToClipboard.test.js    From tonic-ui with MIT License 5 votes vote down vote up
describe('useCopyToClipboard', () => {
  const consoleErrorSpy = jest.spyOn(global.console, 'error').mockImplementation(() => {});
  const originalClipboard = global.navigator.clipboard;

  beforeEach(() => {
    let clipboardData = '';
    const mockClipboard = {
      writeText: jest.fn(data => {
        clipboardData = data;
        return Promise.resolve(clipboardData);
      }),
      readText: jest.fn(() => clipboardData),
    };
    global.navigator.clipboard = mockClipboard;
  });

  afterEach(() => {
    consoleErrorSpy.mockRestore();
  });

  afterAll(() => {
    global.navigator.clipboard = originalClipboard;
  });

  it('should be defined', () => {
    expect(useCopyToClipboard).toBeDefined();
  });

  it('should copy a value to clipboard', async () => {
    const testValue = 'test';
    const { result } = renderHook(() => useCopyToClipboard());
    let [value, copyToClipboard] = result.current;
    expect(value).toBeUndefined();
    await act(async () => {
      const ok = await copyToClipboard(testValue);
      expect(ok).toBe(true);
    });
    expect(global.navigator.clipboard.writeText).toHaveBeenCalledTimes(1);
    expect(global.navigator.clipboard.writeText).toHaveBeenCalledWith(testValue);
    [value] = result.current;
    expect(value).toBe(testValue);
  });

  it('should console error if clipboard not supported', async () => {
    // clipboard not supported
    global.navigator.clipboard = undefined;

    const testValue = 'test';
    const { result } = renderHook(() => useCopyToClipboard());
    let [value, copyToClipboard] = result.current;
    expect(value).toBeUndefined();
    await act(async () => {
      const ok = await copyToClipboard(testValue);
      expect(ok).toBe(false);
    });
    expect(consoleErrorSpy).toBeCalled();
    [value] = result.current;
    expect(value).toBeUndefined();
  });

  it('should console error if clipboard write failed', async () => {
    // clipboard write failed
    global.navigator.clipboard.writeText = jest.fn(() => {
      throw new Error();
    });

    const testValue = 'test';
    const { result } = renderHook(() => useCopyToClipboard());
    let [value, copyToClipboard] = result.current;
    expect(value).toBeUndefined();
    await act(async () => {
      const ok = await copyToClipboard(testValue);
      expect(ok).toBe(false);
    });
    expect(consoleErrorSpy).toBeCalled();
    expect(global.navigator.clipboard.writeText).toBeCalled();
    [value] = result.current;
    expect(value).toBeUndefined();
  });
});
Example #16
Source File: useVisibleComponent.js    From plataforma-sabia with MIT License 5 votes vote down vote up
describe('useVisibleComponent', () => {
	it('should return the default values if initialState is not provided and the user did not click anywhere', () => {
		const { result } = renderHook(() => useVisibleComponent());

		expect(result.current[0].current).toBeNull();
		expect(result.current[1]).toBe(false);

		act(() => {
			result.current[2](true);
		});

		expect(result.current[1]).toBe(true);
	});

	it('should return falsy state only if the user clicks outside the component', () => {
		const { result } = renderHook(() => useVisibleComponent(true));

		expect(result.current[0].current).toBeNull();
		expect(result.current[1]).toBe(true);

		const onClickBtn1 = jest.fn();
		const onClickBtn2 = jest.fn();
		render(
			<>
				<button type="button" onClick={onClickBtn1} ref={result.current[0]}>
					WithRefButton
				</button>
				<button type="button" onClick={onClickBtn2}>
					WithoutRefButton
				</button>
			</>,
		);

		fireEvent.click(screen.getByRole('button', { name: /WithRefButton/i }));
		expect(onClickBtn1).toHaveBeenCalledTimes(1);
		expect(result.current[1]).toBe(true);

		act(() => {
			fireEvent.click(screen.getByRole('button', { name: /WithoutRefButton/i }));
		});
		expect(onClickBtn2).toHaveBeenCalledTimes(1);
		expect(result.current[1]).toBe(false);
	});
});
Example #17
Source File: loadmore.test.js    From 7-react-admin-ts with MIT License 5 votes vote down vote up
describe('useRequest', () => {
  const originalError = console.error;
  beforeAll(() => {
    jest.useFakeTimers();
    console.error = (...args) => {
      if (/Warning.*not wrapped in act/.test(args[0])) {
        return;
      }
      originalError.call(console, ...args);
    };
  });
  afterAll(() => {
    console.error = originalError;
  });

  const asyncFn = ({ pageSize, offset }) =>
    new Promise(resolve => {
      resolve({
        total: dataSource.length,
        list: dataSource.slice(offset, offset + pageSize),
      });
    });

  const setUp = (service, options) => renderHook(() => useRequest(service, options))

  let hook;

  it('useRequest loadMore should work', async () => {
    act(() => {
      hook = setUp(d => asyncFn({
        offset: d ? d.list.length : 0,
        pageSize: 3,
      }), {
        loadMore: true,
        isNoMore: d => (d ? d.total <= d.list.length : false)
      });
    });
    expect(hook.result.current.loading).toEqual(true);
    await hook.waitForNextUpdate();

    expect(hook.result.current.loading).toEqual(false);
    expect(hook.result.current.noMore).toEqual(false);
    expect(hook.result.current.data.list.length).toEqual(3);

    act(() => {
      hook.result.current.loadMore();
    });
    expect(hook.result.current.loading).toEqual(false);
    expect(hook.result.current.loadingMore).toEqual(true);
    expect(hook.result.current.data.list.length).toEqual(3);
    await hook.waitForNextUpdate();
    expect(hook.result.current.loadingMore).toEqual(false);
    expect(hook.result.current.data.list.length).toEqual(6);
    act(() => {
      hook.result.current.loadMore();
    });
    await hook.waitForNextUpdate();
    act(() => {
      hook.result.current.loadMore();
    });
    expect(hook.result.current.loadingMore).toEqual(true);
    expect(hook.result.current.data.list.length).toEqual(9);
    await hook.waitForNextUpdate();
    expect(hook.result.current.loadingMore).toEqual(false);
    expect(hook.result.current.data.list.length).toEqual(10);
    expect(hook.result.current.noMore).toEqual(true);
    act(() => {
      hook.result.current.reload();
    });
    expect(hook.result.current.loading).toEqual(true);
    await hook.waitForNextUpdate();
    expect(hook.result.current.data.list.length).toEqual(3);
    hook.unmount();
  });
});
Example #18
Source File: hooks.test.js    From snowbox with MIT License 4 votes vote down vote up
describe('hooks', () => {
  describe('useList', () => {
    const entity = {
      key: 'tests',
      provider: {
        fetch: jest.fn(fetchImplementation),
        upsert: jest.fn(upsertImplementation),
        remove: jest.fn(removeImplementation),
      },
    };

    beforeEach(() => {
      items = [
        { id: 1 }, { id: 2 }, { id: 3 }, { id: 4 }, { id: 5 }, { id: 6 },
      ];
    });

    it('returns an object', async () => {
      const { result, waitForNextUpdate } = renderHook(() => useList(entity));

      expect(typeof result.current).toBe('object');
      expect(result.current.items).toEqual([]);
      expect(typeof result.current.meta).toBe('object');
      expect(result.current.status).toEqual('pending');
      expect(typeof result.current.setFilters).toBe('function');
      expect(typeof result.current.upsert).toBe('function');
      expect(typeof result.current.remove).toBe('function');

      await waitForNextUpdate();
    });

    it('loads the first page when the component is mounted', async () => {
      const { result, waitForNextUpdate } = renderHook(() =>
        useList(entity, { page: 1 })
      );

      expect(result.current.items).toEqual([]);
      expect(result.current.status).toEqual('pending');

      await waitForNextUpdate();

      expect(result.current.items).toEqual([{ id: 1 }, { id: 2 }]);
      expect(result.current.status).toEqual('succeeded');
    });

    it('loads a new page when the filters change', async () => {
      const { result, waitForNextUpdate } = renderHook(() =>
        useList(entity, { page: 1 })
      );

      await waitForNextUpdate();

      expect(result.current.items).toEqual([{ id: 1 }, { id: 2 }]);
      expect(result.current.meta).toEqual({ total: 6 });
      expect(result.current.status).toEqual('succeeded');

      act(() => result.current.setFilters({ page: 2 }));

      expect(result.current.prevItems).toEqual([{ id: 1 }, { id: 2 }]);
      expect(result.current.prevMeta).toEqual({ total: 6 });
      expect(result.current.items).toEqual([]);
      expect(result.current.meta).toEqual({});
      expect(result.current.status).toEqual('pending');

      await waitForNextUpdate();

      expect(result.current.items).toEqual([{ id: 3 }, { id: 4 }]);
      expect(result.current.meta).toEqual({ total: 6 });
      expect(result.current.status).toEqual('succeeded');
    });

    it ('returns the error when the request fails', async () => {
      const failingEntity = {
        key: 'tests',
        provider: {
          fetch: jest.fn(() => Promise.reject({ ok: false, message: 'Bad' })),
        },
        entitiesPath: 'data',
      };
      const { result, waitForNextUpdate } = renderHook(() =>
        useList(failingEntity, { page: 1 })
      );

      await waitForNextUpdate();

      expect(result.current.items).toEqual([]);
      expect(result.current.meta).toEqual({});
      expect(result.current.status).toEqual('failed');
      expect(result.current.error).toEqual({ ok: false, message: 'Bad' });
    });

    it('does not load again the page when it exists and is valid', async () => {
      const staleEntity = {
        ...entity,
        staleTimeout: 100,
      };
      const { result, waitForNextUpdate } = renderHook(() =>
        useList(staleEntity, { page: 1 })
      );

      await waitForNextUpdate();

      expect(result.current.items).toEqual([{ id: 1 }, { id: 2 }]);
      expect(result.current.meta).toEqual({ total: 6 });
      expect(result.current.status).toEqual('succeeded');

      act(() => result.current.setFilters({ page: 1 }));

      expect(result.current.prevItems).toEqual([{ id: 1 }, { id: 2 }]);
      expect(result.current.prevMeta).toEqual({ total: 6 });
      expect(result.current.items).toEqual([{ id: 1 }, { id: 2 }]);
      expect(result.current.meta).toEqual({ total: 6 });
      expect(result.current.status).toEqual('succeeded');
    });

    it('loads again the page when a new item is added', async () => {
      const { result, waitForNextUpdate } = renderHook(() =>
        useList(entity, { page: 1 })
      );

      await waitForNextUpdate();

      expect(result.current.items).toEqual([{ id: 1 }, { id: 2 }]);
      expect(result.current.meta).toEqual({ total: 6 });
      expect(result.current.status).toEqual('succeeded');

      await act(() => result.current.upsert({ id: 9 }));

      expect(result.current.items).toEqual([{ id: 1 }, { id: 2 }]);
      expect(result.current.meta).toEqual({ total: 7 });
      expect(result.current.status).toEqual('succeeded');
    });

    it('loads again the page when an item is removed', async () => {
      const { result, waitForNextUpdate } = renderHook(() =>
        useList(entity, { page: 1 })
      );

      await waitForNextUpdate();

      expect(result.current.items).toEqual([{ id: 1 }, { id: 2 }]);
      expect(result.current.meta).toEqual({ total: 6 });
      expect(result.current.status).toEqual('succeeded');

      await act(() => result.current.remove({ index: 1 }));

      expect(result.current.items).toEqual([{ id: 1 }, { id: 3 }]);
      expect(result.current.meta).toEqual({ total: 5 });
      expect(result.current.status).toEqual('succeeded');
    });

    it('does nothing when the upsert request fails', async () => {
      const failingEntity = {
        ...entity,
        provider: {
          ...entity.provider,
          upsert: () => Promise.reject({ problems: true }),
        },
      };
      const { result, waitForNextUpdate } = renderHook(() =>
        useList(failingEntity, { page: 1 })
      );

      await waitForNextUpdate();

      expect(result.current.items).toEqual([{ id: 1 }, { id: 2 }]);
      expect(result.current.meta).toEqual({ total: 6 });
      expect(result.current.status).toEqual('succeeded');

      try {
        await act(() => result.current.upsert({ index: 1 }));
      } catch (e) {
        expect(e).toEqual({ problems: true });
      }

      expect(result.current.items).toEqual([{ id: 1 }, { id: 2 }]);
      expect(result.current.meta).toEqual({ total: 6 });
      expect(result.current.status).toEqual('succeeded');
    });

    it('does nothing when the remove request fails', async () => {
      const failingEntity = {
        ...entity,
        provider: {
          ...entity.provider,
          remove: () => Promise.reject({ problems: true }),
        },
      };
      const { result, waitForNextUpdate } = renderHook(() =>
        useList(failingEntity, { page: 1 })
      );

      await waitForNextUpdate();

      expect(result.current.items).toEqual([{ id: 1 }, { id: 2 }]);
      expect(result.current.meta).toEqual({ total: 6 });
      expect(result.current.status).toEqual('succeeded');

      try {
        await act(() => result.current.remove({ index: 1 }));
      } catch (e) {
        expect(e).toEqual({ problems: true });
      }

      expect(result.current.items).toEqual([{ id: 1 }, { id: 2 }]);
      expect(result.current.meta).toEqual({ total: 6 });
      expect(result.current.status).toEqual('succeeded');
    });
  });
});
Example #19
Source File: useSelector.spec.js    From Learning-Redux with MIT License 4 votes vote down vote up
describe('React', () => {
  describe('hooks', () => {
    describe('useSelector', () => {
      let store
      let renderedItems = []

      beforeEach(() => {
        store = createStore(({ count } = { count: -1 }) => ({
          count: count + 1,
        }))
        renderedItems = []
      })

      afterEach(() => rtl.cleanup())

      describe('core subscription behavior', () => {
        it('selects the state on initial render', () => {
          const { result } = renderHook(() => useSelector((s) => s.count), {
            wrapper: (props) => <ProviderMock {...props} store={store} />,
          })

          expect(result.current).toEqual(0)
        })

        it('selects the state and renders the component when the store updates', () => {
          const selector = jest.fn((s) => s.count)

          const { result } = renderHook(() => useSelector(selector), {
            wrapper: (props) => <ProviderMock {...props} store={store} />,
          })

          expect(result.current).toEqual(0)
          expect(selector).toHaveBeenCalledTimes(2)

          act(() => {
            store.dispatch({ type: '' })
          })

          expect(result.current).toEqual(1)
          expect(selector).toHaveBeenCalledTimes(3)
        })
      })

      describe('lifecycle interactions', () => {
        it('always uses the latest state', () => {
          store = createStore((c) => c + 1, -1)

          const Comp = () => {
            const selector = useCallback((c) => c + 1, [])
            const value = useSelector(selector)
            renderedItems.push(value)
            return <div />
          }

          rtl.render(
            <ProviderMock store={store}>
              <Comp />
            </ProviderMock>
          )

          expect(renderedItems).toEqual([1])

          store.dispatch({ type: '' })

          expect(renderedItems).toEqual([1, 2])
        })

        it('subscribes to the store synchronously', () => {
          let rootSubscription

          const Parent = () => {
            const { subscription } = useReduxContext()
            rootSubscription = subscription
            const count = useSelector((s) => s.count)
            return count === 1 ? <Child /> : null
          }

          const Child = () => {
            const count = useSelector((s) => s.count)
            return <div>{count}</div>
          }

          rtl.render(
            <ProviderMock store={store}>
              <Parent />
            </ProviderMock>
          )

          expect(rootSubscription.listeners.get().length).toBe(1)

          store.dispatch({ type: '' })

          expect(rootSubscription.listeners.get().length).toBe(2)
        })

        it('unsubscribes when the component is unmounted', () => {
          let rootSubscription

          const Parent = () => {
            const { subscription } = useReduxContext()
            rootSubscription = subscription
            const count = useSelector((s) => s.count)
            return count === 0 ? <Child /> : null
          }

          const Child = () => {
            const count = useSelector((s) => s.count)
            return <div>{count}</div>
          }

          rtl.render(
            <ProviderMock store={store}>
              <Parent />
            </ProviderMock>
          )

          expect(rootSubscription.listeners.get().length).toBe(2)

          store.dispatch({ type: '' })

          expect(rootSubscription.listeners.get().length).toBe(1)
        })

        it('notices store updates between render and store subscription effect', () => {
          const Comp = () => {
            const count = useSelector((s) => s.count)
            renderedItems.push(count)

            // I don't know a better way to trigger a store update before the
            // store subscription effect happens
            if (count === 0) {
              store.dispatch({ type: '' })
            }

            return <div>{count}</div>
          }

          rtl.render(
            <ProviderMock store={store}>
              <Comp />
            </ProviderMock>
          )

          expect(renderedItems).toEqual([0, 1])
        })
      })

      it('works properly with memoized selector with dispatch in Child useLayoutEffect', () => {
        store = createStore((c) => c + 1, -1)

        const Comp = () => {
          const selector = useCallback((c) => c, [])
          const count = useSelector(selector)
          renderedItems.push(count)
          return <Child parentCount={count} />
        }

        const Child = ({ parentCount }) => {
          useLayoutEffect(() => {
            if (parentCount === 1) {
              store.dispatch({ type: '' })
            }
          }, [parentCount])
          return <div>{parentCount}</div>
        }

        rtl.render(
          <ProviderMock store={store}>
            <Comp />
          </ProviderMock>
        )

        // The first render doesn't trigger dispatch
        expect(renderedItems).toEqual([0])

        // This dispatch triggers another dispatch in useLayoutEffect
        rtl.act(() => {
          store.dispatch({ type: '' })
        })

        expect(renderedItems).toEqual([0, 1, 2])
      })

      describe('performance optimizations and bail-outs', () => {
        it('defaults to ref-equality to prevent unnecessary updates', () => {
          const state = {}
          store = createStore(() => state)

          const Comp = () => {
            const value = useSelector((s) => s)
            renderedItems.push(value)
            return <div />
          }

          rtl.render(
            <ProviderMock store={store}>
              <Comp />
            </ProviderMock>
          )

          expect(renderedItems.length).toBe(1)

          store.dispatch({ type: '' })

          expect(renderedItems.length).toBe(1)
        })

        it('allows other equality functions to prevent unnecessary updates', () => {
          store = createStore(
            ({ count, stable } = { count: -1, stable: {} }) => ({
              count: count + 1,
              stable,
            })
          )

          const Comp = () => {
            const value = useSelector((s) => Object.keys(s), shallowEqual)
            renderedItems.push(value)
            return <div />
          }

          rtl.render(
            <ProviderMock store={store}>
              <Comp />
            </ProviderMock>
          )

          expect(renderedItems.length).toBe(1)

          store.dispatch({ type: '' })

          expect(renderedItems.length).toBe(1)
        })
      })

      it('uses the latest selector', () => {
        let selectorId = 0
        let forceRender

        const Comp = () => {
          const [, f] = useReducer((c) => c + 1, 0)
          forceRender = f
          const renderedSelectorId = selectorId++
          const value = useSelector(() => renderedSelectorId)
          renderedItems.push(value)
          return <div />
        }

        rtl.render(
          <ProviderMock store={store}>
            <Comp />
          </ProviderMock>
        )

        expect(renderedItems).toEqual([0])

        rtl.act(forceRender)
        expect(renderedItems).toEqual([0, 1])

        rtl.act(() => {
          store.dispatch({ type: '' })
        })
        expect(renderedItems).toEqual([0, 1])

        rtl.act(forceRender)
        expect(renderedItems).toEqual([0, 1, 2])
      })

      describe('edge cases', () => {
        it('ignores transient errors in selector (e.g. due to stale props)', () => {
          const spy = jest.spyOn(console, 'error').mockImplementation(() => {})

          const Parent = () => {
            const count = useSelector((s) => s.count)
            return <Child parentCount={count} />
          }

          const Child = ({ parentCount }) => {
            const result = useSelector(({ count }) => {
              if (count !== parentCount) {
                throw new Error()
              }

              return count + parentCount
            })

            return <div>{result}</div>
          }

          rtl.render(
            <ProviderMock store={store}>
              <Parent />
            </ProviderMock>
          )

          expect(() => store.dispatch({ type: '' })).not.toThrowError()

          spy.mockRestore()
        })

        it('correlates the subscription callback error with a following error during rendering', () => {
          const spy = jest.spyOn(console, 'error').mockImplementation(() => {})

          const Comp = () => {
            const result = useSelector((count) => {
              if (count > 0) {
                throw new Error('foo')
              }

              return count
            })

            return <div>{result}</div>
          }

          const store = createStore((count = -1) => count + 1)

          const App = () => (
            <ProviderMock store={store}>
              <Comp />
            </ProviderMock>
          )

          rtl.render(<App />)

          expect(() => store.dispatch({ type: '' })).toThrow(
            /The error may be correlated/
          )

          spy.mockRestore()
        })

        it('re-throws errors from the selector that only occur during rendering', () => {
          const spy = jest.spyOn(console, 'error').mockImplementation(() => {})

          const Parent = () => {
            const count = useSelector((s) => s.count)
            return <Child parentCount={count} />
          }

          const Child = ({ parentCount }) => {
            const result = useSelector(({ count }) => {
              if (parentCount > 0) {
                throw new Error()
              }

              return count + parentCount
            })

            return <div>{result}</div>
          }

          rtl.render(
            <ProviderMock store={store}>
              <Parent />
            </ProviderMock>
          )

          expect(() => store.dispatch({ type: '' })).toThrowError()

          spy.mockRestore()
        })

        it('allows dealing with stale props by putting a specific connected component above the hooks component', () => {
          const spy = jest.spyOn(console, 'error').mockImplementation(() => {})

          const Parent = () => {
            const count = useSelector((s) => s.count)
            return <ConnectedWrapper parentCount={count} />
          }

          const ConnectedWrapper = connect(({ count }) => ({ count }))(
            ({ parentCount }) => {
              return <Child parentCount={parentCount} />
            }
          )

          let sawInconsistentState = false

          const Child = ({ parentCount }) => {
            const result = useSelector(({ count }) => {
              if (count !== parentCount) {
                sawInconsistentState = true
              }

              return count + parentCount
            })

            return <div>{result}</div>
          }

          rtl.render(
            <ProviderMock store={store}>
              <Parent />
            </ProviderMock>
          )

          store.dispatch({ type: '' })

          expect(sawInconsistentState).toBe(false)

          spy.mockRestore()
        })

        it('reuse latest selected state on selector re-run', () => {
          store = createStore(({ count } = { count: -1 }) => ({
            count: count + 1,
          }))

          const alwaysEqual = () => true

          const Comp = () => {
            // triggers render on store change
            useSelector((s) => s.count)
            const array = useSelector(() => [1, 2, 3], alwaysEqual)
            renderedItems.push(array)
            return <div />
          }

          rtl.render(
            <ProviderMock store={store}>
              <Comp />
            </ProviderMock>
          )

          expect(renderedItems.length).toBe(1)

          store.dispatch({ type: '' })

          expect(renderedItems.length).toBe(2)
          expect(renderedItems[0]).toBe(renderedItems[1])
        })
      })

      describe('error handling for invalid arguments', () => {
        it('throws if no selector is passed', () => {
          expect(() => useSelector()).toThrow()
        })

        it('throws if selector is not a function', () => {
          expect(() => useSelector(1)).toThrow()
        })

        it('throws if equality function is not a function', () => {
          expect(() => useSelector((s) => s.count, 1)).toThrow()
        })
      })
    })

    describe('createSelectorHook', () => {
      let defaultStore
      let customStore

      beforeEach(() => {
        defaultStore = createStore(({ count } = { count: -1 }) => ({
          count: count + 1,
        }))
        customStore = createStore(({ count } = { count: 10 }) => ({
          count: count + 2,
        }))
      })

      it('subscribes to the correct store', () => {
        const nestedContext = React.createContext(null)
        const useCustomSelector = createSelectorHook(nestedContext)
        let defaultCount = null
        let customCount = null

        const getCount = (s) => s.count

        const DisplayDefaultCount = ({ children = null }) => {
          const count = useSelector(getCount)
          defaultCount = count
          return <>{children}</>
        }
        const DisplayCustomCount = ({ children = null }) => {
          const count = useCustomSelector(getCount)
          customCount = count
          return <>{children}</>
        }

        rtl.render(
          <ProviderMock store={defaultStore}>
            <ProviderMock context={nestedContext} store={customStore}>
              <DisplayCustomCount>
                <DisplayDefaultCount />
              </DisplayCustomCount>
            </ProviderMock>
          </ProviderMock>
        )

        expect(defaultCount).toBe(0)
        expect(customCount).toBe(12)
      })
    })
  })
})
Example #20
Source File: index.test.js    From use-shopping-cart with MIT License 4 votes vote down vote up
describe('useShoppingCart()', () => {
  beforeEach(() => {
    cart = reload()
  })

  it('has the expected initial state', () => {
    expect(cart.current).toMatchObject(expectedInitialCartState)
  })

  describe('shouldDisplayCart', () => {
    it('is false by default', () => {
      expect(cart.current.shouldDisplayCart).toBe(false)
    })

    it('sets shouldDisplayCart to true after running handleCartClick() once', () => {
      act(() => {
        cart.current.handleCartClick()
      })

      expect(cart.current.shouldDisplayCart).toBe(true)
    })

    it('is toggled by handleCartClick()', () => {
      act(() => {
        cart.current.handleCartClick()
      })
      expect(cart.current.shouldDisplayCart).toBe(true)

      act(() => {
        cart.current.handleCartClick()
      })
      expect(cart.current.shouldDisplayCart).toBe(false)
    })

    it.todo('handleCartHover()')

    it('is set to false by handleCloseCart()', () => {
      act(() => {
        cart.current.handleCartClick()
        cart.current.handleCloseCart()
      })
      expect(cart.current.shouldDisplayCart).toBe(false)
    })
  })

  describe('addItem()', () => {
    it('adds an item to the cart', () => {
      const product = mockProduct({ price: 200 })

      act(() => {
        cart.current.addItem(product)
      })

      expect(cart.current.cartDetails).toHaveProperty(product.id)
      const entry = cart.current.cartDetails[product.id]

      expect(entry.quantity).toBe(1)
      expect(entry.value).toBe(product.price)
      expect(entry.formattedValue).toBe('$2.00')
      expect(entry).toMatchObject(product)

      expect(cart.current.cartCount).toBe(1)
      expect(cart.current.totalPrice).toBe(200)
    })

    it('adds `count` amount of items to the cart', () => {
      let product
      let count = 1

      while (count <= 50) {
        product = mockProduct()
        act(() => {
          cart.current.addItem(product, { count: 1 })
        })
        count++
      }

      expect(cart.current.cartDetails).toHaveProperty(product.id)

      const totalValue = Object.keys(cart.current.cartDetails)
        .map((item) => cart.current.cartDetails[item].value)
        .reduce((acc, current) => acc + current)

      expect(cart.current.cartCount).toBe(50)
      expect(cart.current.totalPrice).toBe(totalValue)
    })

    it('adds multiple different items to the cart', () => {
      const product1 = mockProduct({ price: 400 })
      const product2 = mockProduct({ price: 100 })

      act(() => {
        cart.current.addItem(product1)
        cart.current.addItem(product2)
      })

      expect(cart.current.cartDetails).toHaveProperty(product1.id)
      const entry1 = cart.current.cartDetails[product1.id]

      expect(entry1.quantity).toBe(1)
      expect(entry1.value).toBe(product1.price)

      expect(cart.current.cartDetails).toHaveProperty(product2.id)
      const entry2 = cart.current.cartDetails[product2.id]

      expect(entry2.quantity).toBe(1)
      expect(entry2.value).toBe(product2.price)

      expect(cart.current.cartCount).toBe(2)
      expect(cart.current.totalPrice).toBe(500)
    })

    it('adds multiple of the same item to the cart', () => {
      const product = mockProduct({ price: 325 })

      act(() => {
        cart.current.addItem(product)
        cart.current.addItem(product)
      })

      expect(cart.current.cartDetails).toHaveProperty(product.id)
      const entry = cart.current.cartDetails[product.id]

      expect(entry.quantity).toBe(2)
      expect(entry.value).toBe(650)
      expect(entry.formattedValue).toBe('$6.50')

      expect(cart.current.cartCount).toBe(2)
      expect(cart.current.totalPrice).toBe(650)
    })

    it('adds price_data from the metadata object from the 3rd param', () => {
      const product = mockProduct()

      act(() => {
        cart.current.addItem(product, {
          price_metadata: {
            type: 'tacos',
            test: 'testing'
          }
        })
      })

      expect(cart.current.cartDetails[product.id].price_data).toStrictEqual({
        type: 'tacos',
        test: 'testing'
      })
    })

    it('successfully stacks data to price_data if there is already content there', () => {
      const product = mockProduct({ price_data: { test: 'static metadata' } })

      act(() => {
        cart.current.addItem(product, {
          price_metadata: {
            dynamicTest: 'dynamic data'
          }
        })
      })

      expect(cart.current.cartDetails[product.id].price_data).toStrictEqual({
        test: 'static metadata',
        dynamicTest: 'dynamic data'
      })
    })

    it('adds product_data from the metadata object from the options object', () => {
      const product = mockProduct()

      act(() => {
        cart.current.addItem(product, {
          count: 1,
          product_metadata: { type: 'tacos', test: 'testing' }
        })
      })

      expect(cart.current.cartDetails[product.id].product_data).toStrictEqual({
        type: 'tacos',
        test: 'testing'
      })
    })

    it('successfully stacks data to product_data if there is already content there', () => {
      const product = mockProduct({
        product_data: { test: 'static metadata' }
      })

      act(() => {
        cart.current.addItem(product)
        cart.current.addItem(product, {
          product_metadata: {
            dynamicTest: 'dynamic product data'
          }
        })
      })

      expect(cart.current.cartDetails[product.id].product_data).toStrictEqual({
        test: 'static metadata',
        dynamicTest: 'dynamic product data'
      })
    })
  })

  describe('removeItem()', () => {
    it('removes the item from the cart', () => {
      const product = mockProduct()

      act(() => {
        cart.current.addItem(product)
        cart.current.removeItem(product.id)
      })

      expect(cart.current.cartDetails).not.toHaveProperty(product.id)
    })

    it('should remove correct item', () => {
      const product1 = mockProduct()
      const product2 = mockProduct()

      act(() => {
        cart.current.addItem(product1)
        cart.current.addItem(product2)
        cart.current.removeItem(product1.id)
      })

      expect(cart.current.cartDetails).not.toHaveProperty(product1.id)
      expect(cart.current.cartDetails).toHaveProperty(product2.id)
    })
  })

  describe('incrementItem()', () => {
    it('adds one more of that product to the cart', () => {
      const product = mockProduct()

      act(() => {
        cart.current.addItem(product)
        cart.current.incrementItem(product.id)
      })

      expect(cart.current.cartDetails).toHaveProperty(product.id)
      const entry = cart.current.cartDetails[product.id]

      expect(entry.quantity).toBe(2)
      expect(entry.value).toBe(product.price * 2)
      expect(cart.current.cartCount).toBe(2)
    })
  })

  describe('decrementItem()', () => {
    it('removes one of that item from the cart', () => {
      const product = mockProduct({ price: 256 })

      act(() => {
        cart.current.addItem(product, { count: 3 })
        cart.current.decrementItem(product.id)
      })

      expect(cart.current.cartDetails).toHaveProperty(product.id)
      const entry = cart.current.cartDetails[product.id]

      expect(entry.quantity).toBe(2)
      expect(entry.value).toBe(512)
      expect(entry.formattedValue).toBe('$5.12')

      expect(cart.current.cartCount).toBe(2)
      expect(cart.current.totalPrice).toBe(512)
    })

    it.todo('removes `count` amount of that item from the cart')

    it('removes the item from the cart if the quantity reaches 0', () => {
      const product = mockProduct()

      act(() => {
        cart.current.addItem(product)
        cart.current.decrementItem(product.id)
      })

      expect(cart.current.cartDetails).not.toHaveProperty(product.id)
      expect(cart.current.totalPrice).toBe(0)
      expect(cart.current.cartCount).toBe(0)
    })

    it('does not let you have negative quantities', () => {
      const product = mockProduct()

      act(() => {
        cart.current.addItem(product)
        cart.current.decrementItem(product.id, { count: 5 })
      })

      expect(cart.current.cartDetails).not.toHaveProperty(product.id)
      expect(cart.current.totalPrice).toBe(0)
      expect(cart.current.cartCount).toBe(0)
    })

    it('should decrement correct item', () => {
      const product1 = mockProduct()
      const product2 = mockProduct()

      act(() => {
        cart.current.addItem(product1, { count: 2 })
        cart.current.addItem(product2, { count: 4 })
        cart.current.decrementItem(product2.id)
      })

      expect(cart.current.cartDetails[product1.id].quantity).toBe(2)
      expect(cart.current.cartDetails[product2.id].quantity).toBe(3)
    })
  })

  describe('setItemQuantity()', () => {
    it('updates the quantity correctly', () => {
      const product = mockProduct()

      act(() => {
        cart.current.addItem(product, { count: 1 })
        cart.current.setItemQuantity(product.id, 5)
      })
      const entry = cart.current.cartDetails[product.id]

      expect(entry.quantity).toBe(5)
      expect(entry.value).toBe(product.price * 5)
    })

    it('removes the item when quantity is set to 0', () => {
      const product = mockProduct()

      act(() => {
        cart.current.addItem(product, { count: 10 })
        cart.current.setItemQuantity(product.id, 0)
      })

      expect(cart.current.cartDetails).not.toHaveProperty(product.id)
    })
  })

  describe('loadCart()', () => {
    let cartDetails, product

    beforeEach(() => {
      cartDetails = mockCartDetails()
      product = mockProduct({ price: 200 })
    })

    it('should add cartDetails to cart object', async () => {
      act(() => {
        cart.current.addItem(product)
        cart.current.loadCart(cartDetails, false)
      })

      expect(cart.current).toMatchObject({
        cartDetails,
        totalPrice: 1800,
        cartCount: 6
      })
    })

    it('should merge two cart details items by default', async () => {
      act(() => {
        cart.current.addItem(product)
        cart.current.incrementItem(product.id)
        cart.current.loadCart(cartDetails)
      })

      expect(cart.current).toMatchObject({
        totalPrice: 2200,
        cartCount: 8,
        cartDetails: {
          [product.id]: product,
          ...cartDetails
        }
      })
    })
  })

  describe('storeLastClicked()', () => {
    it('updates lastClicked', () => {
      const product = mockProduct()
      act(() => {
        cart.current.storeLastClicked(product.id)
      })
      expect(cart.current.lastClicked).toBe(product.id)
    })
  })
})
Example #21
Source File: use-certificates.spec.js    From horondi_admin with MIT License 4 votes vote down vote up
describe('use-certificates hook', () => {
  it('should not delete certificate', async () => {
    const { result } = renderHook(() => useCertificates());
    await result.current.deleteCertificateHandler();
    expect(result.current.items.length).toBe(
      certificatesMock.getAllCertificates.count
    );
  });

  it('should not update certificate status', async () => {
    const { result } = renderHook(() => useCertificates());
    await result.current.updateCertificateHandler('HOR58332589');
    expect(result.current.items[1].isActivated).toBeTruthy();
  });

  it('should open update modal window', () => {
    const { result } = renderHook(() => useCertificates());
    act(() => {
      result.current.openUpdateModal('HOR58332589');
    });
    expect(mockOpenSnackbar).toHaveBeenCalled();
  });

  it('should open delete modal window', () => {
    const { result } = renderHook(() => useCertificates());
    act(() => {
      result.current.openDeleteModal('624ee35a3daf9e63c88ceac3');
    });
    expect(mockOpenSnackbar).toHaveBeenCalled();
  });

  it('should transform date', () => {
    let date;
    const { result } = renderHook(() => useCertificates());
    act(() => {
      date = result.current.transformDate('2023-04-08T12:59:25.818Z');
    });
    expect(date).toEqual('04/08/2023');
  });

  it('should get active status', () => {
    let status;
    const { result } = renderHook(() => useCertificates());
    act(() => {
      status = result.current.checkStatus(true, false, false);
    });
    expect(status).toEqual('Активний');
  });

  it('should get used status', () => {
    let status;
    const { result } = renderHook(() => useCertificates());
    act(() => {
      status = result.current.checkStatus(false, true, false);
    });
    expect(status).toEqual('Використаний');
  });

  it('should get exired status', () => {
    let status;
    const { result } = renderHook(() => useCertificates());
    act(() => {
      status = result.current.checkStatus(false, false, true);
    });
    expect(status).toEqual('Протермінований');
  });

  it('should get pending status', () => {
    let status;
    const { result } = renderHook(() => useCertificates());
    act(() => {
      status = result.current.checkStatus(false, false, false);
    });
    expect(status).toEqual('В обробці');
  });

  it('should set name of the certificate creator', () => {
    let name;
    const { result } = renderHook(() => useCertificates());
    act(() => {
      name = result.current.setUser([
        { firstName: 'Andrii', lastName: 'Fedyshyn' }
      ]);
    });
    expect(name).toEqual('Andrii Fedyshyn');
  });
});
Example #22
Source File: use-cart.spec.js    From horondi_client_fe with MIT License 4 votes vote down vote up
describe('use-cart tests', () => {
  let wrap;
  let res;
  beforeEach(() => {
    wrap = renderHook(useCart);
  });
  it('should add item to cart', () => {
    act(() => {
      wrap.result.current.cartOperations.addToCart(mockItem);
    });

    expect(wrap.result.current.cart).toContain(mockItem);
  });
  it('should return cart item by id', () => {
    act(() => {
      res = wrap.result.current.cartOperations.getCartItem(mockItem.productId);
    });

    expect(res).toEqual(mockItem);
  });
  it('should return total price with promo code', () => {
    act(() => {
      res = wrap.result.current.cartOperations.getTotalPricesWithPromoCode(mockPromoCode);
    });

    expect(res).toBe(90);
  });
  it('should return product price with promo code', () => {
    act(() => {
      res = wrap.result.current.cartOperations.getProductPriceWithPromoCode(
        mockItem.id,
        mockPromoCode
      );
    });

    expect(res).toBe(90);
  });
  it('should return product price', () => {
    act(() => {
      res = wrap.result.current.cartOperations.getProductPrice(mockItem.id, 0);
    });

    expect(res).toBe(100);
  });
  it('should return total price', () => {
    act(() => {
      res = wrap.result.current.cartOperations.getTotalPrice();
    });

    expect(res).toBe(100);
  });
  it('should check is item in cart', () => {
    act(() => {
      res = wrap.result.current.isInCart(mockItem.productId);
    });

    expect(res).toEqual(mockItem);
  });
  it('should change quantity', () => {
    act(() => {
      wrap.result.current.cartOperations.changeQuantity(mockItem.id, 2);
    });
    const item = wrap.result.current.cart.find((el) => (el.id = mockItem.id));

    expect(item.quantity).toEqual(2);
  });
  it('should change size', () => {
    act(() => {
      wrap.result.current.cartOperations.changeSize(mockItem.id, sizeAndPrice);
    });
    const item = wrap.result.current.cart.find((el) => (el.id = mockItem.id));

    expect(item.sizeAndPrice).toEqual(sizeAndPrice);
  });
  it('should change constructor size', () => {
    act(() => {
      wrap.result.current.cartOperations.changeSizeConstructor(mockItem.id, sizeAndPrice.size);
    });
    const item = wrap.result.current.cart.find((el) => (el.id = mockItem.id));

    expect(item.sizeAndPrice.size).toEqual(sizeAndPrice.size);
  });
  it('should remove item from cart', () => {
    act(() => {
      wrap.result.current.cartOperations.removeFromCart(mockItem);
    });

    expect(wrap.result.current.cart).toHaveLength(0);
  });
});
Example #23
Source File: GridKeyboardNavigationContext.jest.js    From monday-ui-react-core with MIT License 4 votes vote down vote up
describe("GridKeyboardNavigationContext", () => {
  let wrapperRef;
  let ref1;
  let ref2;
  let ref3;
  let ref4;
  let ref5;

  beforeEach(() => {
    ref1 = createElementRef("ref1");
    ref2 = createElementRef("ref2");
    ref3 = createElementRef("ref3");
    ref4 = createElementRef("ref4");
    ref5 = createElementRef("ref5");
  });

  afterEach(() => {
    [wrapperRef, ref1, ref2, ref3, ref4, ref5].forEach(ref => ref?.current?.remove());
    cleanup();
  });

  describe("useGridKeyboardNavigationContext", () => {
    it("should focus the element positioned on the direction of onOutboundNavigation", () => {
      const positions = [{ leftElement: ref2, rightElement: ref4 }];
      const keyboardDirection = NAV_DIRECTIONS.RIGHT;
      const { result } = renderHookForTest(positions);

      result.current.onOutboundNavigation(ref2, keyboardDirection);

      expect(ref2.current.blur).toHaveBeenCalled();
      expect(ref4.current.focus).toHaveBeenCalled();
    });

    it("should do nothing if there is no element on the direction of onOutboundNavigation", () => {
      const positions = [{ leftElement: ref2, rightElement: ref4 }];
      const keyboardDirection = NAV_DIRECTIONS.UP;
      const { result } = renderHookForTest(positions);

      result.current.onOutboundNavigation(ref2, keyboardDirection);

      expect(ref2.current.blur).not.toHaveBeenCalled();
    });

    it("should do nothing if onOutboundNavigation is called when disabled", () => {
      const positions = [{ leftElement: ref2, rightElement: ref4 }];
      const keyboardDirection = NAV_DIRECTIONS.RIGHT;
      const { result } = renderHookForTest(positions, true);

      result.current.onOutboundNavigation(ref2, keyboardDirection);

      expect(ref2.current.blur).not.toHaveBeenCalled();
      expect(ref4.current.blur).not.toHaveBeenCalled();
    });

    it("should call the upper context's onOutboundNavigation if there is no element in that direction", () => {
      const positions = [{ leftElement: ref2, rightElement: ref4 }];
      const keyboardDirection = NAV_DIRECTIONS.UP;
      const fakeUpperContext = { onOutboundNavigation: jest.fn() };
      const { result } = renderHookWithContext(positions, fakeUpperContext);

      result.current.onOutboundNavigation(ref2, keyboardDirection);

      expect(fakeUpperContext.onOutboundNavigation).toHaveBeenCalledWith(wrapperRef, keyboardDirection);
    });

    it("should not focus any other element when the is no last direction of keyboard navigation, after the wrapper element is focused", () => {
      const positions = [
        { leftElement: ref2, rightElement: ref4 },
        { topElement: ref1, rightElement: ref3 }
      ];

      renderHookForTest(positions);
      focusWrapperElement();

      expect(ref1.current.focus).not.toHaveBeenCalled();
      expect(ref2.current.focus).not.toHaveBeenCalled();
      expect(ref3.current.focus).not.toHaveBeenCalled();
      expect(ref4.current.focus).not.toHaveBeenCalled();
    });

    it("should do nothing if the wrapper element is focused, and the hook is disabled", () => {
      const positions = [{ leftElement: ref2, rightElement: ref4 }];
      renderHookForTest(positions, true);

      act(() => {
        userEvent.keyboard("{ArrowLeft}"); // make sure there's a value for lastNavigationDirection
      });
      focusWrapperElement();

      expect(ref2.current.blur).not.toHaveBeenCalled();
      expect(ref4.current.blur).not.toHaveBeenCalled();
    });

    it("should focus the element in the last direction of keyboard navigation, when the wrapper element is focused", () => {
      const positions = [{ leftElement: ref2, rightElement: ref4 }];

      renderHookForTest(positions);

      act(() => {
        userEvent.keyboard("{ArrowLeft}"); // if the user navigated left, the right-most element should be focused
      });
      focusWrapperElement();

      expect(ref2.current.focus).not.toHaveBeenCalled();
      expect(ref4.current.focus).toHaveBeenCalled();
    });

    function renderHookForTest(positions, disabled = false) {
      wrapperRef = createElementRef("wrapper");
      return renderHook(() => useGridKeyboardNavigationContext(positions, wrapperRef, { disabled }));
    }

    function renderHookWithContext(positions, contextValue) {
      wrapperRef = createElementRef();
      const wrapper = ({ children }) => (
        <GridKeyboardNavigationContext.Provider value={contextValue}>{children}</GridKeyboardNavigationContext.Provider>
      );
      return renderHook(() => useGridKeyboardNavigationContext(positions, wrapperRef), { wrapper });
    }

    function focusWrapperElement() {
      act(() => {
        wrapperRef.current.dispatchEvent(new Event("focus")); //jsdom's .focus() isn't working as it should, so we fire our own event
      });
    }
  });

  function createElementRef(id) {
    const element = document.createElement("div");
    element.id = id;
    document.body.appendChild(element);
    jest.spyOn(element, "blur");
    jest.spyOn(element, "focus");
    return { current: element };
  }
});
Example #24
Source File: useDataHandler.spec.js    From rainbow-modules with MIT License 4 votes vote down vote up
describe('useDataHandler', () => {
    it('should return the correct initial data', () => {
        const {
            result: { current: data },
        } = renderHook(() => useTableDataSource([]));
        const dataHandler = renderHook(() => useDataHandler(data));
        expect(dataHandler.result.current).toEqual([
            [],
            undefined,
            { current: null },
            { current: null },
        ]);
    });

    it('should add the new data at the end', () => {
        const {
            result: { current: data },
        } = renderHook(() => useTableDataSource([{ name: 'Test' }]));
        const dataHandler = renderHook(() => useDataHandler(data));
        act(() => {
            data.push({ data: [{ name: 'Test 2' }] });
        });
        const [currentData] = dataHandler.result.current;
        expect(currentData).toEqual([{ name: 'Test' }, { name: 'Test 2' }]);
    });

    it('should add the new data at the begining', () => {
        const {
            result: { current: data },
        } = renderHook(() => useTableDataSource([{ name: 'Test' }]));
        const dataHandler = renderHook(() => useDataHandler(data));
        act(() => {
            data.unshift({ data: [{ name: 'Test 2' }] });
        });
        const [currentData] = dataHandler.result.current;
        expect(currentData).toEqual([{ name: 'Test 2' }, { name: 'Test' }]);
    });

    it('should replace the existing data with the new', () => {
        const {
            result: { current: data },
        } = renderHook(() => useTableDataSource([{ name: 'Test' }]));
        const dataHandler = renderHook(() => useDataHandler(data));
        act(() => {
            data.set({ data: [{ name: 'Test 2' }] });
        });
        const [currentData] = dataHandler.result.current;
        expect(currentData).toEqual([{ name: 'Test 2' }]);
    });

    it('should update the item with the passed id', () => {
        const {
            result: { current: data },
        } = renderHook(() => useTableDataSource([{ id: 'test', name: 'Test' }]));
        const dataHandler = renderHook(() => useDataHandler(data));
        act(() => {
            data.updateById({ id: 'test', data: { name: 'Updated test' } });
        });
        const [currentData] = dataHandler.result.current;
        expect(currentData).toEqual([{ name: 'Updated test' }]);
    });

    it('should update the item in the passed position', () => {
        const {
            result: { current: data },
        } = renderHook(() => useTableDataSource([{ name: 'Test' }]));
        const dataHandler = renderHook(() => useDataHandler(data));
        act(() => {
            data.updateByIndex({ index: 0, data: { name: 'Updated test' } });
        });
        const [currentData] = dataHandler.result.current;
        expect(currentData).toEqual([{ name: 'Updated test' }]);
    });

    it('should remove the item with the passed id', () => {
        const {
            result: { current: data },
        } = renderHook(() => useTableDataSource([{ id: 'test', name: 'Test' }]));
        const dataHandler = renderHook(() => useDataHandler(data));
        act(() => {
            data.deleteById({ id: 'test' });
        });
        const [currentData] = dataHandler.result.current;
        expect(currentData).toEqual([]);
    });

    it('should remove the item in the passed index', () => {
        const {
            result: { current: data },
        } = renderHook(() => useTableDataSource([{ name: 'Test' }]));
        const dataHandler = renderHook(() => useDataHandler(data));
        act(() => {
            data.deleteByIndex({ index: 0 });
        });
        const [currentData] = dataHandler.result.current;
        expect(currentData).toEqual([]);
    });
});
Example #25
Source File: index.test.js    From react-use-opentok with MIT License 4 votes vote down vote up
describe('session methods after initialization', () => {
  it('connectSession, disconnectSession', async () => {
    const { result } = renderHook(() => reactUseOpentok());
    let [opentokProps, opentokMethods] = result.current;
    await act(() => opentokMethods.initSession(MOCK_CREDENTIALS));

    [opentokProps, opentokMethods] = result.current;
    expect(opentokProps).toMatchObject({
      session: {
        connection: null,
      },
      isSessionConnected: false,
      connectionId: undefined,
    });

    await act(() =>
      opentokMethods.connectSession(
        MOCK_CREDENTIALS.token,
        opentokProps.session
      )
    );
    [opentokProps, opentokMethods] = result.current;
    expect(opentokProps).toMatchObject({
      isSessionConnected: true,
      connectionId: expect.any(String),
    });

    act(() => opentokMethods.disconnectSession());
    [opentokProps, opentokMethods] = result.current;

    expect(opentokProps).toMatchObject({
      isSessionConnected: false,
      connectionId: null,
    });
  });

  it('publish, unpublish', async () => {
    const { name, element, options } = MOCK_INIT_PUBLISHER;
    const { result } = renderHook(() => reactUseOpentok());
    let [opentokProps, opentokMethods] = result.current;
    await act(() => opentokMethods.initSession(MOCK_CREDENTIALS));
    [opentokProps, opentokMethods] = result.current;

    expect(opentokProps).toMatchObject({
      publisher: {},
      streams: [],
    });

    // publish
    await act(() =>
      opentokMethods.publish({
        name,
        element,
        options,
      })
    );

    [opentokProps, opentokMethods] = result.current;
    expect(opentokProps).toMatchObject({
      publisher: {
        [name]: expect.any(Object),
      },
      streams: expect.any(Array),
    });
    expect(opentokProps.streams.length).toBe(1);
    expect(() =>
      act(() =>
        opentokMethods.publish({
          name,
          element,
          options,
        })
      )
    ).toThrow();

    // unpublish
    expect(() =>
      act(() => opentokMethods.unpublish({ name: 'foo' }))
    ).toThrow();

    act(() =>
      opentokMethods.unpublish({
        name,
      })
    );

    [opentokProps, opentokMethods] = result.current;
    expect(opentokProps).toMatchObject({
      publisher: {
        [name]: null,
      },
    });
    expect(opentokProps.streams.length).toBe(0);
  });

  it('subscribe, unsubscribe', async () => {
    const { name, element, options } = MOCK_INIT_PUBLISHER;
    const { result } = renderHook(() => reactUseOpentok());
    let [opentokProps, opentokMethods] = result.current;
    await act(() => opentokMethods.initSession(MOCK_CREDENTIALS));
    [opentokProps, opentokMethods] = result.current;

    await act(() =>
      opentokMethods.publish({
        name,
        element,
        options,
      })
    );
    [opentokProps, opentokMethods] = result.current;

    expect(opentokProps).toMatchObject({
      subscribers: [],
    });
    expect(opentokProps.subscribers.length).toBe(0);
    const stream = opentokProps.publisher[name].stream;

    // subscribe
    act(() => {
      opentokMethods.subscribe({
        stream,
        element: 'subscriber',
      });
      return undefined;
    });

    [opentokProps, opentokMethods] = result.current;

    expect(opentokProps.subscribers.length).toBe(1);

    // unsubscribe
    act(() =>
      opentokMethods.unsubscribe({
        stream,
      })
    );
    [opentokProps, opentokMethods] = result.current;
    expect(opentokProps.subscribers.length).toBe(0);
  });

  it('sendSignal with to and type', async () => {
    const { result } = renderHook(() => reactUseOpentok());
    let [opentokProps, opentokMethods] = result.current;
    expect(() => act(() => opentokMethods.sendSignal(MOCK_SIGNAL))).toThrow();

    await act(() => opentokMethods.initSession(MOCK_CREDENTIALS));
    [opentokProps, opentokMethods] = result.current;

    await act(() =>
      opentokMethods.connectSession(
        MOCK_CREDENTIALS.token,
        opentokProps.session
      )
    );
    [opentokProps, opentokMethods] = result.current;

    // register signal event
    const handleSignal = jest.fn((e) => e);
    act(() => opentokProps.session.on('signal', handleSignal));
    expect(handleSignal).not.toHaveBeenCalled();

    // dispatch signal event
    [opentokProps, opentokMethods] = result.current;
    act(() => opentokMethods.sendSignal(MOCK_SIGNAL));

    expect(handleSignal).toHaveBeenCalledTimes(1);
    expect(handleSignal).toHaveBeenCalledWith({
      type: MOCK_SIGNAL.type,
      data: MOCK_SIGNAL.data,
      to: MOCK_SIGNAL.to,
    });
  });

  it('sendSignal without to and type', async () => {
    const { result } = renderHook(() => reactUseOpentok());
    let [opentokProps, opentokMethods] = result.current;
    const { to, type, ...mockSignalWithoutTypeAndTo } = MOCK_SIGNAL;

    expect(() =>
      act(() => opentokMethods.sendSignal(mockSignalWithoutTypeAndTo))
    ).toThrow();

    await act(() => opentokMethods.initSession(MOCK_CREDENTIALS));
    [opentokProps, opentokMethods] = result.current;

    await act(() =>
      opentokMethods.connectSession(
        MOCK_CREDENTIALS.token,
        opentokProps.session
      )
    );
    [opentokProps, opentokMethods] = result.current;

    // register signal event
    const handleSignal = jest.fn((e) => e);
    act(() => opentokProps.session.on('signal', handleSignal));
    expect(handleSignal).not.toHaveBeenCalled();

    // dispatch signal event
    [opentokProps, opentokMethods] = result.current;
    act(() => opentokMethods.sendSignal(mockSignalWithoutTypeAndTo));

    expect(handleSignal).toHaveBeenCalledTimes(1);
    expect(handleSignal).toHaveBeenCalledWith({
      data: MOCK_SIGNAL.data,
    });
  });
});
Example #26
Source File: ColorModeProvider.test.js    From tonic-ui with MIT License 4 votes vote down vote up
describe('<ColorModeProvider />', () => {
  test('toggle color mode using a toggle button', () => {
    const ToggleColorModeApp = () => {
      const [colorMode, setColorMode] = useColorMode();
      const toggleColorMode = () => {
        setColorMode(colorMode === 'light' ? 'dark' : 'light');
      };
      return (
        <button type="button" onClick={toggleColorMode}>
          {colorMode}
        </button>
      );
    };

    render(
      <ColorModeProvider
        defaultValue="light"
      >
        <ToggleColorModeApp />
      </ColorModeProvider>,
    );

    const toggleColorModeButton = screen.getByRole('button');
    expect(toggleColorModeButton).toHaveTextContent('light');
    userEvent.click(toggleColorModeButton);
    expect(toggleColorModeButton).toHaveTextContent('dark');
  });

  test('toggle color mode using the toggle function', () => {
    const WrapperComponent = ({ children }) => {
      return (
        <ColorModeProvider
          defaultValue="light"
        >
          {children}
        </ColorModeProvider>
      );
    };
    const { result } = renderHook(() => useColorMode(), { wrapper: WrapperComponent });

    expect(result.current[0]).toEqual('light');

    act(() => {
      const toggleColorMode = result.current[1];
      toggleColorMode();
    });

    expect(result.current[0]).toEqual('dark');
  });

  test('prefer useSystemColorMode over defaultValue', () => {
    const getColorSchemeSpy = jest
      .spyOn(colorModeUtils, 'getColorScheme')
      .mockReturnValueOnce('dark');
    const onChange = jest.fn();
    const WrapperComponent = ({ children }) => {
      return (
        <ColorModeProvider
          defaultValue="light"
          onChange={onChange}
          useSystemColorMode
        >
          {children}
        </ColorModeProvider>
      );
    };
    const { result } = renderHook(() => useColorMode(), { wrapper: WrapperComponent });

    expect(getColorSchemeSpy).toHaveBeenCalledWith('light');
    expect(onChange).toHaveBeenCalledTimes(1);
    expect(onChange).toHaveBeenCalledWith('dark');
    expect(result.current[0]).toEqual('dark');
  });

  test('controlled color mode cannot be changed', () => {
    const WrapperComponent = ({ children }) => {
      return (
        <ColorModeProvider
          value="light"
        >
          {children}
        </ColorModeProvider>
      );
    };
    const { result } = renderHook(() => useColorMode(), { wrapper: WrapperComponent });

    expect(result.current[0]).toEqual('light');

    act(() => {
      const setColorMode = result.current[1];
      setColorMode('dark');
    });

    expect(result.current[0]).toEqual('light');
  });

  test('change color mode using the onChange callback', () => {
    const WrapperComponent = ({ children }) => {
      const [colorMode, setColorMode] = useState('light');
      return (
        <ColorModeProvider
          value={colorMode}
          onChange={setColorMode}
        >
          {children}
        </ColorModeProvider>
      );
    };
    const { result } = renderHook(() => useColorMode(), { wrapper: WrapperComponent });

    expect(result.current[0]).toEqual('light');

    act(() => {
      const setColorMode = result.current[1];
      setColorMode('dark');
    });

    expect(result.current[0]).toEqual('dark');
  });
});
Example #27
Source File: BeAReviewerModal.js    From plataforma-sabia with MIT License 4 votes vote down vote up
describe('<BeAReviewerModal />', () => {
	beforeEach(() => {
		fetchMock.mockClear();
		fetchMock.mockReset();
	});

	it('should be able to add category and sub-category to selected list and remove it', async () => {
		jest.setTimeout(30000);

		mockTaxonomies();
		mockCategories();
		mockSubmit();

		jest.spyOn(useAuth, 'default').mockReturnValue({
			user: {
				id: 12345,
			},
		});

		render(<BeAReviewerModal closeModal={closeModalMock} />);

		const categoryInput = await screen.findByLabelText('Área de atuação');
		const subCategoryInput = await screen.findByLabelText('Subárea de atuação');

		// Submit button should be disabled if no item is selected
		let submitBtn = await screen.findByRole('button', { name: /enviar solicitação/i });
		expect(submitBtn).toBeDisabled();

		// Add the first combination to selected list
		act(() => {
			fireEvent.change(categoryInput, { target: { value: 'Fake Taxonomy' } });
			fireEvent.keyDown(categoryInput, { key: 'Enter', keyCode: 13 });
		});

		let addBtn = await screen.findByRole('button', { name: /adicionar/i });
		expect(addBtn).toBeDisabled();

		// Checkboxes
		const checkAvailability = await screen.getByTestId('availability');
		const checkConflicts = await screen.getByTestId('conflicts');
		const checkEthics = await screen.getByTestId('ethics');

		act(() => {
			fireEvent.change(subCategoryInput, { target: { value: 'Fake Subcategory' } });
			fireEvent.keyDown(subCategoryInput, { key: 'Enter', keyCode: 13 });
			fireEvent.click(checkAvailability);
			fireEvent.click(checkConflicts);
			fireEvent.click(checkEthics);
		});

		expect(addBtn).toBeEnabled();

		// Clicking add button twice should not duplicate lines
		fireEvent.click(addBtn);
		fireEvent.click(addBtn);
		expect(screen.getAllByText(/fake taxonomy/i)).toHaveLength(2);
		expect(screen.getAllByText(/fake subcategory/i)).toHaveLength(2);

		// Add the second combination to selected list
		act(() => {
			fireEvent.change(categoryInput, { target: { value: 'Second Taxonomy' } });
			fireEvent.keyDown(categoryInput, { key: 'Enter', keyCode: 13 });
		});

		addBtn = await screen.findByRole('button', { name: /adicionar/i });
		expect(addBtn).toBeDisabled();

		act(() => {
			fireEvent.change(subCategoryInput, { target: { value: 'Second Subcategory' } });
			fireEvent.keyDown(subCategoryInput, { key: 'Enter', keyCode: 13 });
		});

		expect(addBtn).toBeEnabled();
		fireEvent.click(addBtn);
		expect(screen.getAllByText(/second taxonomy/i)).toHaveLength(2);
		expect(screen.getAllByText(/second subcategory/i)).toHaveLength(2);

		// Clicking remove button should only remove the clicked combination
		const removeBtns = screen.getAllByRole('button', { name: /remover/i });
		fireEvent.click(removeBtns[0]);
		expect(screen.queryByText(/fake taxonomy/i)).not.toBeInTheDocument();
		expect(screen.queryByText(/fake subcategory/i)).not.toBeInTheDocument();

		expect(screen.queryAllByText(/second taxonomy/i)).toHaveLength(2);
		expect(screen.queryAllByText(/second subcategory/i)).toHaveLength(2);

		act(() => {
			fireEvent.click(submitBtn);
		});

		submitBtn = await screen.findByRole('button', { name: /enviar solicitação/i });
		expect(submitBtn).toBeEnabled();
		expect(closeModalMock).toHaveBeenCalledTimes(1);

		expect(screen.getByText(/sua solicitação foi enviada/i)).toBeInTheDocument();
	});
});
Example #28
Source File: index.test.js    From 7-react-admin-ts with MIT License 4 votes vote down vote up
describe('useRequest', () => {
  const originalError = console.error;
  beforeAll(() => {
    jest.useFakeTimers();
    console.error = (...args) => {
      if (/Warning.*not wrapped in act/.test(args[0])) {
        return;
      }
      originalError.call(console, ...args);
    };
  });
  afterAll(() => {
    console.error = originalError;
  });

  const request = (req) =>
    new Promise((resolve, reject) => {
      setTimeout(() => {
        if (req === 0) {
          reject(new Error('fail'));
        } else {
          resolve('success');
        }
      }, 1000);
    });

  it('should be defined', () => {
    expect(useRequest).toBeDefined();
  });

  const setUp = (service, options) => renderHook((o) => useRequest(service, o || options));
  let hook;
  it('useRequest should auto run', async () => {
    let successValue;
    const successCallback = (text) => {
      successValue = text;
    };
    const errorCallback = jest.fn();

    act(() => {
      hook = setUp(request, {
        onSuccess: successCallback,
        onError: errorCallback,
      });
    });
    expect(hook.result.current.loading).toEqual(true);
    jest.runAllTimers();
    await hook.waitForNextUpdate();
    expect(hook.result.current.loading).toEqual(false);
    expect(hook.result.current.data).toEqual('success');
    expect(successValue).toEqual('success');
    expect(errorCallback).not.toHaveBeenCalled();
    act(() => {
      hook.result.current.run(0).catch(() => {});
    });
    expect(hook.result.current.loading).toEqual(true);
    jest.runAllTimers();
    await hook.waitForNextUpdate();
    expect(hook.result.current.error).toEqual(new Error('fail'));
    expect(hook.result.current.loading).toEqual(false);
    expect(errorCallback).toHaveBeenCalled();

    act(() => {
      hook.result.current.run(1);
    });
    jest.runAllTimers();
    await hook.waitForNextUpdate();
    expect(hook.result.current.data).toEqual('success');
    expect(hook.result.current.loading).toEqual(false);
    expect(errorCallback).toHaveBeenCalled();

    hook.unmount();
  });

  it('useRequest should be manually triggered', async () => {
    act(() => {
      hook = setUp(request, {
        manual: true,
      });
    });
    expect(hook.result.current.loading).toEqual(false);
    act(() => {
      hook.result.current.run(1);
    });
    expect(hook.result.current.loading).toEqual(true);
    jest.runAllTimers();
    await hook.waitForNextUpdate();
    expect(hook.result.current.loading).toEqual(false);
    expect(hook.result.current.data).toEqual('success');
    hook.unmount();
  });

  it('useRequest polling should work', async () => {
    const callback = jest.fn();

    act(() => {
      hook = setUp(
        () => {
          callback();
          return request();
        },
        {
          pollingInterval: 100,
          pollingWhenHidden: true,
        },
      );
    });
    expect(hook.result.current.loading).toEqual(true);
    jest.runAllTimers();
    await hook.waitForNextUpdate();
    expect(hook.result.current.loading).toEqual(false);
    expect(hook.result.current.data).toEqual('success');
    expect(callback).toHaveBeenCalled();

    jest.runAllTimers();
    await hook.waitForNextUpdate();
    expect(callback).toHaveBeenCalledTimes(2);

    jest.runAllTimers();
    await hook.waitForNextUpdate();
    expect(callback).toHaveBeenCalledTimes(3);

    act(() => {
      hook.result.current.cancel();
    });
    expect(callback).toHaveBeenCalledTimes(3);

    act(() => {
      hook.result.current.run();
    });
    jest.runAllTimers();
    expect(callback).toHaveBeenCalledTimes(4);

    hook.unmount();
  });

  it('useRequest fetchKey should work', async () => {
    act(() => {
      hook = setUp(request, {
        manual: true,
        fetchKey: (id) => id,
      });
    });
    act(() => {
      hook.result.current.run(1);
      hook.result.current.run(2);
      hook.result.current.run(3);
    });
    expect(hook.result.current.fetches[1].loading).toEqual(true);
    expect(hook.result.current.fetches[2].loading).toEqual(true);
    expect(hook.result.current.fetches[3].loading).toEqual(true);
    jest.runAllTimers();
    await hook.waitForNextUpdate();
    expect(hook.result.current.fetches[1].loading).toEqual(false);
    expect(hook.result.current.fetches[2].loading).toEqual(false);
    expect(hook.result.current.fetches[3].loading).toEqual(false);
    expect(hook.result.current.fetches[1].data).toEqual('success');
    expect(hook.result.current.fetches[2].data).toEqual('success');
    expect(hook.result.current.fetches[3].data).toEqual('success');
    hook.unmount();
  });

  it('useRequest debounceInterval should work', async () => {
    const callback = jest.fn();

    act(() => {
      hook = setUp(
        () => {
          callback();
          return request();
        },
        {
          manual: true,
          debounceInterval: 100,
        },
      );
    });

    act(() => {
      hook.result.current.run(1);
      jest.advanceTimersByTime(50);
      hook.result.current.run(2);
      jest.advanceTimersByTime(50);
      hook.result.current.run(3);
      jest.advanceTimersByTime(50);
      hook.result.current.run(4);
    });

    jest.runAllTimers();
    await hook.waitForNextUpdate();
    expect(callback).toHaveBeenCalledTimes(1);

    act(() => {
      hook.result.current.run(1);
      jest.advanceTimersByTime(50);
      hook.result.current.run(2);
      jest.advanceTimersByTime(50);
      hook.result.current.run(3);
      jest.advanceTimersByTime(50);
      hook.result.current.run(4);
    });

    jest.runAllTimers();
    await hook.waitForNextUpdate();
    expect(callback).toHaveBeenCalledTimes(2);

    hook.unmount();
  });

  it('useRequest throttleInterval should work', async () => {
    const callback = jest.fn();

    act(() => {
      hook = setUp(
        () => {
          callback();
          return request();
        },
        {
          manual: true,
          throttleInterval: 100,
        },
      );
    });

    act(() => {
      hook.result.current.run(1);
      jest.advanceTimersByTime(50);
      hook.result.current.run(2);
      jest.advanceTimersByTime(50);
      hook.result.current.run(3);
      jest.advanceTimersByTime(50);
      hook.result.current.run(4);
    });

    jest.runAllTimers();
    await hook.waitForNextUpdate();
    expect(callback).toHaveBeenCalledTimes(2);

    hook.unmount();
  });

  it('useRequest mutate should work', async () => {
    act(() => {
      hook = setUp(request);
    });
    jest.runAllTimers();
    await hook.waitForNextUpdate();
    expect(hook.result.current.data).toEqual('success');
    act(() => {
      hook.result.current.mutate('hello');
    });
    expect(hook.result.current.data).toEqual('hello');
    hook.unmount();
  });

  it('useRequest loadingDelay should work', async () => {
    act(() => {
      hook = setUp(request, {
        loadingDelay: 2000,
      });
    });
    expect(hook.result.current.loading).toEqual(false);
    jest.runAllTimers();
    await hook.waitForNextUpdate();
    expect(hook.result.current.loading).toEqual(false);
    hook.unmount();
  });

  it('useRequest loadingDelay should delay', async () => {
    act(() => {
      hook = setUp(request, {
        loadingDelay: 500,
      });
    });
    expect(hook.result.current.loading).toEqual(false);
    jest.advanceTimersByTime(501);
    expect(hook.result.current.loading).toEqual(true);
    jest.runAllTimers();
    await hook.waitForNextUpdate();
    expect(hook.result.current.loading).toEqual(false);
    hook.unmount();
  });

  it('useRequest refreshDeps should work', async () => {
    act(() => {
      hook = setUp(request, {
        refreshDeps: [1],
      });
    });
    expect(hook.result.current.loading).toEqual(true);
    jest.runAllTimers();
    await hook.waitForNextUpdate();
    expect(hook.result.current.loading).toEqual(false);
    hook.rerender({
      refreshDeps: [2],
    });
    expect(hook.result.current.loading).toEqual(true);
    jest.runAllTimers();
    await hook.waitForNextUpdate();
    expect(hook.result.current.loading).toEqual(false);
    hook.unmount();
  });

  it('useRequest ready should work', async () => {
    act(() => {
      hook = setUp(request, {
        ready: false,
      });
    });
    expect(hook.result.current.loading).toEqual(false);
    hook.rerender({
      ready: true,
    });
    expect(hook.result.current.loading).toEqual(true);
    hook.unmount();
  });

  it('useRequest initialData should work', async () => {
    act(() => {
      hook = setUp(request, {
        initialData: 'hello',
      });
    });
    expect(hook.result.current.loading).toEqual(true);
    expect(hook.result.current.data).toEqual('hello');
    jest.runAllTimers();
    await hook.waitForNextUpdate();

    expect(hook.result.current.data).toEqual('success');
    hook.unmount();
  });

  it('useRequest formatResult should work', async () => {
    let formarParams = '';
    act(() => {
      hook = setUp(request, {
        formatResult: (p) => {
          formarParams = p;
          return 'hello';
        },
      });
    });
    expect(hook.result.current.loading).toEqual(true);
    jest.runAllTimers();
    await hook.waitForNextUpdate();
    expect(hook.result.current.loading).toEqual(false);
    expect(formarParams).toEqual('success');
    expect(hook.result.current.data).toEqual('hello');
    hook.unmount();
  });

  it('useRequest defaultParams should work', async () => {
    act(() => {
      hook = setUp(request, {
        defaultParams: [1, 2, 3],
      });
    });
    expect(hook.result.current.loading).toEqual(true);
    jest.runAllTimers();
    await hook.waitForNextUpdate();
    expect(hook.result.current.params).toEqual([1, 2, 3]);
    hook.unmount();
  });

  it('useRequest refreshOnWindowFocus&focusTimespan should work', async () => {
    act(() => {
      hook = setUp(request, {
        refreshOnWindowFocus: true,
        focusTimespan: 5000,
      });
    });
    expect(hook.result.current.loading).toEqual(true);
    jest.advanceTimersByTime(1001);
    await hook.waitForNextUpdate();
    expect(hook.result.current.loading).toEqual(false);
    fireEvent.focus(window);
    expect(hook.result.current.loading).toEqual(true);
    jest.advanceTimersByTime(2000);
    await hook.waitForNextUpdate();
    expect(hook.result.current.loading).toEqual(false);

    jest.advanceTimersByTime(3000);
    fireEvent.focus(window);
    expect(hook.result.current.loading).toEqual(true);
    hook.unmount();
  });

  it('useRequest throwOnError to be false should work', async () => {
    let success = '';
    let error = '';

    act(() => {
      hook = setUp(request, {
        manual: true,
      });
    });
    act(() => {
      hook.result.current
        .run(0)
        .then((res) => {
          success = res;
        })
        .catch((err) => {
          error = err;
        });
    });
    jest.runAllTimers();
    await hook.waitForNextUpdate();
    expect(success).toEqual('');
    expect(error).toEqual(
      'useRequest has caught the exception, if you need to handle the exception yourself, you can set options.throwOnError to true.',
    );
  });

  it('useRequest throwOnError to be true should work', async () => {
    let success = '';
    let error = '';

    act(() => {
      hook = setUp(request, {
        manual: true,
        throwOnError: true,
      });
    });
    act(() => {
      hook.result.current
        .run(0)
        .then((res) => {
          success = res;
        })
        .catch((err) => {
          error = err;
        });
    });
    jest.runAllTimers();
    await hook.waitForNextUpdate();
    expect(success).toEqual('');
    expect(error).toEqual(new Error('fail'));
  });

  it('useRequest cacheKey should work', async () => {
    act(() => {
      hook = setUp(request, {
        cacheKey: 'testCacheKey',
      });
    });
    jest.runAllTimers();
    await hook.waitForNextUpdate();
    expect(hook.result.current.loading).toEqual(false);
    expect(hook.result.current.data).toEqual('success');
    hook.unmount();

    let hook2;
    act(() => {
      hook2 = setUp(request, {
        cacheKey: 'testCacheKey',
      });
    });
    expect(hook2.result.current.loading).toEqual(true);
    expect(hook2.result.current.data).toEqual('success');
    jest.runAllTimers();
    await hook2.waitForNextUpdate();
    expect(hook2.result.current.loading).toEqual(false);
    hook2.unmount();
  });

  it('useRequest staleTime should work', async () => {
    MockDate.set(0);

    act(() => {
      hook = setUp(request, {
        cacheKey: 'testStaleTime',
        staleTime: 3000,
      });
    });
    expect(hook.result.current.loading).toEqual(true);
    jest.runAllTimers();
    await hook.waitForNextUpdate();
    expect(hook.result.current.loading).toEqual(false);
    expect(hook.result.current.data).toEqual('success');
    hook.unmount();
    MockDate.set(1000);

    let hook2;
    act(() => {
      hook2 = setUp(request, {
        cacheKey: 'testStaleTime',
        staleTime: 3000,
      });
    });
    expect(hook.result.current.loading).toEqual(false);
    expect(hook2.result.current.data).toEqual('success');
    hook2.unmount();
    MockDate.set(3001);
    let hook3;
    act(() => {
      hook3 = setUp(request, {
        cacheKey: 'testStaleTime',
        staleTime: 3000,
      });
    });
    expect(hook3.result.current.loading).toEqual(true);
    expect(hook3.result.current.data).toEqual('success');
    jest.runAllTimers();
    await hook3.waitForNextUpdate();
    expect(hook3.result.current.loading).toEqual(false);
    hook3.unmount();
  });

  it('useRequest cacheTime should work', async () => {
    MockDate.set(0);

    act(() => {
      hook = setUp(request, {
        cacheKey: 'testCacheTime',
        cacheTime: 5000,
      });
    });
    expect(hook.result.current.loading).toEqual(true);
    jest.runAllTimers();
    await hook.waitForNextUpdate();
    expect(hook.result.current.loading).toEqual(false);
    expect(hook.result.current.data).toEqual('success');
    hook.unmount();
    MockDate.set(1000);
    jest.advanceTimersByTime(1000);

    let hook2;
    act(() => {
      hook2 = setUp(request, {
        cacheKey: 'testCacheTime',
        cacheTime: 5000,
      });
    });
    expect(hook2.result.current.loading).toEqual(true);
    expect(hook2.result.current.data).toEqual('success');
    hook2.unmount();
    MockDate.set(6001);
    jest.advanceTimersByTime(5001);

    let hook3;
    act(() => {
      hook3 = setUp(request, {
        cacheKey: 'testCacheTime',
        cacheTime: 5000,
      });
    });
    expect(hook3.result.current.loading).toEqual(true);
    expect(hook3.result.current.data).toEqual(undefined);
    jest.runAllTimers();
    await hook3.waitForNextUpdate();
    expect(hook3.result.current.loading).toEqual(false);
    expect(hook3.result.current.data).toEqual('success');
    hook3.unmount();
  });
});