From afd01996934b165609127e7430db84ab039600e3 Mon Sep 17 00:00:00 2001 From: Tristan Watanabe Date: Mon, 7 Mar 2022 17:05:18 -0500 Subject: [PATCH] Button (v8): convert tests to use testing-library (#21966) --- .../src/components/Button/Button.test.tsx | 412 ++--- .../Button/__snapshots__/Button.test.tsx.snap | 1483 ++++++++--------- 2 files changed, 859 insertions(+), 1036 deletions(-) diff --git a/packages/react/src/components/Button/Button.test.tsx b/packages/react/src/components/Button/Button.test.tsx index 44395bfb56713..3138a444fb9c6 100644 --- a/packages/react/src/components/Button/Button.test.tsx +++ b/packages/react/src/components/Button/Button.test.tsx @@ -1,9 +1,6 @@ import * as React from 'react'; -import * as ReactDOM from 'react-dom'; - -import * as ReactTestUtils from 'react-dom/test-utils'; -import { create } from '@fluentui/utilities/lib/test'; -import { safeMount } from '@fluentui/test-utilities'; +import { render, fireEvent } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; import { DefaultButton } from './DefaultButton/DefaultButton'; import { IconButton } from './IconButton/IconButton'; @@ -11,7 +8,6 @@ import { ActionButton } from './ActionButton/ActionButton'; import { CommandBarButton } from './CommandBarButton/CommandBarButton'; import { CompoundButton } from './CompoundButton/CompoundButton'; import { KeyCodes, resetIds } from '../../Utilities'; -import { renderIntoDocument } from '../../common/testUtilities'; import type { IContextualMenuProps } from '../../ContextualMenu'; const alertClicked = (): void => { @@ -19,37 +15,18 @@ const alertClicked = (): void => { }; describe('Button', () => { - let container: HTMLElement; - beforeEach(() => { resetIds(); - container = document.createElement('div'); - document.body.appendChild(container); - }); - - afterEach(() => { - if (container) { - ReactDOM.unmountComponentAtNode(container); - } - document.body.innerHTML = ''; }); - function render(content: JSX.Element): Element { - ReactDOM.render(content, container); - - return container.firstChild as Element; - } - it('renders DefaultButton correctly', () => { - const component = create(); - const tree = component.toJSON(); - expect(tree).toMatchSnapshot(); + const { container } = render(); + expect(container).toMatchSnapshot(); }); it('renders ActionButton correctly', () => { - const component = create(Button); - const tree = component.toJSON(); - expect(tree).toMatchSnapshot(); + const { container } = render(Button); + expect(container).toMatchSnapshot(); }); it('renders a DefaultButton with a keytip correctly', () => { @@ -57,13 +34,12 @@ describe('Button', () => { content: 'A', keySequences: ['a'], }; - const component = create(); - const tree = component.toJSON(); - expect(tree).toMatchSnapshot(); + const { container } = render(); + expect(container).toMatchSnapshot(); }); it('renders CommandBarButton correctly', () => { - const component = create( + const { container } = render( { }} />, ); - const tree = component.toJSON(); - expect(tree).toMatchSnapshot(); + expect(container).toMatchSnapshot(); }); it('renders CompoundButton correctly', () => { - const component = create( + const { container } = render( Create account, ); - const tree = component.toJSON(); - expect(tree).toMatchSnapshot(); + expect(container).toMatchSnapshot(); }); it('renders IconButton correctly', () => { - const component = create(); - const tree = component.toJSON(); - expect(tree).toMatchSnapshot(); + const { container } = render(); + expect(container).toMatchSnapshot(); }); describe('DefaultButton', () => { it('can render without an onClick.', () => { - const button = render(Hello); - - expect(button.tagName).toEqual('BUTTON'); + const { container } = render(Hello); + expect(container.firstElementChild!.tagName).toEqual('BUTTON'); }); it('can render with an onClick.', () => { const onClick: () => null = () => null; - const button = render(Hello); - expect(button.tagName).toEqual('BUTTON'); + const { container } = render(Hello); + expect(container.firstElementChild!.tagName).toEqual('BUTTON'); }); it('can render with an href', () => { - const button = render( + const { container } = render( Hello , ); - expect(button.tagName).toEqual('A'); + expect(container.firstElementChild!.tagName).toEqual('A'); }); it('can handle elementRef', () => { const ref = React.createRef(); - safeMount(Content, button => { - expect(ref.current).toBeTruthy(); - }); + render(Content); + expect(ref.current).toBeTruthy(); }); describe('aria attributes', () => { it('does not apply aria attributes that are not passed in', () => { - const button: any = render( + const { getByRole } = render( Hello , ); + const button = getByRole('link'); expect(button.getAttribute('aria-label')).toBeNull(); expect(button.getAttribute('aria-labelledby')).toBeNull(); @@ -145,7 +117,7 @@ describe('Button', () => { }); it('overrides native aria-label with Button ariaLabel', () => { - const button = render( + const { getByRole } = render( { Hello , ); + const button = getByRole('link'); expect(button.getAttribute('aria-label')).toEqual('ButtonLabel'); expect(button.getAttribute('aria-labelledby')).toBeNull(); @@ -162,11 +135,12 @@ describe('Button', () => { }); it('applies aria-label', () => { - const button = render( + const { getByRole } = render( Hello , ); + const button = getByRole('link'); expect(button.getAttribute('aria-label')).toEqual('MyLabel'); expect(button.getAttribute('aria-labelledby')).toBeNull(); @@ -174,27 +148,29 @@ describe('Button', () => { }); it('applies aria-labelledby', () => { - const button = render( + const { getByRole } = render( Hello , ); + const button = getByRole('link'); expect(button.getAttribute('aria-labelledby')).toEqual('someid'); expect(button.getAttribute('aria-describedby')).toBeNull(); }); it('does not apply aria-labelledby to a button with no text', () => { - const button = render( + const { getByRole } = render( , ); + const button = getByRole('link'); expect(button.getAttribute('aria-labelledby')).toBeNull(); expect(button.getAttribute('aria-describedby')).toEqual('someid'); }); it('applies aria-labelledby and aria-describedby', () => { - const button = render( + const { getByRole } = render( { Hello , ); + const button = getByRole('link'); expect(button.getAttribute('aria-label')).toBeNull(); @@ -213,13 +190,14 @@ describe('Button', () => { }); it('applies aria-describedby to an IconButton', () => { - const button = render( + const { getByRole } = render( , ); + const button = getByRole('button'); expect(button.getAttribute('aria-label')).toBeNull(); @@ -229,7 +207,7 @@ describe('Button', () => { }); it('applies aria-labelledby and aria-describedby to a CompoundButton with ariaDescription', () => { - const button = render( + const { getByRole } = render( { And this is the label , ); + const button = getByRole('button'); expect(button.getAttribute('aria-label')).toBeNull(); @@ -250,9 +229,10 @@ describe('Button', () => { 'applies aria-labelledby and aria-describedby to a CompoundButton with secondaryText ' + 'and no ariaDescription', () => { - const button = render( + const { getByRole } = render( And this is the label, ); + const button = getByRole('button'); expect(button.getAttribute('aria-label')).toBeNull(); @@ -263,23 +243,23 @@ describe('Button', () => { ); it('does not apply aria-pressed to an unchecked button', () => { - const button: any = render(Hello); + const { getByRole } = render(Hello); - expect(button.getAttribute('aria-pressed')).toEqual('false'); + expect(getByRole('button').getAttribute('aria-pressed')).toEqual('false'); }); it('applies aria-pressed to a checked button', () => { - const button: any = render( + const { getByRole } = render( Hello , ); - expect(button.getAttribute('aria-pressed')).toEqual('true'); + expect(getByRole('button').getAttribute('aria-pressed')).toEqual('true'); }); it('does not apply aria-pressed to an unchecked split button', () => { - const button: any = render( + const { getAllByRole } = render( { , ); - expect(button.getAttribute('aria-pressed')).toEqual('false'); + expect(getAllByRole('button')[0].getAttribute('aria-pressed')).toEqual('false'); }); it('applies aria-checked to a role=menuitemcheckbox checked button', () => { - const button: any = render( + const { getByRole } = render( Hello , ); - expect(button.getAttribute('aria-checked')).toEqual('true'); + expect(getByRole('menuitemcheckbox').getAttribute('aria-checked')).toEqual('true'); }); it('applies aria-checked to a role=checkbox checked button', () => { - const button: any = render( + const { getByRole } = render( Hello , ); - expect(button.getAttribute('aria-checked')).toEqual('true'); + expect(getByRole('checkbox').getAttribute('aria-checked')).toEqual('true'); }); it('applies aria-checked=false to a role=checkbox button even if toggle is not passed', () => { - const button: any = render(Hello); + const { getByRole } = render(Hello); - expect(button.getAttribute('aria-checked')).toEqual('false'); + expect(getByRole('checkbox').getAttribute('aria-checked')).toEqual('false'); }); it('does not mutate menuprops hidden property', () => { @@ -338,13 +318,13 @@ describe('Button', () => { }, ], }; - const button: any = render( + const { getAllByRole } = render( Hello , ); - expect(button).toBeTruthy(); + expect(getAllByRole('button')[0]).toBeTruthy(); expect(menuProps.hidden).toEqual(false); }); @@ -359,14 +339,16 @@ describe('Button', () => { ], }; - const button = renderIntoDocument(Hello); - ReactTestUtils.Simulate.click(button); + const { getByRole } = render(Hello); + const button = getByRole('button'); + + userEvent.click(button); expect(button.getAttribute('aria-controls')).toBe('custom-id'); }); it('applies aria-pressed to a checked split button', () => { - const button: any = render( + const { getAllByRole } = render( { , ); - expect(button.getAttribute('aria-pressed')).toEqual('true'); + expect(getAllByRole('button')[0].getAttribute('aria-pressed')).toEqual('true'); }); }); describe('with menuProps', () => { - let button: Element; - - beforeAll(() => { - button = render(Hello); - }); - it('contains aria-haspopup=true', () => { - expect(button.getAttribute('aria-haspopup')).toEqual('true'); + const { getAllByRole } = render( + Hello, + ); + + expect(getAllByRole('button')[0].getAttribute('aria-haspopup')).toEqual('true'); }); }); describe('without menuProps', () => { - let button: Element; - - beforeAll(() => { - button = render(Hello); - }); - it('does not contain aria-haspopup', () => { - expect(button.getAttribute('aria-haspopup')).toEqual(null); + const { getByRole } = render(Hello); + expect(getByRole('button').getAttribute('aria-haspopup')).toEqual(null); }); }); describe('with menuIconProps', () => { - let button: Element; - - beforeAll(() => { - button = render(Hello); - }); - it('Contains the expected icon via menuIconProps', () => { - expect(button.querySelectorAll('[data-icon-name="fontColor"]').length).toEqual(1); + const { getAllByRole } = render(Hello); + expect(getAllByRole('button')[0].querySelectorAll('[data-icon-name="fontColor"]').length).toEqual(1); }); }); it('Providing onClick and menuProps does not render a SplitButton', () => { - const button = render( + const { getAllByRole } = render( { }} />, ); - expect(button.tagName).not.toEqual('DIV'); + expect(getAllByRole('button')[0].tagName).not.toEqual('DIV'); }); it('Providing onKeyDown and menuProps still fires provided onKeyDown', () => { const keyDownSpy = jest.fn(); - - const button = render( + render( { }} />, ); - - ReactTestUtils.Simulate.keyDown(button, { which: KeyCodes.enter }); - + userEvent.tab(); + userEvent.keyboard('{enter}'); expect(keyDownSpy).toHaveBeenCalled(); }); it('Providing onKeyDown, menuProps and splitButton=true fires provided onKeyDown on both buttons', () => { const keyDownSpy = jest.fn(); - - const button = render( + render( { }} />, ); - const primaryButtonDOM: HTMLElement = button.querySelector( - "[data-automationid='splitbuttonprimary']", - ) as HTMLElement; - - ReactTestUtils.Simulate.keyDown(primaryButtonDOM, { which: KeyCodes.enter }); - + userEvent.tab(); + userEvent.keyboard('{arrowdown}'); expect(keyDownSpy).toHaveBeenCalled(); }); it('Space keydown in a splitButton will fire onClick', () => { const onClickSpy = jest.fn(); - const button = render( + render( { }} />, ); - const buttonContainer: HTMLDivElement = button.getElementsByTagName('span')[0] as HTMLDivElement; - - ReactTestUtils.Simulate.keyDown(buttonContainer, { which: KeyCodes.space }); - + userEvent.tab(); + userEvent.keyboard('{space}'); expect(onClickSpy).toHaveBeenCalled(); }); it('Providing onClick, menuProps and setting splitButton to true renders a SplitButton', () => { - const button = render( + const { getAllByRole } = render( { }} />, ); - expect(button.tagName).toEqual('DIV'); + expect(getAllByRole('button')[0].tagName).toEqual('DIV'); }); it('Tapping menu button of SplitButton expands menu', () => { - const button = render( + const { getAllByRole } = render( { }} />, ); - const menuButtonDOM: HTMLButtonElement = button.getElementsByTagName('button')[1] as HTMLButtonElement; - ReactTestUtils.Simulate.click(menuButtonDOM); - expect(button.getAttribute('aria-expanded')).toEqual('true'); + const menuButton = getAllByRole('button')[2]; + userEvent.click(menuButton); + expect(getAllByRole('button')[0].getAttribute('aria-expanded')).toEqual('true'); }); it('Touch Start on primary button of SplitButton expands menu', () => { - const button = render( + const { getAllByRole } = render( { }} />, ); - const primaryButtonDOM: HTMLButtonElement = button.getElementsByTagName('button')[0] as HTMLButtonElement; + const primaryButton = getAllByRole('button')[1]; // in a normal scenario, when we do a touchstart we would also cause a // click event to fire. This doesn't happen in the simulator so we're // manually adding this in. - ReactTestUtils.Simulate.touchStart(primaryButtonDOM); - ReactTestUtils.Simulate.click(primaryButtonDOM); - expect(button.getAttribute('aria-expanded')).toEqual('true'); + fireEvent.touchStart(primaryButton); + userEvent.click(primaryButton); + expect(getAllByRole('button')[0].getAttribute('aria-expanded')).toEqual('true'); }); it('If menu trigger is disabled, pressing down does not trigger menu', () => { - const button = render( + const { getAllByRole } = render( { }} />, ); - - ReactTestUtils.Simulate.keyDown(button, { - which: KeyCodes.down, - }); - expect(button.getAttribute('aria-expanded')).toEqual('false'); + userEvent.tab(); + userEvent.keyboard('{arrowdown}'); + expect(getAllByRole('button')[0].getAttribute('aria-expanded')).toEqual('false'); }); it('If menu trigger is specified, default key is overridden', () => { - const button = render( + const { getAllByRole } = render( { />, ); - ReactTestUtils.Simulate.keyDown(button, { - which: KeyCodes.down, - }); + const button = getAllByRole('button')[0]; + userEvent.tab(); + + userEvent.keyboard('{arrowdown}'); expect(button.getAttribute('aria-expanded')).toEqual('false'); - ReactTestUtils.Simulate.keyDown(button, { - which: KeyCodes.right, - }); + userEvent.keyboard('{arrowright}'); expect(button.getAttribute('aria-expanded')).toEqual('true'); }); @@ -701,7 +659,7 @@ describe('Button', () => { }); function buildRenderButtonWithMenu(callbackMock?: jest.Mock, persistMenu?: boolean): HTMLElement { - const button: HTMLElement = renderIntoDocument( + const { getAllByRole } = render( { }} />, ); - return button; + return getAllByRole('button')[0]; } it('Clicking SplitButton button triggers action', () => { const button: HTMLElement = buildRenderButtonWithMenu(); const menuButtonDOM: HTMLButtonElement = button.querySelectorAll('button')[0]; - - ReactTestUtils.Simulate.click(menuButtonDOM); + userEvent.click(menuButtonDOM); expect(didClick).toEqual(true); }); it('Pressing alt + down on SplitButton triggers menu', () => { const button: HTMLElement = buildRenderButtonWithMenu(); - const menuButtonElement = button.querySelectorAll('button')[1]; - - ReactTestUtils.Simulate.keyDown(menuButtonElement, { - which: KeyCodes.down, - altKey: true, - }); + userEvent.tab(); + userEvent.keyboard('{alt}{arrowdown}'); expect(button.getAttribute('aria-expanded')).toEqual('true'); }); @@ -751,11 +704,10 @@ describe('Button', () => { const button: HTMLElement = buildRenderButtonWithMenu(callbackMock); const menuButtonElement = button.querySelectorAll('button')[1]; + userEvent.click(menuButtonElement); - ReactTestUtils.Simulate.click(menuButtonElement); expect(button.getAttribute('aria-expanded')).toEqual('true'); - - ReactTestUtils.Simulate.click(menuButtonElement); + userEvent.click(menuButtonElement); expect(button.getAttribute('aria-expanded')).toEqual('false'); expect(callbackMock.mock.calls.length).toBe(1); }); @@ -765,17 +717,16 @@ describe('Button', () => { const button: HTMLElement = buildRenderButtonWithMenu(callbackMock, true); const menuButtonElement = button.querySelectorAll('button')[1]; + userEvent.click(menuButtonElement); - ReactTestUtils.Simulate.click(menuButtonElement); expect(button.getAttribute('aria-expanded')).toEqual('true'); - - ReactTestUtils.Simulate.click(menuButtonElement); + userEvent.click(menuButtonElement); expect(button.getAttribute('aria-expanded')).toEqual('false'); expect(callbackMock.mock.calls.length).toBe(1); }); it('A disabled SplitButton does not respond to input events', () => { - const button: HTMLElement = renderIntoDocument( + const { getAllByRole } = render( { />, ); - ReactTestUtils.Simulate.click(button); - ReactTestUtils.Simulate.keyDown(button, { - which: KeyCodes.down, - altKey: true, - }); - ReactTestUtils.Simulate.keyUp(button, { - which: KeyCodes.down, - altKey: true, - }); - ReactTestUtils.Simulate.keyPress(button, { - which: KeyCodes.down, - altKey: true, - }); - ReactTestUtils.Simulate.mouseDown(button, { - type: 'mousedown', - clientX: 0, - clientY: 0, - }); - - ReactTestUtils.Simulate.mouseUp(button, { - type: 'mouseup', - clientX: 0, - clientY: 0, - }); + const button = getAllByRole('button')[0]; + userEvent.click(button); + userEvent.keyboard('{alt}{arrowdown}'); expect(didClick).toEqual(false); }); it('A disabled Button does not respond to input events', () => { - const button: HTMLElement = renderIntoDocument( + const { getAllByRole } = render( { />, ); - ReactTestUtils.Simulate.click(button); - ReactTestUtils.Simulate.keyDown(button, { - which: KeyCodes.down, - altKey: true, - }); - ReactTestUtils.Simulate.keyUp(button, { - which: KeyCodes.down, - altKey: true, - }); - ReactTestUtils.Simulate.keyPress(button, { - which: KeyCodes.down, - altKey: true, - }); - ReactTestUtils.Simulate.mouseDown(button, { - type: 'mousedown', - clientX: 0, - clientY: 0, - }); - - ReactTestUtils.Simulate.mouseUp(button, { - type: 'mouseup', - clientX: 0, - clientY: 0, - }); + const button = getAllByRole('button')[0]; + userEvent.click(button); + userEvent.keyboard('{alt}{arrowdown}'); expect(didClick).toEqual(false); }); it('A focusable disabled button does not respond to input events', () => { - const button: HTMLElement = renderIntoDocument( + const { getAllByRole } = render( { />, ); - ReactTestUtils.Simulate.click(button); - ReactTestUtils.Simulate.keyDown(button, { - which: KeyCodes.down, - altKey: true, - }); - ReactTestUtils.Simulate.keyUp(button, { - which: KeyCodes.down, - altKey: true, - }); - ReactTestUtils.Simulate.keyPress(button, { - which: KeyCodes.down, - altKey: true, - }); - ReactTestUtils.Simulate.mouseDown(button, { - type: 'mousedown', - clientX: 0, - clientY: 0, - }); - - ReactTestUtils.Simulate.mouseUp(button, { - type: 'mouseup', - clientX: 0, - clientY: 0, - }); + const button = getAllByRole('button')[0]; + userEvent.click(button); + userEvent.keyboard('{alt}{arrowdown}'); expect(didClick).toEqual(false); }); it('A focusable disabled menu button does not respond to input events', () => { - const button: HTMLElement = renderIntoDocument( + const { getAllByRole } = render( { />, ); - ReactTestUtils.Simulate.click(button); - ReactTestUtils.Simulate.keyDown(button, { - which: KeyCodes.down, - altKey: true, - }); - ReactTestUtils.Simulate.keyUp(button, { - which: KeyCodes.down, - altKey: true, - }); - ReactTestUtils.Simulate.keyPress(button, { - which: KeyCodes.down, - altKey: true, - }); - ReactTestUtils.Simulate.mouseDown(button, { - type: 'mousedown', - clientX: 0, - clientY: 0, - }); - - ReactTestUtils.Simulate.mouseUp(button, { - type: 'mouseup', - clientX: 0, - clientY: 0, - }); + const button = getAllByRole('button')[0]; + userEvent.click(button); + userEvent.keyboard('{alt}{arrowdown}'); expect(didClick).toEqual(false); }); }); @@ -988,12 +855,9 @@ describe('Button', () => { ); - render(element); - - const button = render(element); - - ReactTestUtils.Simulate.click(button); - + const { getAllByRole } = render(element); + const button = getAllByRole('button')[0]; + userEvent.click(button); // get the menu id from the button's aria attribute const menuId = button.getAttribute('aria-controls'); expect(menuId).toBeTruthy(); @@ -1046,26 +910,22 @@ describe('Button', () => { it('Click on button opens the menu, escape press dismisses menu', () => { const callbackMock = jest.fn(); const menuProps = { items: [{ key: 'item', name: 'Item' }], onDismiss: callbackMock }; - const element: React.ReactElement = ( + const { getAllByRole } = render( {'Button Text'} - + , ); - render(element); - - const button = render(element); - - ReactTestUtils.Simulate.click(button); + const button = getAllByRole('button')[0]; + userEvent.click(button); // get the menu id from the button's aria attribute const menuId = button.getAttribute('aria-controls'); expect(menuId).toBeTruthy(); - const contextualMenuElement = button.ownerDocument!.getElementById(menuId as string); expect(contextualMenuElement).not.toBeNull(); - - ReactTestUtils.Simulate.keyDown(contextualMenuElement!, { which: KeyCodes.escape }); + userEvent.tab(); + userEvent.keyboard('{esc}'); expect(callbackMock.mock.calls.length).toBe(1); // Expect that the menu doesn't exist any more since it's been dismissed @@ -1074,7 +934,7 @@ describe('Button', () => { }); it(`If button has text but labelElementId provided in menuProps, contextual menu has - aria-labelledBy reflecting labelElementId`, () => { + aria-labelledBy reflecting labelElementId`, () => { const explicitLabelElementId = 'id_ExplicitLabel'; const contextualMenuElement = buildRenderAndClickButtonAndReturnContextualMenuDOMElement( { labelElementId: explicitLabelElementId }, diff --git a/packages/react/src/components/Button/__snapshots__/Button.test.tsx.snap b/packages/react/src/components/Button/__snapshots__/Button.test.tsx.snap index 87c08c329a6f6..218d40eeec072 100644 --- a/packages/react/src/components/Button/__snapshots__/Button.test.tsx.snap +++ b/packages/react/src/components/Button/__snapshots__/Button.test.tsx.snap @@ -1,858 +1,821 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`Button renders ActionButton correctly 1`] = ` - + + `; exports[`Button renders CommandBarButton correctly 1`] = ` - + + `; exports[`Button renders CompoundButton correctly 1`] = ` - + + `; exports[`Button renders DefaultButton correctly 1`] = ` - + + `; exports[`Button renders IconButton correctly 1`] = ` - + + + + `; exports[`Button renders a DefaultButton with a keytip correctly 1`] = ` - + + `;