From 8e1ffb2f2ae9771c670af970f5ed9915d9d73349 Mon Sep 17 00:00:00 2001 From: Nandor Date: Tue, 10 Oct 2023 17:26:32 +0200 Subject: [PATCH] test(ui-checkbox): migrate old Checkbox tests Closes: INSTUI-3878 --- package-lock.json | 12 +- packages/ui-checkbox/package.json | 7 +- .../CheckboxFacade.test.tsx | 25 +- .../src/Checkbox/CheckboxLocator.ts | 30 -- .../ToggleFacade.test.tsx | 24 +- .../Checkbox/__new-tests__/Checkbox.test.tsx | 221 +++++++++++ .../src/Checkbox/__tests__/Checkbox.test.tsx | 344 ------------------ packages/ui-checkbox/src/Checkbox/locator.ts | 28 -- .../src/CheckboxGroup/CheckboxGroupLocator.ts | 30 -- .../__new-tests__/CheckboxGroup.test.tsx | 202 ++++++++++ .../__tests__/CheckboxGroup.test.tsx | 297 --------------- .../ui-checkbox/src/CheckboxGroup/locator.ts | 28 -- packages/ui-checkbox/tsconfig.build.json | 4 +- 13 files changed, 470 insertions(+), 782 deletions(-) rename packages/ui-checkbox/src/Checkbox/CheckboxFacade/{__tests__ => __new-tests__}/CheckboxFacade.test.tsx (69%) delete mode 100644 packages/ui-checkbox/src/Checkbox/CheckboxLocator.ts rename packages/ui-checkbox/src/Checkbox/ToggleFacade/{__tests__ => __new-tests__}/ToggleFacade.test.tsx (69%) create mode 100644 packages/ui-checkbox/src/Checkbox/__new-tests__/Checkbox.test.tsx delete mode 100644 packages/ui-checkbox/src/Checkbox/__tests__/Checkbox.test.tsx delete mode 100644 packages/ui-checkbox/src/Checkbox/locator.ts delete mode 100644 packages/ui-checkbox/src/CheckboxGroup/CheckboxGroupLocator.ts create mode 100644 packages/ui-checkbox/src/CheckboxGroup/__new-tests__/CheckboxGroup.test.tsx delete mode 100644 packages/ui-checkbox/src/CheckboxGroup/__tests__/CheckboxGroup.test.tsx delete mode 100644 packages/ui-checkbox/src/CheckboxGroup/locator.ts diff --git a/package-lock.json b/package-lock.json index f376da29ad..46d373ab41 100644 --- a/package-lock.json +++ b/package-lock.json @@ -53985,11 +53985,14 @@ "prop-types": "^15.8.1" }, "devDependencies": { + "@instructure/ui-axe-check": "8.46.1", "@instructure/ui-babel-preset": "8.46.1", "@instructure/ui-color-utils": "8.46.1", - "@instructure/ui-test-locator": "8.46.1", "@instructure/ui-test-utils": "8.46.1", - "@instructure/ui-themes": "8.46.1" + "@instructure/ui-themes": "8.46.1", + "@testing-library/jest-dom": "^5.17.0", + "@testing-library/react": "^14.0.0", + "@testing-library/user-event": "^14.4.3" }, "peerDependencies": { "react": ">=16.8 <=18" @@ -59686,6 +59689,7 @@ "@instructure/console": "8.46.1", "@instructure/emotion": "8.46.1", "@instructure/shared-types": "8.46.1", + "@instructure/ui-axe-check": "8.46.1", "@instructure/ui-babel-preset": "8.46.1", "@instructure/ui-color-utils": "8.46.1", "@instructure/ui-dom-utils": "8.46.1", @@ -59694,13 +59698,15 @@ "@instructure/ui-prop-types": "8.46.1", "@instructure/ui-react-utils": "8.46.1", "@instructure/ui-svg-images": "8.46.1", - "@instructure/ui-test-locator": "8.46.1", "@instructure/ui-test-utils": "8.46.1", "@instructure/ui-testable": "8.46.1", "@instructure/ui-themes": "8.46.1", "@instructure/ui-utils": "8.46.1", "@instructure/ui-view": "8.46.1", "@instructure/uid": "8.46.1", + "@testing-library/jest-dom": "^5.17.0", + "@testing-library/react": "^14.0.0", + "@testing-library/user-event": "^14.4.3", "keycode": "^2.2.1", "prop-types": "^15.8.1" } diff --git a/packages/ui-checkbox/package.json b/packages/ui-checkbox/package.json index a0754fcb11..9ff481fa8f 100644 --- a/packages/ui-checkbox/package.json +++ b/packages/ui-checkbox/package.json @@ -43,9 +43,12 @@ "devDependencies": { "@instructure/ui-babel-preset": "8.46.1", "@instructure/ui-color-utils": "8.46.1", - "@instructure/ui-test-locator": "8.46.1", "@instructure/ui-test-utils": "8.46.1", - "@instructure/ui-themes": "8.46.1" + "@instructure/ui-themes": "8.46.1", + "@instructure/ui-axe-check": "8.46.1", + "@testing-library/jest-dom": "^5.17.0", + "@testing-library/react": "^14.0.0", + "@testing-library/user-event": "^14.4.3" }, "peerDependencies": { "react": ">=16.8 <=18" diff --git a/packages/ui-checkbox/src/Checkbox/CheckboxFacade/__tests__/CheckboxFacade.test.tsx b/packages/ui-checkbox/src/Checkbox/CheckboxFacade/__new-tests__/CheckboxFacade.test.tsx similarity index 69% rename from packages/ui-checkbox/src/Checkbox/CheckboxFacade/__tests__/CheckboxFacade.test.tsx rename to packages/ui-checkbox/src/Checkbox/CheckboxFacade/__new-tests__/CheckboxFacade.test.tsx index f99678fae9..5515afc147 100644 --- a/packages/ui-checkbox/src/Checkbox/CheckboxFacade/__tests__/CheckboxFacade.test.tsx +++ b/packages/ui-checkbox/src/Checkbox/CheckboxFacade/__new-tests__/CheckboxFacade.test.tsx @@ -23,19 +23,26 @@ */ import React from 'react' -import { expect, mount, within } from '@instructure/ui-test-utils' +import { render, screen } from '@testing-library/react' +import '@testing-library/jest-dom/extend-expect' +import { runAxeCheck } from '@instructure/ui-axe-check' import { CheckboxFacade } from '../index' -describe('', async () => { - it('should render', async () => { - const subject = await mount(label text) - const checkboxFacade = within(subject.getDOMNode()) - expect(checkboxFacade).to.exist() +const TEST_TEXT = 'test-text' + +describe('', () => { + it('should render', () => { + render({TEST_TEXT}) + const facade = screen.getByText(TEST_TEXT) + + expect(facade).toBeInTheDocument() }) + it('should meet a11y standards', async () => { - const subject = await mount(label text) - const checkboxFacade = within(subject.getDOMNode()) - expect(await checkboxFacade.accessible()).to.be.true() + const { container } = render({TEST_TEXT}) + const axeCheck = await runAxeCheck(container) + + expect(axeCheck).toBe(true) }) }) diff --git a/packages/ui-checkbox/src/Checkbox/CheckboxLocator.ts b/packages/ui-checkbox/src/Checkbox/CheckboxLocator.ts deleted file mode 100644 index 6ee80a1cbf..0000000000 --- a/packages/ui-checkbox/src/Checkbox/CheckboxLocator.ts +++ /dev/null @@ -1,30 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2015 - present Instructure, Inc. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -import { locator } from '@instructure/ui-test-locator' - -import { Checkbox } from './index' - -// @ts-expect-error ts-migrate(2339) FIXME: Property 'selector' does not exist on type 'typeof... Remove this comment to see the full error message -export const CheckboxLocator = locator(Checkbox.selector) diff --git a/packages/ui-checkbox/src/Checkbox/ToggleFacade/__tests__/ToggleFacade.test.tsx b/packages/ui-checkbox/src/Checkbox/ToggleFacade/__new-tests__/ToggleFacade.test.tsx similarity index 69% rename from packages/ui-checkbox/src/Checkbox/ToggleFacade/__tests__/ToggleFacade.test.tsx rename to packages/ui-checkbox/src/Checkbox/ToggleFacade/__new-tests__/ToggleFacade.test.tsx index d12f80ad3d..3de5ca16eb 100644 --- a/packages/ui-checkbox/src/Checkbox/ToggleFacade/__tests__/ToggleFacade.test.tsx +++ b/packages/ui-checkbox/src/Checkbox/ToggleFacade/__new-tests__/ToggleFacade.test.tsx @@ -23,20 +23,26 @@ */ import React from 'react' -import { expect, mount, within } from '@instructure/ui-test-utils' +import { render, screen } from '@testing-library/react' +import '@testing-library/jest-dom/extend-expect' +import { runAxeCheck } from '@instructure/ui-axe-check' import { ToggleFacade } from '../index' -describe('', async () => { - it('should render', async () => { - const subject = await mount(label text) - const toggleFacade = within(subject.getDOMNode()) - expect(toggleFacade).to.exist() +const TEST_TEXT = 'test-text' + +describe('', () => { + it('should render', () => { + render({TEST_TEXT}) + const facade = screen.getByText(TEST_TEXT) + + expect(facade).toBeInTheDocument() }) it('should meet a11y standards', async () => { - const subject = await mount(label text) - const toggleFacade = within(subject.getDOMNode()) - expect(await toggleFacade.accessible()).to.be.true() + const { container } = render({TEST_TEXT}) + const axeCheck = await runAxeCheck(container) + + expect(axeCheck).toBe(true) }) }) diff --git a/packages/ui-checkbox/src/Checkbox/__new-tests__/Checkbox.test.tsx b/packages/ui-checkbox/src/Checkbox/__new-tests__/Checkbox.test.tsx new file mode 100644 index 0000000000..d83027f658 --- /dev/null +++ b/packages/ui-checkbox/src/Checkbox/__new-tests__/Checkbox.test.tsx @@ -0,0 +1,221 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2015 - present Instructure, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +import React from 'react' +import { fireEvent, render, screen, waitFor } from '@testing-library/react' +import userEvent from '@testing-library/user-event' +import '@testing-library/jest-dom/extend-expect' + +import { runAxeCheck } from '@instructure/ui-axe-check' +import { Checkbox } from '../index' +import { CheckboxProps } from '../props' + +const TEST_VALUE = 'test-value' +const TEST_NAME = 'test-name' +const TEST_LABEL = 'test-label' + +const initProps = { + label: TEST_LABEL, + defaultChecked: true, + value: TEST_VALUE, + name: TEST_NAME +} + +const renderCheckbox = (props?: Partial) => { + const allProps: CheckboxProps = { + ...initProps, + ...props + } + return render() +} + +describe('', () => { + it('renders an input with type "checkbox"', () => { + renderCheckbox() + const inputElem = screen.getByRole('checkbox') + + expect(inputElem).toBeInTheDocument() + expect(inputElem.tagName).toBe('INPUT') + expect(inputElem).toHaveAttribute('type', 'checkbox') + }) + + it('`simple` variant only displays a checkmark when checked', async () => { + const { container } = renderCheckbox({ + variant: 'simple', + defaultChecked: false + }) + const checkboxElement = container.querySelector('input[type="checkbox"]') + const svgElement = container.querySelector('svg') + + expect(svgElement).not.toBeInTheDocument() + + userEvent.click(checkboxElement!) + await waitFor(() => { + const svgElementAfterClick = container.querySelector('svg') + expect(svgElementAfterClick).toBeInTheDocument() + }) + }) + + it('`simple` variant supports indeterminate/mixed state', () => { + renderCheckbox({ variant: 'simple', indeterminate: true }) + + const inputElem = screen.getByRole('checkbox') + + expect(inputElem).toBeInTheDocument() + expect(inputElem).toHaveAttribute('aria-checked', 'mixed') + }) + + describe('events', () => { + it('when clicked, fires onClick and onChange events', async () => { + const onClick = jest.fn() + const onChange = jest.fn() + renderCheckbox({ onClick, onChange }) + const checkboxElement = screen.getByRole('checkbox') + + userEvent.click(checkboxElement) + + await waitFor(() => { + expect(onClick).toHaveBeenCalled() + expect(onChange).toHaveBeenCalled() + }) + }) + + it('when clicked, does not call onClick or onChange when disabled', async () => { + const onClick = jest.fn() + const onChange = jest.fn() + renderCheckbox({ onClick, onChange, disabled: true }) + const checkboxElement = screen.getByRole('checkbox') + + fireEvent.click(checkboxElement) + + await waitFor(() => { + expect(onClick).not.toHaveBeenCalled() + expect(onChange).not.toHaveBeenCalled() + expect(checkboxElement).toBeDisabled() + }) + }) + + it('when clicked, does not call onClick or onChange when readOnly', async () => { + const onClick = jest.fn() + const onChange = jest.fn() + renderCheckbox({ onClick, onChange, readOnly: true }) + const checkboxElement = screen.getByRole('checkbox') + + fireEvent.click(checkboxElement) + + await waitFor(() => { + expect(onClick).not.toHaveBeenCalled() + expect(onChange).not.toHaveBeenCalled() + }) + }) + + it('calls onChange when enter key is pressed', async () => { + const onChange = jest.fn() + renderCheckbox({ onChange }) + const checkboxElement = screen.getByRole('checkbox') + + userEvent.type(checkboxElement, '{enter}') + + await waitFor(() => { + expect(onChange).toHaveBeenCalled() + }) + }) + + it('responds to onBlur event', async () => { + const onBlur = jest.fn() + renderCheckbox({ onBlur }) + + userEvent.tab() + userEvent.tab() + + await waitFor(() => { + expect(onBlur).toHaveBeenCalled() + }) + }) + + it('responds to onFocus event', async () => { + const onFocus = jest.fn() + renderCheckbox({ onFocus }) + + userEvent.tab() + + await waitFor(() => { + expect(onFocus).toHaveBeenCalled() + }) + }) + + it('focuses with the focus helper', () => { + const checkboxRef = React.createRef() + render() + const checkboxElement = screen.getByRole('checkbox') + + expect(checkboxElement).not.toHaveFocus() + + checkboxRef.current?.focus() + + expect(checkboxElement).toHaveFocus() + }) + + it('calls onMouseOver', async () => { + const onMouseOver = jest.fn() + renderCheckbox({ onMouseOver }) + const checkboxElement = screen.getByRole('checkbox') + + userEvent.hover(checkboxElement) + + await waitFor(() => { + expect(onMouseOver).toHaveBeenCalled() + }) + }) + + it('calls onMouseOut', async () => { + const onMouseOut = jest.fn() + renderCheckbox({ onMouseOut }) + const checkboxElement = screen.getByRole('checkbox') + + userEvent.hover(checkboxElement) + userEvent.unhover(checkboxElement) + + await waitFor(() => { + expect(onMouseOut).toHaveBeenCalled() + }) + }) + }) + + describe('for a11y', () => { + it('`simple` variant should meet standards', async () => { + const { container } = renderCheckbox({ variant: 'simple' }) + const axeCheck = await runAxeCheck(container) + + expect(axeCheck).toBe(true) + }) + + it('`toggle` variant should meet standards', async () => { + const { container } = renderCheckbox({ variant: 'toggle' }) + const axeCheck = await runAxeCheck(container) + + expect(axeCheck).toBe(true) + }) + }) +}) diff --git a/packages/ui-checkbox/src/Checkbox/__tests__/Checkbox.test.tsx b/packages/ui-checkbox/src/Checkbox/__tests__/Checkbox.test.tsx deleted file mode 100644 index e482fa2ba6..0000000000 --- a/packages/ui-checkbox/src/Checkbox/__tests__/Checkbox.test.tsx +++ /dev/null @@ -1,344 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2015 - present Instructure, Inc. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -import React from 'react' -import { expect, mount, stub, wait } from '@instructure/ui-test-utils' - -import { Checkbox } from '../index' -import { CheckboxLocator } from '../CheckboxLocator' - -describe('', async () => { - it('renders an input with type "checkbox"', async () => { - await mount( - - ) - - const checkbox = await CheckboxLocator.find() - const input = await checkbox.find('input') - - expect((input.getDOMNode() as HTMLInputElement).type).to.equal('checkbox') - }) - - it('`simple` variant only displays a checkmark when checked', async () => { - await mount( - - ) - - const checkbox = await CheckboxLocator.find() - - expect(await checkbox.find('svg', { expectEmpty: true })).to.not.exist() - }) - - it('`simple` variant supports indeterminate/mixed state', async () => { - await mount( - - ) - - const checkbox = await CheckboxLocator.find() - const input = await checkbox.find('input') - - expect(input).to.have.attribute('aria-checked', 'mixed') - }) - - describe('events', async () => { - it('when clicked, fires onClick and onChange events', async () => { - const onClick = stub() - const onChange = stub() - - await mount( - - ) - - const checkbox = await CheckboxLocator.find() - const input = await checkbox.find('input') - - await input.click() - - await wait(() => { - expect(onClick).to.have.been.called() - expect(onChange).to.have.been.called() - }) - }) - - it('when clicked, does not call onClick or onChange when disabled', async () => { - const onClick = stub() - const onChange = stub() - - await mount( - - ) - - const checkbox = await CheckboxLocator.find() - const input = await checkbox.find('input') - await input.click(undefined, { clickable: false }) - - expect(onClick).to.not.have.been.called() - expect(onChange).to.not.have.been.called() - }) - - it('when clicked, does not call onClick or onChange when readOnly', async () => { - const onClick = stub() - const onChange = stub() - - await mount( - - ) - - const checkbox = await CheckboxLocator.find() - const input = await checkbox.find('input') - - await input.click() - - await wait(() => { - expect(onClick).to.not.have.been.called() - expect(onChange).to.not.have.been.called() - }) - }) - - it('calls onChange when enter key is pressed', async () => { - const onChange = stub() - - await mount( - - ) - - const checkbox = await CheckboxLocator.find() - const input = await checkbox.find('input') - - await input.keyDown('enter') - - await wait(() => { - expect(onChange).to.have.been.called() - }) - }) - - it('responds to onBlur event', async () => { - const onBlur = stub() - - await mount( - - ) - - const checkbox = await CheckboxLocator.find() - const input = await checkbox.find('input') - - await input.focusOut() - - await wait(() => { - expect(onBlur).to.have.been.called() - }) - }) - - it('responds to onFocus event', async () => { - const onFocus = stub() - - await mount( - - ) - - const checkbox = await CheckboxLocator.find() - const input = await checkbox.find('input') - - await input.focus() - - await wait(() => { - expect(onFocus).to.have.been.called() - }) - }) - - it('focuses with the focus helper', async () => { - let checkboxRef: Checkbox | undefined - - await mount( - { - checkboxRef = el - }} - /> - ) - - expect(checkboxRef?.focused).to.be.false() - - checkboxRef?.focus() - - expect(checkboxRef?.focused).to.be.true() - - const checkbox = await CheckboxLocator.find() - const input = await checkbox.find('input') - - await wait(() => { - expect(input).to.have.focus() - }) - }) - - it('calls onMouseOver', async () => { - const onMouseOver = stub() - - /* eslint-disable jsx-a11y/mouse-events-have-key-events */ - - await mount( - - ) - /* eslint-enable jsx-a11y/mouse-events-have-key-events */ - - const checkbox = await CheckboxLocator.find() - - await checkbox.mouseOver() - - await wait(() => { - expect(onMouseOver).to.have.been.called() - }) - }) - - it('calls onMouseOut', async () => { - const onMouseOut = stub() - - /* eslint-disable jsx-a11y/mouse-events-have-key-events */ - - await mount( - - ) - /* eslint-enable jsx-a11y/mouse-events-have-key-events */ - - const checkbox = await CheckboxLocator.find() - - await checkbox.mouseOut() - - await wait(() => { - expect(onMouseOut).to.have.been.called() - }) - }) - }) - - describe('for a11y', () => { - it('`simple` variant should meet standards', async () => { - await mount( - - ) - - const checkbox = await CheckboxLocator.find() - - expect(await checkbox.accessible()).to.be.true() - }) - - it('`toggle` variant should meet standards', async () => { - await mount( - - ) - - const checkbox = await CheckboxLocator.find() - - expect(await checkbox.accessible()).to.be.true() - }) - }) -}) diff --git a/packages/ui-checkbox/src/Checkbox/locator.ts b/packages/ui-checkbox/src/Checkbox/locator.ts deleted file mode 100644 index ed5c2888db..0000000000 --- a/packages/ui-checkbox/src/Checkbox/locator.ts +++ /dev/null @@ -1,28 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2015 - present Instructure, Inc. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -import { CheckboxLocator } from './CheckboxLocator' - -export { CheckboxLocator } -export default CheckboxLocator diff --git a/packages/ui-checkbox/src/CheckboxGroup/CheckboxGroupLocator.ts b/packages/ui-checkbox/src/CheckboxGroup/CheckboxGroupLocator.ts deleted file mode 100644 index 76de9685f8..0000000000 --- a/packages/ui-checkbox/src/CheckboxGroup/CheckboxGroupLocator.ts +++ /dev/null @@ -1,30 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2015 - present Instructure, Inc. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -import { locator } from '@instructure/ui-test-locator' - -import { CheckboxGroup } from './index' - -// @ts-expect-error ts-migrate(2339) FIXME: Property 'selector' does not exist on type 'typeof... Remove this comment to see the full error message -export const CheckboxGroupLocator = locator(CheckboxGroup.selector) diff --git a/packages/ui-checkbox/src/CheckboxGroup/__new-tests__/CheckboxGroup.test.tsx b/packages/ui-checkbox/src/CheckboxGroup/__new-tests__/CheckboxGroup.test.tsx new file mode 100644 index 0000000000..1f8b869abe --- /dev/null +++ b/packages/ui-checkbox/src/CheckboxGroup/__new-tests__/CheckboxGroup.test.tsx @@ -0,0 +1,202 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2015 - present Instructure, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +import React from 'react' +import { fireEvent, render, screen, waitFor } from '@testing-library/react' +import userEvent from '@testing-library/user-event' +import '@testing-library/jest-dom/extend-expect' + +import { runAxeCheck } from '@instructure/ui-axe-check' +import { CheckboxGroup } from '../index' +import { CheckboxGroupProps } from '../props' +import { Checkbox } from '../../Checkbox' + +const TEST_NAME = 'test-name' +const TEST_DESCRIPTION = 'test-description' +const TEST_ERROR_MESSAGE = 'test-error-message' +const TEST_VALUE_1 = 'test-value-1' +const TEST_VALUE_2 = 'test-value-2' +const TEST_LABEL_1 = 'test-label-1' +const TEST_LABEL_2 = 'test-label-2' + +const renderCheckboxGroup = (props?: Partial) => { + const allProps: CheckboxGroupProps = { + name: TEST_NAME, + description: TEST_DESCRIPTION, + ...props + } + + return render( + + + + + ) +} + +describe('', () => { + it('adds the name props to all Checkbox types', () => { + renderCheckboxGroup({ name: TEST_NAME }) + const checkboxes = screen.getAllByRole('checkbox') + + expect(checkboxes.length).toBe(2) + expect(checkboxes[0]).toHaveAttribute('name', TEST_NAME) + expect(checkboxes[1]).toHaveAttribute('name', TEST_NAME) + }) + + it('links the messages to the fieldset via aria-describedby', () => { + const { container } = renderCheckboxGroup({ + messages: [{ text: TEST_ERROR_MESSAGE, type: 'error' }] + }) + const fieldset = screen.getByRole('group') + const ariaDesc = fieldset.getAttribute('aria-describedby') + const messageById = container.querySelector(`[id="${ariaDesc}"]`) + + expect(messageById).toBeInTheDocument() + expect(messageById).toHaveTextContent(TEST_ERROR_MESSAGE) + }) + + it('displays description message inside the legend', () => { + const { container } = renderCheckboxGroup({ description: TEST_DESCRIPTION }) + const legend = container.querySelector('legend') + + expect(legend).toBeInTheDocument() + expect(legend).toHaveTextContent(TEST_DESCRIPTION) + }) + + it('does not call the onChange prop when disabled', async () => { + const onChange = jest.fn() + renderCheckboxGroup({ onChange, disabled: true }) + const checkboxElement = screen.getAllByRole('checkbox')[0] + + fireEvent.click(checkboxElement) + + await waitFor(() => { + expect(onChange).not.toHaveBeenCalled() + expect(checkboxElement).toBeDisabled() + }) + }) + + it('does not call the onChange prop when readOnly', async () => { + const onChange = jest.fn() + renderCheckboxGroup({ onChange, readOnly: true }) + const checkboxElement = screen.getAllByRole('checkbox')[0] + + fireEvent.click(checkboxElement) + + await waitFor(() => { + expect(onChange).not.toHaveBeenCalled() + expect(checkboxElement).toBeDisabled() + }) + }) + + it('should not update the value when the value prop is set', async () => { + const onChange = jest.fn() + renderCheckboxGroup({ onChange, value: ['tester'] }) + const checkboxes = screen.getAllByRole('checkbox') + + expect(checkboxes[0]).not.toBeChecked() + expect(checkboxes[1]).not.toBeChecked() + + userEvent.click(checkboxes[0]) + + await waitFor(() => { + expect(onChange).not.toHaveBeenCalled() + expect(checkboxes[0]).not.toBeChecked() + expect(checkboxes[1]).not.toBeChecked() + }) + }) + + it('should add the checkbox value to the value list when it is checked', async () => { + const onChange = jest.fn() + renderCheckboxGroup({ onChange }) + const checkboxes = screen.getAllByRole('checkbox') + + expect(checkboxes[0]).not.toBeChecked() + expect(checkboxes[1]).not.toBeChecked() + + userEvent.click(checkboxes[0]) + userEvent.click(checkboxes[1]) + + await waitFor(() => { + expect(checkboxes[0]).toBeChecked() + expect(checkboxes[1]).toBeChecked() + expect(onChange).toHaveBeenCalledWith([TEST_VALUE_1, TEST_VALUE_2]) + }) + }) + + it('should check the checkboxes based on the defaultValue prop', () => { + const defaultValue = [TEST_VALUE_2] + renderCheckboxGroup({ defaultValue }) + const checkboxes = screen.getAllByRole('checkbox') + + expect(checkboxes[0]).not.toBeChecked() + expect(checkboxes[1]).toBeChecked() + }) + + it('should remove the checkbox value from the value list when it is unchecked', async () => { + const onChange = jest.fn() + const defaultValue = [TEST_VALUE_1, TEST_VALUE_2] + renderCheckboxGroup({ onChange, defaultValue }) + const checkboxes = screen.getAllByRole('checkbox') + + expect(checkboxes[0]).toBeChecked() + expect(checkboxes[1]).toBeChecked() + + userEvent.click(checkboxes[0]) + + await waitFor(() => { + expect(checkboxes[0]).not.toBeChecked() + expect(checkboxes[1]).toBeChecked() + expect(onChange).toHaveBeenCalledWith([TEST_VALUE_2]) + }) + }) + + it('passes the array of selected values to onChange handler', async () => { + const onChange = jest.fn() + const defaultValue = [TEST_VALUE_2] + renderCheckboxGroup({ onChange, defaultValue }) + const checkboxes = screen.getAllByRole('checkbox') + + expect(checkboxes[0]).not.toBeChecked() + expect(checkboxes[1]).toBeChecked() + + userEvent.click(checkboxes[0]) + + await waitFor(() => { + expect(checkboxes[0]).toBeChecked() + expect(checkboxes[1]).toBeChecked() + expect(onChange).toHaveBeenCalledWith([TEST_VALUE_2, TEST_VALUE_1]) + }) + }) + + describe('for a11y', () => { + it('should meet standards', async () => { + const { container } = renderCheckboxGroup() + const axeCheck = await runAxeCheck(container) + + expect(axeCheck).toBe(true) + }) + }) +}) diff --git a/packages/ui-checkbox/src/CheckboxGroup/__tests__/CheckboxGroup.test.tsx b/packages/ui-checkbox/src/CheckboxGroup/__tests__/CheckboxGroup.test.tsx deleted file mode 100644 index c2effad5bf..0000000000 --- a/packages/ui-checkbox/src/CheckboxGroup/__tests__/CheckboxGroup.test.tsx +++ /dev/null @@ -1,297 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2015 - present Instructure, Inc. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -import React from 'react' -import { expect, mount, stub } from '@instructure/ui-test-utils' - -import { CheckboxGroupLocator } from '../CheckboxGroupLocator' - -import { CheckboxGroup } from '../index' -import { Checkbox } from '../../Checkbox' - -describe('', async () => { - it('adds the name props to all Checkbox types', async () => { - await mount( - - - - - - - ) - - const checkboxGroup = await CheckboxGroupLocator.find() - const checkboxes = await checkboxGroup.findAll('input[name="sports"]') - expect(checkboxes.length).to.equal(4) - }) - - it('links the messages to the fieldset via aria-describedby', async () => { - await mount( - - - - - - - ) - - const checkboxGroup = await CheckboxGroupLocator.find() - const fieldset = await checkboxGroup.find('fieldset') - const messagesId = fieldset.getAttribute('aria-describedby') - const messages = await checkboxGroup.find(`#${messagesId}`) - - expect(messages.getTextContent()).to.equal('Invalid name') - }) - - it('displays description message inside the legend', async () => { - const description = 'You should pick something' - - await mount( - - - - - - - ) - - const checkboxGroup = await CheckboxGroupLocator.find() - const legend = await checkboxGroup.find('legend') - expect(legend.getTextContent()).to.equal(description) - }) - - it('does not call the onChange prop when disabled', async () => { - const onChange = stub() - - await mount( - - - - - - - ) - - const checkboxGroup = await CheckboxGroupLocator.find() - const input = await checkboxGroup.find('input[value="football"]') - await input.click(undefined, { clickable: false }) - - expect(onChange).to.not.have.been.called() - }) - - it('does not call the onChange prop when readOnly', async () => { - const onChange = stub() - - await mount( - - - - - - - ) - - const checkboxGroup = await CheckboxGroupLocator.find() - const input = await checkboxGroup.find('input[value="football"]') - await input.click() - - expect(onChange).to.not.have.been.called() - }) - - it('should not update the value when the value prop is set', async () => { - await mount( - - - - - - - ) - - const checkboxGroup = await CheckboxGroupLocator.find() - const inputs = await checkboxGroup.findAll('input') - inputs.forEach((input) => { - expect((input.getDOMNode() as HTMLInputElement).checked).to.be.false() - }) - - const input = await checkboxGroup.find('input[value="football"]') - await input.click() - - inputs.forEach((input) => { - expect((input.getDOMNode() as HTMLInputElement).checked).to.be.false() - }) - }) - - it('should add the checkbox value to the value list when it is checked', async () => { - const onChange = stub() - await mount( - - - - - - - ) - - const checkboxGroup = await CheckboxGroupLocator.find() - const input = await checkboxGroup.find('input[value="football"]') - await input.click() - - expect(onChange.lastCall.args[0]).to.deep.equal(['football']) - }) - - it('should check the checkboxes based on the defaultValue prop', async () => { - const defaultValue = ['football', 'volleyball'] - await mount( - - - - - - - ) - - const checkboxGroup = await CheckboxGroupLocator.find() - const inputs = await checkboxGroup.findAll('input') - - inputs.forEach((input) => { - if (defaultValue.includes(input.getAttribute('value')!)) { - expect((input.getDOMNode() as HTMLInputElement).checked).to.be.true() - } else { - expect((input.getDOMNode() as HTMLInputElement).checked).to.be.false() - } - }) - }) - - it('should remove the checkbox value from the value list when it is unchecked', async () => { - const onChange = stub() - - await mount( - - - - - - - ) - - const checkboxGroup = await CheckboxGroupLocator.find() - const input = await checkboxGroup.find('input[value="football"]') - await input.click() - - expect(onChange.lastCall.args[0]).to.deep.equal([ - 'basketball', - 'volleyball' - ]) - }) - - it('passes the array of selected values to onChange handler', async () => { - const onChange = stub() - - await mount( - - - - - - - ) - - const checkboxGroup = await CheckboxGroupLocator.find() - const input1 = await checkboxGroup.find('input[value="football"]') - const input2 = await checkboxGroup.find('input[value="other"]') - - await input1.click() - expect(onChange.lastCall.args[0]).to.deep.equal([ - 'basketball', - 'volleyball' - ]) - - await input2.click() - expect(onChange.lastCall.args[0]).to.deep.equal([ - 'basketball', - 'volleyball', - 'other' - ]) - }) - - describe('for a11y', async () => { - it('should meet standards', async () => { - await mount( - - - - - - - ) - - const checkboxGroup = await CheckboxGroupLocator.find() - expect( - await checkboxGroup.accessible({ - ignores: [ - 'checkboxgroup' - ] /* https://github.com/dequelabs/axe-core/issues/176 */ - }) - ).to.be.true() - }) - }) -}) diff --git a/packages/ui-checkbox/src/CheckboxGroup/locator.ts b/packages/ui-checkbox/src/CheckboxGroup/locator.ts deleted file mode 100644 index c01ac6a7af..0000000000 --- a/packages/ui-checkbox/src/CheckboxGroup/locator.ts +++ /dev/null @@ -1,28 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2015 - present Instructure, Inc. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -import { CheckboxGroupLocator } from './CheckboxGroupLocator' - -export { CheckboxGroupLocator } -export default CheckboxGroupLocator diff --git a/packages/ui-checkbox/tsconfig.build.json b/packages/ui-checkbox/tsconfig.build.json index 29141a4155..e878844fb3 100644 --- a/packages/ui-checkbox/tsconfig.build.json +++ b/packages/ui-checkbox/tsconfig.build.json @@ -22,8 +22,8 @@ { "path": "../uid/tsconfig.build.json" }, { "path": "../ui-babel-preset/tsconfig.build.json" }, { "path": "../ui-color-utils/tsconfig.build.json" }, - { "path": "../ui-test-locator/tsconfig.build.json" }, { "path": "../ui-test-utils/tsconfig.build.json" }, - { "path": "../ui-themes/tsconfig.build.json" } + { "path": "../ui-themes/tsconfig.build.json" }, + { "path": "../ui-axe-check/tsconfig.build.json" } ] }