Fix CI errored tasks for front (#6806)

In this PR:
- revert de-optimization of icons bundle for storybook. This was forcing
the browser to load ~3k files while running stories
- adding lazy loading on Settings route to improve developer experience
(some files will be loaded later)
- fix FE tests: unit, modules stories, pages stories

---------

Co-authored-by: Charles Bochet <charles@twenty.com>
This commit is contained in:
Lucas Bordeau
2024-08-31 01:19:24 +02:00
committed by Charles Bochet
parent a3ea0acd1a
commit 56f8091a42
28 changed files with 599 additions and 250 deletions

View File

@ -82,7 +82,9 @@ export const DefaultWithoutSearch: Story = {
play: async () => {
const canvas = within(document.body);
expect(await canvas.findByText('Create Task')).toBeInTheDocument();
expect(
await canvas.findByText('Create Task', undefined, { timeout: 10000 }),
).toBeInTheDocument();
expect(await canvas.findByText('Go to People')).toBeInTheDocument();
expect(await canvas.findByText('Go to Companies')).toBeInTheDocument();
expect(await canvas.findByText('Go to Opportunities')).toBeInTheDocument();

View File

@ -0,0 +1,36 @@
import { Meta, StoryObj } from '@storybook/react';
import { expect, within } from '@storybook/test';
import { SnackBarDecorator } from '~/testing/decorators/SnackBarDecorator';
import { useKeyboardShortcutMenu } from '@/keyboard-shortcut-menu/hooks/useKeyboardShortcutMenu';
import { useEffect } from 'react';
import { ComponentWithRouterDecorator } from '~/testing/decorators/ComponentWithRouterDecorator';
import { KeyboardShortcutMenu } from '../KeyboardShortcutMenu';
const meta: Meta<typeof KeyboardShortcutMenu> = {
title: 'Modules/KeyboardShortcutMenu/KeyboardShortcutMenu',
component: KeyboardShortcutMenu,
decorators: [
(Story) => {
const { openKeyboardShortcutMenu } = useKeyboardShortcutMenu();
useEffect(() => {
openKeyboardShortcutMenu();
}, [openKeyboardShortcutMenu]);
return <Story />;
},
SnackBarDecorator,
ComponentWithRouterDecorator,
],
};
export default meta;
type Story = StoryObj<typeof KeyboardShortcutMenu>;
export const Default: Story = {
play: async () => {
const canvas = within(document.body);
expect(await canvas.findByText('Keyboard shortcuts')).toBeInTheDocument();
},
};

View File

@ -1,6 +1,4 @@
import { expect } from '@storybook/jest';
import { Meta, StoryObj } from '@storybook/react';
import { within } from '@storybook/test';
import { ComponentWithRecoilScopeDecorator } from '~/testing/decorators/ComponentWithRecoilScopeDecorator';
import { ComponentWithRouterDecorator } from '~/testing/decorators/ComponentWithRouterDecorator';
@ -10,6 +8,7 @@ import { SnackBarDecorator } from '~/testing/decorators/SnackBarDecorator';
import { graphqlMocks } from '~/testing/graphqlMocks';
import { NavigationDrawerSectionForObjectMetadataItems } from '@/object-metadata/components/NavigationDrawerSectionForObjectMetadataItems';
import { within } from '@storybook/test';
import { PrefetchLoadedDecorator } from '~/testing/decorators/PrefetchLoadedDecorator';
const meta: Meta<typeof NavigationDrawerSectionForObjectMetadataItems> = {
@ -32,10 +31,10 @@ export default meta;
type Story = StoryObj<typeof NavigationDrawerSectionForObjectMetadataItems>;
export const Default: Story = {
play: async () => {
const canvas = within(document.body);
expect(await canvas.findByText('People')).toBeInTheDocument();
expect(await canvas.findByText('Companies')).toBeInTheDocument();
expect(await canvas.findByText('Opportunities')).toBeInTheDocument();
play: async ({ canvasElement }) => {
const canvas = within(canvasElement);
await canvas.findByText('People', undefined, { timeout: 10000 });
await canvas.findByText('Companies');
await canvas.findByText('Opportunities');
},
};

View File

@ -27,6 +27,7 @@ export const ObjectFilterDropdownDateInput = () => {
const [internalDate, setInternalDate] = useState<Date | null>(
selectedFilter?.value ? new Date(selectedFilter.value) : new Date(),
);
const handleChange = (date: Date | null) => {
setInternalDate(date);

View File

@ -1,5 +1,5 @@
import { useState } from 'react';
import styled from '@emotion/styled';
import { useState } from 'react';
import { useRecoilValue } from 'recoil';
import { useIcons } from 'twenty-ui';

View File

@ -0,0 +1,118 @@
import { Meta, StoryObj } from '@storybook/react';
import { TaskGroups } from '@/activities/tasks/components/TaskGroups';
import { MultipleFiltersDropdownButton } from '@/object-record/object-filter-dropdown/components/MultipleFiltersDropdownButton';
import { useFilterDropdown } from '@/object-record/object-filter-dropdown/hooks/useFilterDropdown';
import { ObjectFilterDropdownScope } from '@/object-record/object-filter-dropdown/scopes/ObjectFilterDropdownScope';
import { within } from '@storybook/test';
import { ComponentDecorator } from 'twenty-ui';
import { FieldMetadataType } from '~/generated/graphql';
import { IconsProviderDecorator } from '~/testing/decorators/IconsProviderDecorator';
import { ObjectMetadataItemsDecorator } from '~/testing/decorators/ObjectMetadataItemsDecorator';
import { SnackBarDecorator } from '~/testing/decorators/SnackBarDecorator';
const meta: Meta<typeof MultipleFiltersDropdownButton> = {
title:
'Modules/ObjectRecord/ObjectFilterDropdown/MultipleFiltersDropdownButton',
component: MultipleFiltersDropdownButton,
decorators: [
(Story) => {
const { setAvailableFilterDefinitions } = useFilterDropdown({
filterDropdownId: 'entity-tasks-filter-scope',
});
setAvailableFilterDefinitions([
{
fieldMetadataId: '1',
iconName: 'IconUser',
label: 'Text',
type: FieldMetadataType.Text,
},
{
fieldMetadataId: '2',
iconName: 'Icon123',
label: 'Email',
type: FieldMetadataType.Email,
},
{
fieldMetadataId: '3',
iconName: 'IconNumber',
label: 'Number',
type: FieldMetadataType.Number,
},
{
fieldMetadataId: '3',
iconName: 'IconCalendar',
label: 'Date',
type: FieldMetadataType.DateTime,
},
]);
return (
<ObjectFilterDropdownScope filterScopeId="entity-tasks-filter-scope">
<Story />
</ObjectFilterDropdownScope>
);
},
ObjectMetadataItemsDecorator,
SnackBarDecorator,
ComponentDecorator,
IconsProviderDecorator,
],
args: {
hotkeyScope: {
scope: 'object-filter-dropdown',
},
},
};
export default meta;
type Story = StoryObj<typeof TaskGroups>;
export const Default: Story = {
play: async () => {
const canvas = within(document.body);
const filterButton = await canvas.findByText('Filter');
filterButton.click();
const textFilter = await canvas.findByText('Text');
textFilter.click();
const operatorDropdown = await canvas.findByText('Contains');
operatorDropdown.click();
const containsOption = await canvas.findByText("Doesn't contain");
containsOption.click();
},
};
export const Date: Story = {
play: async () => {
const canvas = within(document.body);
const filterButton = await canvas.findByText('Filter');
filterButton.click();
const dateFilter = await canvas.findByText('Date');
dateFilter.click();
},
};
export const Number: Story = {
play: async () => {
const canvas = within(document.body);
const filterButton = await canvas.findByText('Filter');
filterButton.click();
const dateFilter = await canvas.findByText('Number');
dateFilter.click();
},
};

View File

@ -29,6 +29,6 @@ export const Default: Story = {};
export const Performance = getProfilingStory({
componentName: 'RatingFieldDisplay',
averageThresholdInMs: 0.5,
numberOfRuns: 50,
numberOfRuns: 30,
numberOfTestsPerRun: 100,
});

View File

@ -1,4 +1,3 @@
import { useEffect } from 'react';
import { Decorator, Meta, StoryObj } from '@storybook/react';
import {
expect,
@ -8,6 +7,7 @@ import {
waitFor,
within,
} from '@storybook/test';
import { useEffect } from 'react';
import { useSetRecoilState } from 'recoil';
import { currentWorkspaceMemberState } from '@/auth/states/currentWorkspaceMemberState';
@ -141,7 +141,9 @@ export const Submit: Story = {
expect(submitJestFn).toHaveBeenCalledTimes(0);
const item = await canvas.findByText('John Wick');
const item = await canvas.findByText('John Wick', undefined, {
timeout: 3000,
});
await waitFor(() => {
userEvent.click(item);
@ -156,7 +158,7 @@ export const Cancel: Story = {
const canvas = within(canvasElement);
expect(cancelJestFn).toHaveBeenCalledTimes(0);
await canvas.findByText('John Wick');
await canvas.findByText('John Wick', undefined, { timeout: 3000 });
const emptyDiv = canvas.getByTestId('data-field-input-click-outside-div');
fireEvent.click(emptyDiv);

View File

@ -61,12 +61,7 @@ export const useHandleToggleTrashColumnFilter = ({
};
upsertCombinedViewFilter(newFilter);
}, [
columnDefinitions,
objectMetadataItem,
objectNameSingular,
upsertCombinedViewFilter,
]);
}, [columnDefinitions, objectMetadataItem, upsertCombinedViewFilter]);
return handleToggleTrashColumnFilter;
};

View File

@ -13,6 +13,7 @@ import { isDefined } from '~/utils/isDefined';
export type TextInputProps = TextInputV2ComponentProps & {
disableHotkeys?: boolean;
onInputEnter?: () => void;
dataTestId?: string;
focused?: boolean;
};
@ -22,6 +23,7 @@ export const TextInput = ({
onInputEnter,
disableHotkeys = false,
focused,
dataTestId,
...props
}: TextInputProps) => {
const inputRef = useRef<HTMLInputElement>(null);
@ -103,6 +105,7 @@ export const TextInput = ({
ref={inputRef}
// eslint-disable-next-line react/jsx-props-no-spreading
{...props}
dataTestId={dataTestId}
onFocus={handleFocus}
onBlur={handleBlur}
/>

View File

@ -127,6 +127,7 @@ export type TextInputV2ComponentProps = Omit<
LeftIcon?: IconComponent;
onKeyDown?: (event: React.KeyboardEvent<HTMLInputElement>) => void;
onBlur?: FocusEventHandler<HTMLInputElement>;
dataTestId?: string;
};
const TextInputV2Component = (
@ -151,6 +152,7 @@ const TextInputV2Component = (
LeftIcon,
autoComplete,
maxLength,
dataTestId,
}: TextInputV2ComponentProps,
// eslint-disable-next-line @nx/workspace-component-props-naming
ref: ForwardedRef<HTMLInputElement>,
@ -178,6 +180,7 @@ const TextInputV2Component = (
</StyledLeftIconContainer>
)}
<StyledInput
data-testId={dataTestId}
autoComplete={autoComplete || 'off'}
ref={combinedRef}
tabIndex={tabIndex ?? 0}

View File

@ -15,19 +15,23 @@ export const useShowAuthModal = () => {
const isLoggedIn = useIsLogged();
const onboardingStatus = useOnboardingStatus();
const subscriptionStatus = useSubscriptionStatus();
const isDefaultLayoutAuthModalVisible = useRecoilValue(
isDefaultLayoutAuthModalVisibleState,
);
return useMemo(() => {
if (isMatchingLocation(AppPath.Verify)) {
return false;
}
if (
isMatchingLocation(AppPath.Invite) ||
isMatchingLocation(AppPath.ResetPassword)
) {
return isDefaultLayoutAuthModalVisible;
}
if (
!isLoggedIn ||
onboardingStatus === OnboardingStatus.PlanRequired ||
@ -38,6 +42,7 @@ export const useShowAuthModal = () => {
) {
return true;
}
if (isMatchingLocation(AppPath.PlanRequired)) {
return (
(onboardingStatus === OnboardingStatus.Completed &&
@ -45,6 +50,7 @@ export const useShowAuthModal = () => {
subscriptionStatus === SubscriptionStatus.Canceled
);
}
return false;
}, [
isLoggedIn,

View File

@ -109,6 +109,7 @@ export const ConfirmationModal = ({
{confirmationValue && (
<Section>
<TextInput
dataTestId="confirmation-modal-input"
value={inputConfirmationValue}
onChange={handleInputConfimrationValueChange}
placeholder={confirmationPlaceholder}