From 59b67686f064db053716f24510c0bb284af78d76 Mon Sep 17 00:00:00 2001 From: Anurag Date: Tue, 17 Nov 2020 14:46:33 +0530 Subject: [PATCH 1/8] test(select): added test for select component --- src/select/Select.ts | 6 +- src/select/__tests__/Select.test.tsx | 129 ++++++++++++++++++ .../__snapshots__/Select.test.tsx.snap | 57 ++++++++ 3 files changed, 189 insertions(+), 3 deletions(-) create mode 100644 src/select/__tests__/Select.test.tsx create mode 100644 src/select/__tests__/__snapshots__/Select.test.tsx.snap diff --git a/src/select/Select.ts b/src/select/Select.ts index 0369bb288..4bdd449e4 100644 --- a/src/select/Select.ts +++ b/src/select/Select.ts @@ -35,13 +35,13 @@ export const useSelect = createHook({ onKeyDownRef.current?.(event); if (event.defaultPrevented) return; - // setTimeout on show prevents scroll jump on ArrowUp & ArrowDown + // window.setTimeout on show prevents scroll jump on ArrowUp & ArrowDown const first = () => { - if (!options.visible) options.show && setTimeout(options.show); + if (!options.visible) options.show && window.setTimeout(options.show); if (!options.selectedValue) options.first?.(); }; const last = () => { - if (!options.visible) options.show && setTimeout(options.show); + if (!options.visible) options.show && window.setTimeout(options.show); if (!options.selectedValue) options.last?.(); }; diff --git a/src/select/__tests__/Select.test.tsx b/src/select/__tests__/Select.test.tsx new file mode 100644 index 000000000..f4a1516f0 --- /dev/null +++ b/src/select/__tests__/Select.test.tsx @@ -0,0 +1,129 @@ +import * as React from "react"; +import userEvent from "@testing-library/user-event"; +import { axe, render, press, screen } from "reakit-test-utils"; +import { + Select, + SelectOption, + SelectPopover, + useSelectState, + SelectInitialState, +} from ".."; + +jest.useFakeTimers(); + +export const SelectComponent: React.FC = props => { + const select = useSelectState({ baseId: "select", ...props }); + + return ( + <> + + + + + + + + + ); +}; + +describe("Select", () => { + it("should render correctly", () => { + const { baseElement } = render(); + + expect(baseElement).toMatchSnapshot(); + }); + + it("should be able to open popover and select an element", () => { + render(); + + const popover = screen.getByTestId("popover"); + const popoverButton = screen.getByRole("button", { + name: /fruit/i, + }); + + expect(popoverButton).toHaveTextContent(/select a fruit/i); + expect(popover).not.toBeVisible(); + + press.Tab(); + expect(popoverButton).toHaveFocus(); + userEvent.click(popoverButton); + + expect(popover).toBeVisible(); + + const appleCusturd = screen.getByRole("option", { + name: /applecusturd/i, + }); + userEvent.click(appleCusturd); + expect(popoverButton).toHaveTextContent(/applecusturd/i); + }); + + it("should be able to open popover and select an element with keyboard", () => { + render(); + + const popover = screen.getByTestId("popover"); + const popoverButton = screen.getByRole("button", { + name: /fruit/i, + }); + + expect(popoverButton).toHaveTextContent(/select a fruit/i); + expect(popover).not.toBeVisible(); + + press.Tab(); + expect(popoverButton).toHaveFocus(); + + press.ArrowDown(); + // had micro-task queue operations because + // Select ArrowDown uses setTimeout to open the popover in order to prevent scroll jump + // see `onKeyDown` function of `Select.ts` + jest.runAllTimers(); + + expect(popover).toBeVisible(); + expect(screen.getByText("Apple")).toHaveFocus(); + + // select orange + press.ArrowDown(); + press.ArrowDown(); + const orange = screen.getByRole("option", { + name: /orange/i, + }); + expect(orange).toHaveFocus(); + + press.Enter(orange); + + expect(popoverButton).toHaveTextContent(/orange/i); + expect(popover).not.toBeVisible(); + }); + + it("should behave properly with default selected", () => { + render(); + + const popover = screen.getByTestId("popover"); + const popoverButton = screen.getByRole("button", { + name: /fruit/i, + }); + + expect(popoverButton).toHaveTextContent(/orange/i); + expect(popover).not.toBeVisible(); + + press.Tab(); + press.ArrowDown(); + jest.runAllTimers(); + expect(popover).toBeVisible(); + + press.Tab(); + press.Enter(); + expect(popover).not.toBeVisible(); + expect(popoverButton).toHaveTextContent(/orange/i); + }); + + test("Select renders with no a11y violations", async () => { + jest.useRealTimers(); + const { container } = render(); + const results = await axe(container); + + expect(results).toHaveNoViolations(); + }); +}); diff --git a/src/select/__tests__/__snapshots__/Select.test.tsx.snap b/src/select/__tests__/__snapshots__/Select.test.tsx.snap new file mode 100644 index 000000000..198c9d305 --- /dev/null +++ b/src/select/__tests__/__snapshots__/Select.test.tsx.snap @@ -0,0 +1,57 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Select should render correctly 1`] = ` + +
+ + +
+ +`; From 4c6ddbe8508221fb473ffa7a7863567e7e983514 Mon Sep 17 00:00:00 2001 From: Anurag Date: Tue, 17 Nov 2020 16:30:17 +0530 Subject: [PATCH 2/8] test(select): added typeahead tests --- src/select/Select.ts | 1 + src/select/__tests__/Select.test.tsx | 96 +++++++++++++++++++++++++++- 2 files changed, 96 insertions(+), 1 deletion(-) diff --git a/src/select/Select.ts b/src/select/Select.ts index 4bdd449e4..d145429b7 100644 --- a/src/select/Select.ts +++ b/src/select/Select.ts @@ -87,6 +87,7 @@ export const Select = createComponent({ const handleCharacterPress = (options: SelectOptions) => ( character: string, ) => { + console.log(character); /** * Typeahead: Based on current character pressed, * find the next item to be selected diff --git a/src/select/__tests__/Select.test.tsx b/src/select/__tests__/Select.test.tsx index f4a1516f0..9a0592758 100644 --- a/src/select/__tests__/Select.test.tsx +++ b/src/select/__tests__/Select.test.tsx @@ -1,6 +1,6 @@ import * as React from "react"; import userEvent from "@testing-library/user-event"; -import { axe, render, press, screen } from "reakit-test-utils"; +import { axe, render, press, screen, fireEvent } from "reakit-test-utils"; import { Select, SelectOption, @@ -119,6 +119,100 @@ describe("Select", () => { expect(popoverButton).toHaveTextContent(/orange/i); }); + test("typeahead should work properly when popover is not open", () => { + render(); + + const popover = screen.getByTestId("popover"); + const popoverButton = screen.getByRole("button", { + name: /fruit/i, + }); + + expect(popover).not.toBeVisible(); + expect(popoverButton).toHaveTextContent(/select a fruit/i); + + press.Tab(); + fireEvent.keyDown(popoverButton, { key: "a" }); + expect(popoverButton).toHaveTextContent(/apple/i); + jest.runAllTimers(); + fireEvent.keyDown(popoverButton, { key: "a" }); + expect(popoverButton).toHaveTextContent(/applecusturd/i); + jest.runAllTimers(); + fireEvent.keyDown(popoverButton, { key: "o" }); + expect(popoverButton).toHaveTextContent(/orange/i); + jest.runAllTimers(); + fireEvent.keyDown(popoverButton, { key: "b" }); + expect(popoverButton).toHaveTextContent(/banana/i); + }); + + test("typeahead should work properly when popover is open", () => { + render(); + + const popover = screen.getByTestId("popover"); + const popoverButton = screen.getByRole("button", { + name: /fruit/i, + }); + + userEvent.click(popoverButton); + expect(popoverButton).toHaveTextContent(/select a fruit/i); + expect(popover).toBeVisible(); + + const orange = screen.getByRole("option", { + name: /orange/i, + }); + const apple = screen.getByRole("option", { + name: /apple$/i, + }); + const applecusturd = screen.getByRole("option", { + name: /applecusturd/i, + }); + const banana = screen.getByRole("option", { + name: /banana/i, + }); + + fireEvent.keyDown(popover, { key: "o" }); + expect(orange).toHaveFocus(); + jest.runAllTimers(); + + fireEvent.keyDown(popover, { key: "a" }); + expect(apple).toHaveFocus(); + jest.runAllTimers(); + + fireEvent.keyDown(popover, { key: "a" }); + expect(applecusturd).toHaveFocus(); + jest.runAllTimers(); + + fireEvent.keyDown(popover, { key: "b" }); + expect(banana).toHaveFocus(); + }); + + test("open popover with arrowdown & select orange with typeahead", () => { + render(); + + const popover = screen.getByTestId("popover"); + const popoverButton = screen.getByRole("button", { + name: /fruit/i, + }); + + press.Tab(); + press.ArrowDown(); + jest.runAllTimers(); + + expect(popoverButton).toHaveTextContent(/select a fruit/i); + expect(popover).toBeVisible(); + + const orange = screen.getByRole("option", { + name: /orange/i, + }); + + fireEvent.keyDown(popover, { key: "o" }); + expect(orange).toHaveFocus(); + jest.runAllTimers(); + + press.Enter(); + expect(popoverButton).toHaveTextContent(/orange/i); + expect(popover).not.toBeVisible(); + }); + test("Select renders with no a11y violations", async () => { jest.useRealTimers(); const { container } = render(); From f44351cff1a5a1ce8cf77b06dd0d2edc38b95a84 Mon Sep 17 00:00:00 2001 From: Anurag Date: Tue, 17 Nov 2020 16:33:20 +0530 Subject: [PATCH 3/8] chore: window.setTimeout -> setTimeout --- src/select/Select.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/select/Select.ts b/src/select/Select.ts index d145429b7..2a1f989d7 100644 --- a/src/select/Select.ts +++ b/src/select/Select.ts @@ -35,13 +35,13 @@ export const useSelect = createHook({ onKeyDownRef.current?.(event); if (event.defaultPrevented) return; - // window.setTimeout on show prevents scroll jump on ArrowUp & ArrowDown + // setTimeout on show prevents scroll jump on ArrowUp & ArrowDown const first = () => { - if (!options.visible) options.show && window.setTimeout(options.show); + if (!options.visible) options.show && setTimeout(options.show); if (!options.selectedValue) options.first?.(); }; const last = () => { - if (!options.visible) options.show && window.setTimeout(options.show); + if (!options.visible) options.show && setTimeout(options.show); if (!options.selectedValue) options.last?.(); }; From c5c3886e71879ac210075e75a7d2b8f9a5d17253 Mon Sep 17 00:00:00 2001 From: Anurag Date: Tue, 17 Nov 2020 16:41:39 +0530 Subject: [PATCH 4/8] chore: fix comment --- src/select/Select.ts | 1 - src/select/__tests__/Select.test.tsx | 6 +++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/select/Select.ts b/src/select/Select.ts index 2a1f989d7..0369bb288 100644 --- a/src/select/Select.ts +++ b/src/select/Select.ts @@ -87,7 +87,6 @@ export const Select = createComponent({ const handleCharacterPress = (options: SelectOptions) => ( character: string, ) => { - console.log(character); /** * Typeahead: Based on current character pressed, * find the next item to be selected diff --git a/src/select/__tests__/Select.test.tsx b/src/select/__tests__/Select.test.tsx index 9a0592758..70fcd1723 100644 --- a/src/select/__tests__/Select.test.tsx +++ b/src/select/__tests__/Select.test.tsx @@ -75,7 +75,7 @@ describe("Select", () => { expect(popoverButton).toHaveFocus(); press.ArrowDown(); - // had micro-task queue operations because + // had to run micro-task queue operations because // Select ArrowDown uses setTimeout to open the popover in order to prevent scroll jump // see `onKeyDown` function of `Select.ts` jest.runAllTimers(); @@ -131,15 +131,19 @@ describe("Select", () => { expect(popoverButton).toHaveTextContent(/select a fruit/i); press.Tab(); + fireEvent.keyDown(popoverButton, { key: "a" }); expect(popoverButton).toHaveTextContent(/apple/i); jest.runAllTimers(); + fireEvent.keyDown(popoverButton, { key: "a" }); expect(popoverButton).toHaveTextContent(/applecusturd/i); jest.runAllTimers(); + fireEvent.keyDown(popoverButton, { key: "o" }); expect(popoverButton).toHaveTextContent(/orange/i); jest.runAllTimers(); + fireEvent.keyDown(popoverButton, { key: "b" }); expect(popoverButton).toHaveTextContent(/banana/i); }); From 65d478e43198ddbc4621a115968d162723d1629e Mon Sep 17 00:00:00 2001 From: Anurag Date: Tue, 17 Nov 2020 17:22:54 +0530 Subject: [PATCH 5/8] test(select): added multi select tests --- src/select/__tests__/MultiSelect.test.tsx | 54 +++++++++++++++++++++++ 1 file changed, 54 insertions(+) create mode 100644 src/select/__tests__/MultiSelect.test.tsx diff --git a/src/select/__tests__/MultiSelect.test.tsx b/src/select/__tests__/MultiSelect.test.tsx new file mode 100644 index 000000000..9135a2525 --- /dev/null +++ b/src/select/__tests__/MultiSelect.test.tsx @@ -0,0 +1,54 @@ +import * as React from "react"; +import userEvent from "@testing-library/user-event"; +import { axe, render, press, screen } from "reakit-test-utils"; + +import MultiSelectComponent from "../stories/SelectMultiple.component"; + +jest.useFakeTimers(); + +describe("MultiSelect", () => { + it("should be able to open popover and select an element", () => { + render(); + + const popover = screen.getByLabelText(/Fruits$/i); + const popoverButton = screen.getByRole("button", { + name: /fruit/i, + }); + + expect(popoverButton).toHaveTextContent(/select a fruit/i); + expect(popover).not.toBeVisible(); + + userEvent.click(popoverButton); + + expect(popover).toBeVisible(); + + const apple = screen.getByRole("option", { + name: /^apple$/i, + }); + userEvent.click(apple); + expect(popoverButton).toHaveTextContent(/1 fruits selected/i); + + const banana = screen.getByRole("option", { + name: /banana/i, + }); + userEvent.click(banana); + expect(popoverButton).toHaveTextContent(/2 fruits selected/i); + + const figs = screen.getByRole("option", { + name: /figs/i, + }); + userEvent.click(figs); + expect(popoverButton).toHaveTextContent(/3 fruits selected/i); + + press.Escape(popover); + expect(popover).not.toBeVisible(); + }); + + test("Select renders with no a11y violations", async () => { + jest.useRealTimers(); + const { container } = render(); + const results = await axe(container); + + expect(results).toHaveNoViolations(); + }); +}); From e3782f556f223b61d049499bc77f981e13e0bbbb Mon Sep 17 00:00:00 2001 From: Anurag Date: Tue, 17 Nov 2020 19:28:55 +0530 Subject: [PATCH 6/8] chore: moduleNameWrapper for renderless-component in jest --- jest.config.ts | 2 ++ src/select/stories/SelectMultiple.component.tsx | 6 +++--- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/jest.config.ts b/jest.config.ts index 930ef629a..fcb30bb74 100644 --- a/jest.config.ts +++ b/jest.config.ts @@ -12,6 +12,8 @@ export default { setupFilesAfterEnv: ["/jest.setup.js"], moduleNameMapper: { "\\.(css|less|sass|scss)$": "/src/__mocks__/styleMock.js", + "^@shared(.*)$": "/shared$1", + "^renderless-components$": "/src", }, coveragePathIgnorePatterns: [ "node_modules", diff --git a/src/select/stories/SelectMultiple.component.tsx b/src/select/stories/SelectMultiple.component.tsx index debad8075..95489481f 100644 --- a/src/select/stories/SelectMultiple.component.tsx +++ b/src/select/stories/SelectMultiple.component.tsx @@ -1,11 +1,11 @@ import * as React from "react"; import { - SelectInitialState, - useSelectState, Select, - SelectPopover, SelectOption, + SelectPopover, + useSelectState, + SelectInitialState, } from "../index"; import { fruits } from "./fruits"; From d48bb14b7599adead0b25fff4bc20e11d3419a5e Mon Sep 17 00:00:00 2001 From: Anurag Date: Tue, 17 Nov 2020 19:32:34 +0530 Subject: [PATCH 7/8] chore: try fix --- src/select/stories/SelectMultiple.component.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/select/stories/SelectMultiple.component.tsx b/src/select/stories/SelectMultiple.component.tsx index 95489481f..479f24828 100644 --- a/src/select/stories/SelectMultiple.component.tsx +++ b/src/select/stories/SelectMultiple.component.tsx @@ -6,7 +6,7 @@ import { SelectPopover, useSelectState, SelectInitialState, -} from "../index"; +} from "renderless-components"; import { fruits } from "./fruits"; export const App: React.FC = props => { From a303362d69dc0aea2d43ed0ac757cc90c8e445d4 Mon Sep 17 00:00:00 2001 From: Anurag Date: Tue, 17 Nov 2020 19:41:37 +0530 Subject: [PATCH 8/8] chore: update import path --- src/select/stories/Select.component.tsx | 2 +- src/select/stories/SelectControlled.component.tsx | 2 +- src/select/stories/SelectCustom.component.tsx | 2 +- src/select/stories/SelectDynamic.component.tsx | 2 +- src/select/stories/SelectFetch.component.tsx | 2 +- src/select/stories/SelectVirtual.component.tsx | 2 +- src/select/stories/SelectWindows.component.tsx | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/select/stories/Select.component.tsx b/src/select/stories/Select.component.tsx index f9bbea4b8..6db75299f 100644 --- a/src/select/stories/Select.component.tsx +++ b/src/select/stories/Select.component.tsx @@ -6,7 +6,7 @@ import { Select, SelectPopover, SelectOption, -} from "../index"; +} from "renderless-components"; export const App: React.FC = props => { const select = useSelectState({ gutter: 8, ...props }); diff --git a/src/select/stories/SelectControlled.component.tsx b/src/select/stories/SelectControlled.component.tsx index ae1c30f19..0763da926 100644 --- a/src/select/stories/SelectControlled.component.tsx +++ b/src/select/stories/SelectControlled.component.tsx @@ -7,7 +7,7 @@ import { Select, SelectPopover, SelectOption, -} from "../index"; +} from "renderless-components"; import { fruits } from "./fruits"; export const App: React.FC = () => { diff --git a/src/select/stories/SelectCustom.component.tsx b/src/select/stories/SelectCustom.component.tsx index 66e726b4a..cd34c3a36 100644 --- a/src/select/stories/SelectCustom.component.tsx +++ b/src/select/stories/SelectCustom.component.tsx @@ -6,7 +6,7 @@ import { Select, SelectPopover, SelectOption, -} from "../index"; +} from "renderless-components"; import { fruits } from "./fruits"; export const App: React.FC = props => { diff --git a/src/select/stories/SelectDynamic.component.tsx b/src/select/stories/SelectDynamic.component.tsx index b31b9ec31..9fde8380c 100644 --- a/src/select/stories/SelectDynamic.component.tsx +++ b/src/select/stories/SelectDynamic.component.tsx @@ -6,7 +6,7 @@ import { Select, SelectPopover, SelectOption, -} from "../index"; +} from "renderless-components"; import { fruits } from "./fruits"; export const App: React.FC = props => { diff --git a/src/select/stories/SelectFetch.component.tsx b/src/select/stories/SelectFetch.component.tsx index 75234d1e5..1fe82e57b 100644 --- a/src/select/stories/SelectFetch.component.tsx +++ b/src/select/stories/SelectFetch.component.tsx @@ -6,7 +6,7 @@ import { Select, SelectPopover, SelectOption, -} from "../index"; +} from "renderless-components"; type User = { value: string; label: string }; diff --git a/src/select/stories/SelectVirtual.component.tsx b/src/select/stories/SelectVirtual.component.tsx index 0a07364e5..c0e52ac91 100644 --- a/src/select/stories/SelectVirtual.component.tsx +++ b/src/select/stories/SelectVirtual.component.tsx @@ -7,7 +7,7 @@ import { Select, SelectPopover, SelectOption, -} from "../index"; +} from "renderless-components"; import { fruits } from "./fruits"; export const App: React.FC = props => { diff --git a/src/select/stories/SelectWindows.component.tsx b/src/select/stories/SelectWindows.component.tsx index 2134a152f..b602e66a8 100644 --- a/src/select/stories/SelectWindows.component.tsx +++ b/src/select/stories/SelectWindows.component.tsx @@ -6,7 +6,7 @@ import { Select, SelectPopover, SelectOption, -} from "../index"; +} from "renderless-components"; import { fruits } from "./fruits"; export const App: React.FC = props => {