From 00ea6d1cadc96721f5dcd857596af4be7e9b31c4 Mon Sep 17 00:00:00 2001 From: Tom Begley Date: Mon, 3 Feb 2025 18:51:31 +0000 Subject: [PATCH 1/2] Fix tests --- .../accordion/__tests__/Accordion.test.js | 64 ++++---- src/components/badge/__tests__/Badge.test.js | 15 +- src/components/button/Button.js | 47 +++--- .../button/__tests__/Button.test.js | 20 ++- .../card/__tests__/CardLink.test.js | 20 ++- src/components/carousel/Carousel.js | 28 ++-- .../carousel/__tests__/Carousel.test.js | 11 +- .../collapse/__tests__/Collapse.test.js | 4 +- .../__tests__/DropdownMenu.test.js | 153 +++++++++--------- .../__tests__/DropdownMenuItem.test.js | 54 +++---- src/components/fade/__tests__/Fade.test.js | 71 ++++---- src/components/form/Label.js | 4 +- src/components/input/Checklist.js | 2 - src/components/input/Input.js | 1 + src/components/input/RadioItems.js | 3 +- src/components/input/Select.js | 6 +- src/components/input/Textarea.js | 2 +- .../input/__tests__/Checkbox.test.js | 9 +- .../input/__tests__/Checklist.test.js | 19 ++- src/components/input/__tests__/Input.test.js | 17 +- .../input/__tests__/RadioButton.test.js | 7 +- .../input/__tests__/RadioItems.test.js | 20 ++- src/components/input/__tests__/Select.test.js | 5 +- src/components/input/__tests__/Switch.test.js | 9 +- .../input/__tests__/Textarea.test.js | 31 ++-- .../listgroup/__tests__/ListGroupItem.test.js | 21 ++- src/components/modal/__tests__/Modal.test.js | 40 ++--- src/components/nav/NavbarToggler.js | 5 +- src/components/nav/__tests__/NavLink.test.js | 20 ++- .../nav/__tests__/NavbarBrand.test.js | 10 +- .../nav/__tests__/NavbarSimple.test.js | 10 +- .../nav/__tests__/NavbarToggler.test.js | 5 +- .../offcanvas/__tests__/Offcanvas.test.js | 80 +++++---- .../pagination/__tests__/Pagination.test.js | 19 ++- .../popover/__tests__/Popover.test.js | 137 ++++++++-------- src/components/tabs/__tests__/Tabs.test.js | 5 +- src/components/toast/__tests__/Toast.test.js | 5 +- src/private/Link.js | 2 +- src/private/OverlayTemplates.js | 24 ++- src/private/__tests__/Link.test.js | 25 +-- src/private/__tests__/Overlay.test.js | 59 +++---- 41 files changed, 579 insertions(+), 510 deletions(-) diff --git a/src/components/accordion/__tests__/Accordion.test.js b/src/components/accordion/__tests__/Accordion.test.js index bd785ff0d..2e978e379 100644 --- a/src/components/accordion/__tests__/Accordion.test.js +++ b/src/components/accordion/__tests__/Accordion.test.js @@ -3,7 +3,7 @@ */ import React from 'react'; -import {render} from '@testing-library/react'; +import {act, render, screen, waitFor} from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import Accordion from '../Accordion'; import AccordionItem from '../AccordionItem'; @@ -91,7 +91,8 @@ describe('Accordion', () => { ).not.toHaveClass('show'); }); - test('tracks most recently clicked item with "active_item" prop', () => { + test('tracks most recently clicked item with "active_item" prop', async () => { + const user = userEvent.setup({advanceTimers: jest.advanceTimersByTime}); const mockSetProps = jest.fn(); const {container, rerender} = render( @@ -109,9 +110,7 @@ describe('Accordion', () => { accordionItems.children[1].querySelector('div.accordion-collapse') ).not.toHaveClass('show'); - userEvent.click( - accordionItems.children[1].querySelector('h2.accordion-header > button') - ); + await user.click(await screen.findByText('item-title-2')); expect(mockSetProps.mock.calls).toHaveLength(1); rerender( @@ -120,7 +119,7 @@ describe('Accordion', () => { item-content-2 ); - jest.runAllTimers(); + act(() => jest.runAllTimers()); expect( accordionItems.children[0].querySelector('div.accordion-collapse') @@ -130,9 +129,7 @@ describe('Accordion', () => { ).toHaveClass('show'); // clicking on an open item closes it - userEvent.click( - accordionItems.children[1].querySelector('h2.accordion-header > button') - ); + await user.click(await screen.findByText('item-title-2')); expect(mockSetProps.mock.calls).toHaveLength(2); rerender( @@ -141,7 +138,7 @@ describe('Accordion', () => { item-content-2 ); - jest.runAllTimers(); + act(() => jest.runAllTimers()); expect( accordionItems.children[0].querySelector('div.accordion-collapse') @@ -151,7 +148,8 @@ describe('Accordion', () => { ).not.toHaveClass('show'); }); - test('keeps item open with "always_open" prop', () => { + test('keeps item open with "always_open" prop', async () => { + const user = userEvent.setup({advanceTimers: jest.advanceTimersByTime}); const mockSetProps = jest.fn(); const {container} = render( @@ -172,34 +170,31 @@ describe('Accordion', () => { ).not.toHaveClass('show'); // Click on the second item - userEvent.click( - accordionItems.children[1].querySelector('h2.accordion-header > button') - ); + await user.click(await screen.findByText('item-title-2')); expect(mockSetProps.mock.calls).toHaveLength(1); - // Allow the click to take effect - jest.runAllTimers(); + // wait for transition to complete + await waitFor(() => + expect( + accordionItems.children[1].querySelector('div.accordion-collapse') + ).toHaveClass('show') + ); // Check just the second item is open expect( accordionItems.children[0].querySelector('div.accordion-collapse') ).not.toHaveClass('show'); - expect( - accordionItems.children[1].querySelector('div.accordion-collapse') - ).toHaveClass('show'); // Click on the first item - userEvent.click( - accordionItems.children[0].querySelector('h2.accordion-header > button') - ); + await user.click(await screen.findByText('item-title-1')); expect(mockSetProps.mock.calls).toHaveLength(2); - // Allow the click to take effect - jest.runAllTimers(); // Check that the first child is now open, and the second remains open - expect( - accordionItems.children[0].querySelector('div.accordion-collapse') - ).toHaveClass('show'); + await waitFor(() => + expect( + accordionItems.children[0].querySelector('div.accordion-collapse') + ).toHaveClass('show') + ); expect( accordionItems.children[1].querySelector('div.accordion-collapse') ).toHaveClass('show'); @@ -224,7 +219,8 @@ describe('Accordion', () => { ).toHaveClass('show'); }); - test('tracks most recently clicked item with "active_item" prop when always_open', () => { + test('tracks most recently clicked item with "active_item" prop when always_open', async () => { + const user = userEvent.setup({advanceTimers: jest.advanceTimersByTime}); const mockSetProps = jest.fn(); const {container, rerender} = render( @@ -248,9 +244,7 @@ describe('Accordion', () => { ).not.toHaveClass('show'); // Click the middle option - userEvent.click( - accordionItems.children[1].querySelector('h2.accordion-header > button') - ); + await user.click(await screen.getByText('item-title-2')); expect(mockSetProps.mock.calls).toHaveLength(1); rerender( @@ -264,7 +258,7 @@ describe('Accordion', () => { item-content-3 ); - jest.runAllTimers(); + act(() => jest.runAllTimers()); // Check first option stayed open, middle option now open but third still // closed @@ -279,9 +273,7 @@ describe('Accordion', () => { ).not.toHaveClass('show'); // clicking on 1st item closes it, but keeps second item open and 3rd closed - userEvent.click( - accordionItems.children[0].querySelector('h2.accordion-header > button') - ); + await user.click(await screen.getByText('item-title-1')); expect(mockSetProps.mock.calls).toHaveLength(2); rerender( @@ -295,7 +287,7 @@ describe('Accordion', () => { item-content-3 ); - jest.runAllTimers(); + act(() => jest.runAllTimers()); // Check that 1 and 3 now closed, and 2 is open expect( diff --git a/src/components/badge/__tests__/Badge.test.js b/src/components/badge/__tests__/Badge.test.js index 3ddfa7a6b..6bb1ba1d5 100644 --- a/src/components/badge/__tests__/Badge.test.js +++ b/src/components/badge/__tests__/Badge.test.js @@ -56,19 +56,21 @@ describe('Badge', () => { expect(badge.getAttribute('href')).toBe(href); }); - test('tracks clicks with n_clicks', () => { + test('tracks clicks with n_clicks', async () => { + const user = userEvent.setup(); const mockSetProps = jest.fn(); const badge = render(Clickable); expect(mockSetProps.mock.calls).toHaveLength(0); - userEvent.click(badge.getByText('Clickable')); + await user.click(badge.getByText('Clickable')); expect(mockSetProps.mock.calls).toHaveLength(1); expect(mockSetProps.mock.calls[0][0].n_clicks).toBe(1); }); - test('relative links are internal by default', () => { + test('relative links are internal by default', async () => { + const user = userEvent.setup(); const badge = render(Clickable); const mockEventListener = jest.fn(); @@ -76,11 +78,12 @@ describe('Badge', () => { window.scrollTo = jest.fn(); expect(mockEventListener.mock.calls).toHaveLength(0); - userEvent.click(badge.getByText('Clickable')); + await user.click(badge.getByText('Clickable')); expect(mockEventListener.mock.calls).toHaveLength(1); }); - test('relative links are external with external_link=true', () => { + test('relative links are external with external_link=true', async () => { + const user = userEvent.setup(); const badge = render( Clickable @@ -92,7 +95,7 @@ describe('Badge', () => { window.scrollTo = jest.fn(); expect(mockEventListener.mock.calls).toHaveLength(0); - userEvent.click(badge.getByText('Clickable')); + await user.click(badge.getByText('Clickable')); expect(mockEventListener.mock.calls).toHaveLength(0); }); }); diff --git a/src/components/button/Button.js b/src/components/button/Button.js index cc052f76d..0993aad44 100644 --- a/src/components/button/Button.js +++ b/src/components/button/Button.js @@ -12,28 +12,26 @@ import Link from '../../private/Link'; * Use the `n_clicks` prop to trigger callbacks when the button has been * clicked. */ -const Button = props => { - const { - children, - disabled, - href, - loading_state, - setProps, - n_clicks, - target, - type, - download, - name, - value, - className, - class_name, - color, - outline, - onClick, - rel, - ...otherProps - } = props; - +const Button = ({ + children, + disabled, + href, + loading_state, + setProps, + target, + type, + download, + name, + value, + className, + class_name, + color, + outline, + onClick, + rel, + n_clicks = 0, + ...otherProps +}) => { const incrementClicks = () => { if (!disabled && setProps) { setProps({ @@ -73,11 +71,6 @@ const Button = props => { ); }; -Button.dashPersistence = { - n_clicks: 0, - n_clicks_timestamp: -1 -}; - Button.propTypes = { /** * The ID of this component, used to identify dash components diff --git a/src/components/button/__tests__/Button.test.js b/src/components/button/__tests__/Button.test.js index d7cd61e95..386b07374 100644 --- a/src/components/button/__tests__/Button.test.js +++ b/src/components/button/__tests__/Button.test.js @@ -84,19 +84,21 @@ describe('Button', () => { ); }); - test('tracks clicks with n_clicks', () => { + test('tracks clicks with n_clicks', async () => { + const user = userEvent.setup(); const mockSetProps = jest.fn(); const button = render(); expect(mockSetProps.mock.calls).toHaveLength(0); - userEvent.click(button.getByText('Clickable')); + await user.click(button.getByText('Clickable')); expect(mockSetProps.mock.calls).toHaveLength(1); expect(mockSetProps.mock.calls[0][0].n_clicks).toBe(1); }); - test("doesn't track clicks if disabled", () => { + test("doesn't track clicks if disabled", async () => { + const user = userEvent.setup(); const mockSetProps = jest.fn(); const button = render( ); const mockEventListener = jest.fn(); @@ -119,11 +122,12 @@ describe('Button', () => { window.scrollTo = jest.fn(); expect(mockEventListener.mock.calls).toHaveLength(0); - userEvent.click(button.getByText('Clickable')); + await user.click(button.getByText('Clickable')); expect(mockEventListener.mock.calls).toHaveLength(1); }); - test('relative links are external with external_link=true', () => { + test('relative links are external with external_link=true', async () => { + const user = userEvent.setup(); const button = render(