From dee779179b62532be34bd7c864d122d4ea0910ba Mon Sep 17 00:00:00 2001 From: Charles Bochet Date: Tue, 15 Apr 2025 17:34:28 +0200 Subject: [PATCH] Remove user action from waitFor in stories (#11588) As per title: - waitFor is a loop that waits for a condition to be filled, it should be use to expect or in rare case to wait for element to be present in the page (in most cases, you can use findByXXX) - user actions should not be in this loop, otherwise they will be triggered multiple times --- .../__stories__/AddressFieldInput.stories.tsx | 3 +- .../__stories__/NumberFieldInput.stories.tsx | 14 +++-- .../__stories__/RatingFieldInput.stories.tsx | 8 ++- .../RichTextFieldInput.stories.tsx | 11 ++-- .../__stories__/TextFieldInput.stories.tsx | 21 ++++++-- .../__tests__/useExportFetchRecords.test.ts | 4 +- .../__stories__/IconPicker.stories.tsx | 4 +- ...nMenu.stories.tsx => Dropdown.stories.tsx} | 54 ++++++++++--------- .../SettingsExperience.stories.tsx | 12 ++--- .../OverflowTextWithTooltip.stories.tsx | 2 +- 10 files changed, 77 insertions(+), 56 deletions(-) rename packages/twenty-front/src/modules/ui/layout/dropdown/components/__stories__/{DropdownMenu.stories.tsx => Dropdown.stories.tsx} (92%) diff --git a/packages/twenty-front/src/modules/object-record/record-field/meta-types/input/components/__stories__/AddressFieldInput.stories.tsx b/packages/twenty-front/src/modules/object-record/record-field/meta-types/input/components/__stories__/AddressFieldInput.stories.tsx index 11c9253d7..a5aadabf2 100644 --- a/packages/twenty-front/src/modules/object-record/record-field/meta-types/input/components/__stories__/AddressFieldInput.stories.tsx +++ b/packages/twenty-front/src/modules/object-record/record-field/meta-types/input/components/__stories__/AddressFieldInput.stories.tsx @@ -146,8 +146,9 @@ export const Enter: Story = { play: async () => { expect(enterJestFn).toHaveBeenCalledTimes(0); + await userEvent.keyboard('{enter}'); + await waitFor(() => { - userEvent.keyboard('{enter}'); expect(enterJestFn).toHaveBeenCalledTimes(1); }); }, diff --git a/packages/twenty-front/src/modules/object-record/record-field/meta-types/input/components/__stories__/NumberFieldInput.stories.tsx b/packages/twenty-front/src/modules/object-record/record-field/meta-types/input/components/__stories__/NumberFieldInput.stories.tsx index eb4dfcc07..d4304a944 100644 --- a/packages/twenty-front/src/modules/object-record/record-field/meta-types/input/components/__stories__/NumberFieldInput.stories.tsx +++ b/packages/twenty-front/src/modules/object-record/record-field/meta-types/input/components/__stories__/NumberFieldInput.stories.tsx @@ -139,9 +139,9 @@ export const Default: Story = {}; export const Enter: Story = { play: async () => { expect(enterJestFn).toHaveBeenCalledTimes(0); + await userEvent.keyboard('{enter}'); await waitFor(() => { - userEvent.keyboard('{enter}'); expect(enterJestFn).toHaveBeenCalledTimes(1); }); }, @@ -151,8 +151,9 @@ export const Escape: Story = { play: async () => { expect(escapeJestfn).toHaveBeenCalledTimes(0); + await userEvent.keyboard('{esc}'); + await waitFor(() => { - userEvent.keyboard('{esc}'); expect(escapeJestfn).toHaveBeenCalledTimes(1); }); }, @@ -166,8 +167,9 @@ export const ClickOutside: Story = { const emptyDiv = canvas.getByTestId('data-field-input-click-outside-div'); + await userEvent.click(emptyDiv); + await waitFor(() => { - userEvent.click(emptyDiv); expect(clickOutsideJestFn).toHaveBeenCalledTimes(1); }); }, @@ -177,8 +179,9 @@ export const Tab: Story = { play: async () => { expect(tabJestFn).toHaveBeenCalledTimes(0); + await userEvent.keyboard('{tab}'); + await waitFor(() => { - userEvent.keyboard('{tab}'); expect(tabJestFn).toHaveBeenCalledTimes(1); }); }, @@ -188,8 +191,9 @@ export const ShiftTab: Story = { play: async () => { expect(shiftTabJestFn).toHaveBeenCalledTimes(0); + await userEvent.keyboard('{shift>}{tab}'); + await waitFor(() => { - userEvent.keyboard('{shift>}{tab}'); expect(shiftTabJestFn).toHaveBeenCalledTimes(1); }); }, diff --git a/packages/twenty-front/src/modules/object-record/record-field/meta-types/input/components/__stories__/RatingFieldInput.stories.tsx b/packages/twenty-front/src/modules/object-record/record-field/meta-types/input/components/__stories__/RatingFieldInput.stories.tsx index b044df4c0..21751c7c8 100644 --- a/packages/twenty-front/src/modules/object-record/record-field/meta-types/input/components/__stories__/RatingFieldInput.stories.tsx +++ b/packages/twenty-front/src/modules/object-record/record-field/meta-types/input/components/__stories__/RatingFieldInput.stories.tsx @@ -9,7 +9,6 @@ import { RecordFieldComponentInstanceContext } from '@/object-record/record-fiel import { DEFAULT_CELL_SCOPE } from '@/object-record/record-table/record-table-cell/hooks/useOpenRecordTableCellV2'; import { getRecordFieldInputId } from '@/object-record/utils/getRecordFieldInputId'; import { FieldMetadataType } from 'twenty-shared/types'; -import { isDefined } from 'twenty-shared/utils'; import { FieldRatingValue } from '../../../../types/FieldMetadata'; import { useRatingField } from '../../../hooks/useRatingField'; import { RatingFieldInput, RatingFieldInputProps } from '../RatingFieldInput'; @@ -118,11 +117,10 @@ export const Submit: Story = { const input = canvas.getByRole('slider', { name: 'Rating' }); const firstStar = input.firstElementChild; + await userEvent.click(firstStar); + await waitFor(() => { - if (isDefined(firstStar)) { - userEvent.click(firstStar); - expect(submitJestFn).toHaveBeenCalledTimes(1); - } + expect(submitJestFn).toHaveBeenCalledTimes(1); }); }, }; diff --git a/packages/twenty-front/src/modules/object-record/record-field/meta-types/input/components/__stories__/RichTextFieldInput.stories.tsx b/packages/twenty-front/src/modules/object-record/record-field/meta-types/input/components/__stories__/RichTextFieldInput.stories.tsx index 528b111ed..219b1be79 100644 --- a/packages/twenty-front/src/modules/object-record/record-field/meta-types/input/components/__stories__/RichTextFieldInput.stories.tsx +++ b/packages/twenty-front/src/modules/object-record/record-field/meta-types/input/components/__stories__/RichTextFieldInput.stories.tsx @@ -111,8 +111,9 @@ export const Escape: Story = { play: async () => { expect(escapeJestFn).toHaveBeenCalledTimes(0); + await userEvent.keyboard('{esc}'); + await waitFor(() => { - userEvent.keyboard('{esc}'); expect(escapeJestFn).toHaveBeenCalledTimes(1); }); }, @@ -123,9 +124,11 @@ export const ClickOutside: Story = { const canvas = within(canvasElement); expect(clickOutsideJestFn).toHaveBeenCalledTimes(0); - await waitFor(async () => { - const outsideElement = await canvas.findByTestId('click-outside-element'); - userEvent.click(outsideElement); + const outsideElement = await canvas.findByTestId('click-outside-element'); + + await userEvent.click(outsideElement); + + await waitFor(() => { expect(clickOutsideJestFn).toHaveBeenCalledTimes(1); }); }, diff --git a/packages/twenty-front/src/modules/object-record/record-field/meta-types/input/components/__stories__/TextFieldInput.stories.tsx b/packages/twenty-front/src/modules/object-record/record-field/meta-types/input/components/__stories__/TextFieldInput.stories.tsx index 480354cc5..6d1352fde 100644 --- a/packages/twenty-front/src/modules/object-record/record-field/meta-types/input/components/__stories__/TextFieldInput.stories.tsx +++ b/packages/twenty-front/src/modules/object-record/record-field/meta-types/input/components/__stories__/TextFieldInput.stories.tsx @@ -11,6 +11,7 @@ import { FieldMetadataType } from '~/generated/graphql'; import { StorybookFieldInputDropdownFocusIdSetterEffect } from '~/testing/components/StorybookFieldInputDropdownFocusIdSetterEffect'; import { I18nFrontDecorator } from '~/testing/decorators/I18nFrontDecorator'; import { SnackBarDecorator } from '~/testing/decorators/SnackBarDecorator'; +import { sleep } from '~/utils/sleep'; import { useTextField } from '../../../hooks/useTextField'; import { TextFieldInput, TextFieldInputProps } from '../TextFieldInput'; const TextFieldValueSetterEffect = ({ value }: { value: string }) => { @@ -131,9 +132,11 @@ export const Default: Story = {}; export const Enter: Story = { play: async () => { expect(enterJestFn).toHaveBeenCalledTimes(0); + await sleep(100); + + await userEvent.keyboard('{enter}'); await waitFor(() => { - userEvent.keyboard('{enter}'); expect(enterJestFn).toHaveBeenCalledTimes(1); }); }, @@ -142,9 +145,11 @@ export const Enter: Story = { export const Escape: Story = { play: async () => { expect(escapeJestfn).toHaveBeenCalledTimes(0); + await sleep(100); + + await userEvent.keyboard('{esc}'); await waitFor(() => { - userEvent.keyboard('{esc}'); expect(escapeJestfn).toHaveBeenCalledTimes(1); }); }, @@ -153,13 +158,15 @@ export const Escape: Story = { export const ClickOutside: Story = { play: async ({ canvasElement }) => { const canvas = within(canvasElement); + await sleep(100); expect(clickOutsideJestFn).toHaveBeenCalledTimes(0); const emptyDiv = canvas.getByTestId('data-field-input-click-outside-div'); + await userEvent.click(emptyDiv); + await waitFor(() => { - userEvent.click(emptyDiv); expect(clickOutsideJestFn).toHaveBeenCalled(); }); }, @@ -168,9 +175,11 @@ export const ClickOutside: Story = { export const Tab: Story = { play: async () => { expect(tabJestFn).toHaveBeenCalledTimes(0); + await sleep(100); + + await userEvent.keyboard('{tab}'); await waitFor(() => { - userEvent.keyboard('{tab}'); expect(tabJestFn).toHaveBeenCalledTimes(1); }); }, @@ -179,9 +188,11 @@ export const Tab: Story = { export const ShiftTab: Story = { play: async () => { expect(shiftTabJestFn).toHaveBeenCalledTimes(0); + await sleep(100); + + await userEvent.keyboard('{shift>}{tab}'); await waitFor(() => { - userEvent.keyboard('{shift>}{tab}'); expect(shiftTabJestFn).toHaveBeenCalledTimes(1); }); }, diff --git a/packages/twenty-front/src/modules/object-record/record-index/export/hooks/__tests__/useExportFetchRecords.test.ts b/packages/twenty-front/src/modules/object-record/record-index/export/hooks/__tests__/useExportFetchRecords.test.ts index a405958a4..5310f118e 100644 --- a/packages/twenty-front/src/modules/object-record/record-index/export/hooks/__tests__/useExportFetchRecords.test.ts +++ b/packages/twenty-front/src/modules/object-record/record-index/export/hooks/__tests__/useExportFetchRecords.test.ts @@ -206,7 +206,7 @@ describe('useRecordData', () => { result.current.tableData.getTableData(); }); - await waitFor(async () => { + await waitFor(() => { expect(callback).toHaveBeenCalledWith( [mockPerson], [ @@ -297,7 +297,7 @@ describe('useRecordData', () => { result.current.tableData.getTableData(); }); - await waitFor(async () => { + await waitFor(() => { expect(callback).toHaveBeenCalledWith([mockPerson], []); }); }); diff --git a/packages/twenty-front/src/modules/ui/input/components/__stories__/IconPicker.stories.tsx b/packages/twenty-front/src/modules/ui/input/components/__stories__/IconPicker.stories.tsx index 4ded11428..e2a29cd3f 100644 --- a/packages/twenty-front/src/modules/ui/input/components/__stories__/IconPicker.stories.tsx +++ b/packages/twenty-front/src/modules/ui/input/components/__stories__/IconPicker.stories.tsx @@ -5,8 +5,8 @@ import { expect, userEvent, within } from '@storybook/test'; import { IconsProviderDecorator } from '~/testing/decorators/IconsProviderDecorator'; import { sleep } from '~/utils/sleep'; -import { IconPicker, IconPickerProps } from '../IconPicker'; import { ComponentDecorator } from 'twenty-ui/testing'; +import { IconPicker, IconPickerProps } from '../IconPicker'; type RenderProps = IconPickerProps; const Render = (args: RenderProps) => { @@ -123,7 +123,7 @@ export const WithSearchAndClose: Story = { name: 'Click to select icon (selected: IconBuildingSkyscraper)', }); - userEvent.click(iconPickerButton); + await userEvent.click(iconPickerButton); await sleep(500); diff --git a/packages/twenty-front/src/modules/ui/layout/dropdown/components/__stories__/DropdownMenu.stories.tsx b/packages/twenty-front/src/modules/ui/layout/dropdown/components/__stories__/Dropdown.stories.tsx similarity index 92% rename from packages/twenty-front/src/modules/ui/layout/dropdown/components/__stories__/DropdownMenu.stories.tsx rename to packages/twenty-front/src/modules/ui/layout/dropdown/components/__stories__/Dropdown.stories.tsx index 697a7d8f0..46ffaad7c 100644 --- a/packages/twenty-front/src/modules/ui/layout/dropdown/components/__stories__/DropdownMenu.stories.tsx +++ b/packages/twenty-front/src/modules/ui/layout/dropdown/components/__stories__/Dropdown.stories.tsx @@ -6,6 +6,15 @@ import { useState } from 'react'; import { DropdownMenuSkeletonItem } from '@/ui/input/relation-picker/components/skeletons/DropdownMenuSkeletonItem'; +import { DropdownMenuHeaderLeftComponent } from '@/ui/layout/dropdown/components/DropdownMenuHeader/internal/DropdownMenuHeaderLeftComponent'; +import { Avatar, IconChevronLeft } from 'twenty-ui/display'; +import { Button } from 'twenty-ui/input'; +import { + MenuItem, + MenuItemMultiSelectAvatar, + MenuItemSelectAvatar, +} from 'twenty-ui/navigation'; +import { ComponentDecorator } from 'twenty-ui/testing'; import { Dropdown } from '../Dropdown'; import { DropdownMenuHeader } from '../DropdownMenuHeader/DropdownMenuHeader'; import { DropdownMenuInput } from '../DropdownMenuInput'; @@ -13,26 +22,17 @@ import { DropdownMenuItemsContainer } from '../DropdownMenuItemsContainer'; import { DropdownMenuSearchInput } from '../DropdownMenuSearchInput'; import { DropdownMenuSeparator } from '../DropdownMenuSeparator'; import { StyledDropdownMenuSubheader } from '../StyledDropdownMenuSubheader'; -import { DropdownMenuHeaderLeftComponent } from '@/ui/layout/dropdown/components/DropdownMenuHeader/internal/DropdownMenuHeaderLeftComponent'; -import { Avatar, IconChevronLeft } from 'twenty-ui/display'; -import { Button } from 'twenty-ui/input'; -import { ComponentDecorator } from 'twenty-ui/testing'; -import { - MenuItem, - MenuItemMultiSelectAvatar, - MenuItemSelectAvatar, -} from 'twenty-ui/navigation'; const meta: Meta = { title: 'UI/Layout/Dropdown/Dropdown', component: Dropdown, - decorators: [ComponentDecorator, (Story) => ], args: { clickableComponent: