From 57ba567a690c43d739c6c88ab4cb473b039613d5 Mon Sep 17 00:00:00 2001 From: Anurag Hazra Date: Tue, 15 Sep 2020 11:12:05 +0530 Subject: [PATCH] test: added tests for toast (#38) * test: jest testing setup * test: fixed jest typescript definitions * fix: test script * chore(deps): move deps to dev * test: added tests for accordion * test: added husky precommit test * test: axe container test * chore(infra): added github action test * test: added tests for number input * fix: a11y aria-valuetext issue with NumberInput * test: added tests for Slider * test: added tests for progress * test: added tests for link * test: added tests for breadcrumb * test: added tests for pagination * test: added tests for toast * chore(deps): remove unused raf --- src/toast/ToastProvider.tsx | 2 +- src/toast/ToastState.ts | 4 +- src/toast/__tests__/Toast.test.tsx | 118 +++++++++++++++++ src/toast/__tests__/ToastState.test.ts | 174 +++++++++++++++++++++++++ src/toast/stories/Demo.tsx | 12 +- 5 files changed, 300 insertions(+), 10 deletions(-) create mode 100644 src/toast/__tests__/Toast.test.tsx create mode 100644 src/toast/__tests__/ToastState.test.ts diff --git a/src/toast/ToastProvider.tsx b/src/toast/ToastProvider.tsx index 811f8fb29..0216d01cd 100644 --- a/src/toast/ToastProvider.tsx +++ b/src/toast/ToastProvider.tsx @@ -5,7 +5,7 @@ import { createContext } from "@chakra-ui/utils"; import { ToastStateReturn } from "./ToastState"; import { ToastController } from "./ToastController"; -import useToastState, { IToast } from "./ToastState"; +import { useToastState, IToast } from "./ToastState"; const DEFAULT_TIMEOUT = 5000; const PLACEMENTS = { diff --git a/src/toast/ToastState.ts b/src/toast/ToastState.ts index 1bbc3db48..a5f8079b2 100644 --- a/src/toast/ToastState.ts +++ b/src/toast/ToastState.ts @@ -27,7 +27,7 @@ interface ToastStateProps { animationTimeout?: number; } -const useToastState = ({ animationTimeout = 0 }: ToastStateProps) => { +export const useToastState = ({ animationTimeout = 0 }: ToastStateProps) => { const [toasts, setToasts] = React.useState({}); // toggle can be used to just hide/show the toast instead of removing it. @@ -140,5 +140,3 @@ const useToastState = ({ animationTimeout = 0 }: ToastStateProps) => { }; export type ToastStateReturn = ReturnType; - -export default useToastState; diff --git a/src/toast/__tests__/Toast.test.tsx b/src/toast/__tests__/Toast.test.tsx new file mode 100644 index 000000000..ba9910f1e --- /dev/null +++ b/src/toast/__tests__/Toast.test.tsx @@ -0,0 +1,118 @@ +import * as React from "react"; +import { axe } from "jest-axe"; +import { render } from "reakit-test-utils"; + +import { useToast, ToastProvider } from ".."; + +const Demo = () => { + const { show } = useToast(); + + return ( +
+ + + + +
+ ); +}; + +const ToastComp: React.FC = () => { + return ( + { + return ( +
+ {content} +
+ ); + }, + success: ({ remove, content, id }) => { + return ( +
+ {content} +
+ ); + }, + warning: ({ remove, content, id }) => { + return ( +
+ {content} +
+ ); + }, + }} + > + +
+ ); +}; + +describe("Toast", () => { + it("should render correctly", () => { + const { baseElement } = render(); + + expect(baseElement).toMatchInlineSnapshot(` + +
+
+ + + + +
+
+ + `); + }); + + test("Toast renders with no a11y violations", async () => { + const { container } = render(); + const results = await axe(container); + + expect(results).toHaveNoViolations(); + }); +}); diff --git a/src/toast/__tests__/ToastState.test.ts b/src/toast/__tests__/ToastState.test.ts new file mode 100644 index 000000000..305df2518 --- /dev/null +++ b/src/toast/__tests__/ToastState.test.ts @@ -0,0 +1,174 @@ +import { wait } from "reakit-test-utils"; +import { renderHook, act } from "@testing-library/react-hooks"; + +import { useToastState } from ".."; + +beforeEach(() => { + jest.useFakeTimers(); + jest + .spyOn(window, "requestAnimationFrame") + .mockImplementation((cb: any) => cb()); +}); + +afterEach(() => { + (window.requestAnimationFrame as any).mockRestore(); +}); + +describe("ToastState", () => { + it("should render correctly", () => { + const state = renderHook(() => useToastState({})).result; + + expect(state.current).toMatchInlineSnapshot(` + Object { + "getToastToRender": [Function], + "hide": [Function], + "remove": [Function], + "setToasts": [Function], + "show": [Function], + "toasts": Object {}, + "toggle": [Function], + } + `); + }); + + it("should add a new toast", () => { + const { result } = renderHook(() => useToastState({})); + + expect(result.current.toasts).toStrictEqual({}); + + act(() => { + result.current.show({ type: "primary", content: "Hello world" }); + }); + expect(Object.values(result.current.toasts)).toMatchObject([ + { + autoDismiss: undefined, + content: "Hello world", + isVisible: true, + placement: undefined, + timeout: undefined, + type: "primary", + }, + ]); + }); + + it("should toggle toast", () => { + const { result } = renderHook(() => useToastState({})); + + expect(result.current.toasts).toStrictEqual({}); + + act(() => { + result.current.show({ type: "primary", content: "Hello world" }); + }); + + const id = Object.values(result.current.toasts)[0].id; + + act(() => { + result.current.toggle({ id, isVisible: false }); + }); + expect(result.current.toasts[id]).toMatchObject({ isVisible: false }); + }); + + it("should remove toast", () => { + const { result } = renderHook(() => useToastState({})); + + expect(result.current.toasts).toStrictEqual({}); + + act(() => { + result.current.show({ type: "primary", content: "Hello world" }); + }); + expect(Object.values(result.current.toasts)).toHaveLength(1); + const id = Object.values(result.current.toasts)[0].id; + + act(() => { + result.current.remove(id); + }); + expect(result.current.toasts).toStrictEqual({}); + }); + + it("should hide toast", () => { + const { result } = renderHook(() => useToastState({ animationTimeout: 5 })); + let id = ""; + + expect(result.current.toasts).toStrictEqual({}); + + act(() => { + result.current.show({ type: "primary", content: "Hello world" }); + }); + expect(Object.values(result.current.toasts)).toHaveLength(1); + id = Object.values(result.current.toasts)[0].id; + + act(() => { + result.current.hide(id); + }); + expect(Object.values(result.current.toasts)).toMatchObject([ + { + autoDismiss: undefined, + content: "Hello world", + isVisible: false, + placement: undefined, + timeout: undefined, + type: "primary", + }, + ]); + + // Wait for animation timeout and after that toast should be removed + wait( + () => { + expect(result.current.toasts).toStrictEqual({}); + }, + { timeout: 5 }, + ); + }); + + it("should test getToastToRender", () => { + const { result } = renderHook(() => useToastState({})); + + expect(result.current.toasts).toStrictEqual({}); + + act(() => { + result.current.show({ + type: "primary", + placement: "top-center", + content: "Hello world 1", + }); + result.current.show({ + type: "primary", + placement: "bottom-center", + content: "Hello world 2", + }); + result.current.show({ + type: "primary", + placement: "bottom-left", + content: "Hello world 3", + }); + result.current.show({ + type: "primary", + placement: "bottom-left", + content: "Hello world 4", + }); + result.current.show({ + type: "primary", + placement: "top-right", + content: "Hello world 5", + }); + }); + + const allPositions: any[] = []; + result.current.getToastToRender("bottom-center", (pos, toastList) => { + allPositions.push(toastList); + }); + expect(allPositions[0]).toHaveLength(1); + expect(allPositions[1]).toHaveLength(1); + expect(allPositions[2]).toHaveLength(2); + expect(allPositions[3]).toHaveLength(1); + + expect(allPositions[2][0]).toMatchObject({ + content: "Hello world 3", + placement: "bottom-left", + }); + expect(allPositions[2][1]).toMatchObject({ + content: "Hello world 4", + placement: "bottom-left", + }); + }); +}); diff --git a/src/toast/stories/Demo.tsx b/src/toast/stories/Demo.tsx index 5532670b8..726ceec36 100644 --- a/src/toast/stories/Demo.tsx +++ b/src/toast/stories/Demo.tsx @@ -61,7 +61,7 @@ const PlacementDemo = () => { onClick={() => { show({ type: randomType(), - content: "This is error", + content: "This is top-left", placement: "top-left", }); }} @@ -72,7 +72,7 @@ const PlacementDemo = () => { onClick={() => { show({ type: randomType(), - content: "This is error", + content: "This is top-right", placement: "top-right", }); }} @@ -83,7 +83,7 @@ const PlacementDemo = () => { onClick={() => { show({ type: randomType(), - content: "This is error", + content: "This is top-center", placement: "top-center", }); }} @@ -94,7 +94,7 @@ const PlacementDemo = () => { onClick={() => { show({ type: randomType(), - content: "This is error", + content: "This is bottom-left", placement: "bottom-left", }); }} @@ -105,7 +105,7 @@ const PlacementDemo = () => { onClick={() => { show({ type: randomType(), - content: "This is error", + content: "This is bottom-right", placement: "bottom-right", }); }} @@ -116,7 +116,7 @@ const PlacementDemo = () => { onClick={() => { show({ type: randomType(), - content: "This is error", + content: "This is bottom-center", placement: "bottom-center", }); }}