From 7ce03ffdd1fd648587eaf17ee46f0b17b2091514 Mon Sep 17 00:00:00 2001 From: Charles Bochet Date: Thu, 21 Sep 2023 11:53:36 -0700 Subject: [PATCH] Refactor tests command menu (#1702) * Fix tests * Refactor tests command menu * Improve tests * Fix optimistic render breaking tests --- .../hooks/useOptimisticEffect.ts | 13 +- .../__stories__/CommandMenu.stories.tsx | 261 ++++-------------- .../__stories__/WrapperCommandMenu.tsx | 247 ----------------- .../fragments/companyFieldsFragment.ts | 2 + .../getCompaniesOptimisticEffectDefinition.ts | 6 +- .../companies/graphql/queries/getCompanies.ts | 3 + .../getPeopleOptimisticEffectDefinition.ts | 6 +- .../people/graphql/queries/getPeople.ts | 3 + .../ui/dropdown/hooks/useDropdownButton.ts | 1 - front/src/testing/mock-data/users.ts | 22 ++ 10 files changed, 100 insertions(+), 464 deletions(-) delete mode 100644 front/src/modules/command-menu/components/__stories__/WrapperCommandMenu.tsx diff --git a/front/src/modules/apollo/optimistic-effect/hooks/useOptimisticEffect.ts b/front/src/modules/apollo/optimistic-effect/hooks/useOptimisticEffect.ts index 009831a81..d1bfaf274 100644 --- a/front/src/modules/apollo/optimistic-effect/hooks/useOptimisticEffect.ts +++ b/front/src/modules/apollo/optimistic-effect/hooks/useOptimisticEffect.ts @@ -6,12 +6,9 @@ import { } from '@apollo/client'; import { useRecoilCallback } from 'recoil'; -import { - GetCompaniesDocument, - GetCompaniesQuery, - GetPeopleDocument, - GetPeopleQuery, -} from '~/generated/graphql'; +import { GET_COMPANIES } from '@/companies/graphql/queries/getCompanies'; +import { GET_PEOPLE } from '@/people/graphql/queries/getPeople'; +import { GetCompaniesQuery, GetPeopleQuery } from '~/generated/graphql'; import { optimisticEffectState } from '../states/optimisticEffectState'; import { OptimisticEffectDefinition } from '../types/OptimisticEffectDefinition'; @@ -52,7 +49,7 @@ export const useOptimisticEffect = () => { return; } - if (query === GetPeopleDocument) { + if (query === GET_PEOPLE) { cache.writeQuery({ query, variables, @@ -66,7 +63,7 @@ export const useOptimisticEffect = () => { }); } - if (query === GetCompaniesDocument) { + if (query === GET_COMPANIES) { cache.writeQuery({ query, variables, diff --git a/front/src/modules/command-menu/components/__stories__/CommandMenu.stories.tsx b/front/src/modules/command-menu/components/__stories__/CommandMenu.stories.tsx index 501bbd782..b767bf43a 100644 --- a/front/src/modules/command-menu/components/__stories__/CommandMenu.stories.tsx +++ b/front/src/modules/command-menu/components/__stories__/CommandMenu.stories.tsx @@ -1,185 +1,62 @@ -import { MemoryRouter } from 'react-router-dom'; +import { useEffect } from 'react'; import { expect } from '@storybook/jest'; import type { Meta, StoryObj } from '@storybook/react'; -import { fireEvent, userEvent, within } from '@storybook/testing-library'; +import { userEvent, within } from '@storybook/testing-library'; -import { ComponentDecorator } from '~/testing/decorators/ComponentDecorator'; +import { useCommandMenu } from '@/command-menu/hooks/useCommandMenu'; +import { CommandType } from '@/command-menu/types/Command'; +import { IconCheckbox, IconNotes } from '@/ui/icon'; +import { ComponentWithRouterDecorator } from '~/testing/decorators/ComponentWithRouterDecorator'; +import { graphqlMocks } from '~/testing/graphqlMocks'; import { sleep } from '~/testing/sleep'; -import { WrapperCommandMenu } from './WrapperCommandMenu'; +import { CommandMenu } from '../CommandMenu'; -enum CommandType { - Navigate = 'Navigate', - Create = 'Create', -} - -const meta: Meta = { - title: 'Modules/CommandMenu/WrapperCommandMenu', - component: () => ( - - ), +const meta: Meta = { + title: 'Modules/CommandMenu/CommandMenu', + component: CommandMenu, decorators: [ - (Story) => ( - - - - ), - ComponentDecorator, + ComponentWithRouterDecorator, + (Story) => { + const { addToCommandMenu, setToIntitialCommandMenu, openCommandMenu } = + useCommandMenu(); + + useEffect(() => { + setToIntitialCommandMenu(); + addToCommandMenu([ + { + to: '', + label: 'Create Task', + type: CommandType.Create, + Icon: IconCheckbox, + onCommandClick: () => console.log('create task click'), + }, + { + to: '', + label: 'Create Note', + type: CommandType.Create, + Icon: IconNotes, + onCommandClick: () => console.log('create note click'), + }, + ]); + openCommandMenu(); + }, [addToCommandMenu, setToIntitialCommandMenu, openCommandMenu]); + + return ; + }, ], + parameters: { + msw: graphqlMocks, + }, }; export default meta; -type Story = StoryObj; +type Story = StoryObj; export const DefaultWithoutSearch: Story = { - play: async ({ canvasElement }) => { - fireEvent.keyDown(canvasElement, { - key: 'k', - code: 'KeyK', - metaKey: true, - }); - await sleep(50); + play: async () => { const canvas = within(document.body); - const searchInput = await canvas.findByPlaceholderText('Search'); - await sleep(10); - await userEvent.type(searchInput, ''); + expect(await canvas.findByText('Create Task')).toBeInTheDocument(); expect(await canvas.findByText('Go to People')).toBeInTheDocument(); expect(await canvas.findByText('Go to Companies')).toBeInTheDocument(); @@ -190,66 +67,42 @@ export const DefaultWithoutSearch: Story = { }; export const MatchingPersonCompanyActivityCreateNavigate: Story = { - play: async ({ canvasElement }) => { - fireEvent.keyDown(canvasElement, { - key: 'k', - code: 'KeyK', - metaKey: true, - }); - await sleep(50); + play: async () => { const canvas = within(document.body); const searchInput = await canvas.findByPlaceholderText('Search'); await sleep(10); - await userEvent.type(searchInput, 'a'); - expect(await canvas.findByText('Isabella Scott')).toBeInTheDocument(); + await userEvent.type(searchInput, 'n'); + expect(await canvas.findByText('Alexandre Prot')).toBeInTheDocument(); expect(await canvas.findByText('Airbnb')).toBeInTheDocument(); - expect(await canvas.findByText('Buyout Proposal')).toBeInTheDocument(); - expect(await canvas.findByText('Create Task')).toBeInTheDocument(); - expect(await canvas.findByText('Go to Tasks')).toBeInTheDocument(); + expect(await canvas.findByText('My very first note')).toBeInTheDocument(); + expect(await canvas.findByText('Create Note')).toBeInTheDocument(); + expect(await canvas.findByText('Go to Companies')).toBeInTheDocument(); }, }; export const OnlyMatchingCreateAndNavigate: Story = { - play: async ({ canvasElement }) => { - fireEvent.keyDown(canvasElement, { - key: 'k', - code: 'KeyK', - metaKey: true, - }); - await sleep(50); + play: async () => { const canvas = within(document.body); const searchInput = await canvas.findByPlaceholderText('Search'); await sleep(10); - await userEvent.type(searchInput, 'tas'); + await userEvent.type(searchInput, 'ta'); expect(await canvas.findByText('Create Task')).toBeInTheDocument(); expect(await canvas.findByText('Go to Tasks')).toBeInTheDocument(); }, }; export const AtleastMatchingOnePerson: Story = { - play: async ({ canvasElement }) => { - fireEvent.keyDown(canvasElement, { - key: 'k', - code: 'KeyK', - metaKey: true, - }); - await sleep(50); + play: async () => { const canvas = within(document.body); const searchInput = await canvas.findByPlaceholderText('Search'); await sleep(10); - await userEvent.type(searchInput, 'sy'); - expect(await canvas.findByText('Sylvie Palmer')).toBeInTheDocument(); + await userEvent.type(searchInput, 'alex'); + expect(await canvas.findByText('Alexandre Prot')).toBeInTheDocument(); }, }; export const NotMatchingAnything: Story = { - play: async ({ canvasElement }) => { - fireEvent.keyDown(canvasElement, { - key: 'k', - code: 'KeyK', - metaKey: true, - }); - await sleep(50); + play: async () => { const canvas = within(document.body); const searchInput = await canvas.findByPlaceholderText('Search'); await sleep(10); diff --git a/front/src/modules/command-menu/components/__stories__/WrapperCommandMenu.tsx b/front/src/modules/command-menu/components/__stories__/WrapperCommandMenu.tsx deleted file mode 100644 index ccdea421c..000000000 --- a/front/src/modules/command-menu/components/__stories__/WrapperCommandMenu.tsx +++ /dev/null @@ -1,247 +0,0 @@ -import { useState } from 'react'; -import { useRecoilValue } from 'recoil'; - -import { useOpenActivityRightDrawer } from '@/activities/hooks/useOpenActivityRightDrawer'; -import { IconNotes } from '@/ui/icon'; -import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys'; -import { AppHotkeyScope } from '@/ui/utilities/hotkey/types/AppHotkeyScope'; -import { Avatar } from '@/users/components/Avatar'; -import { getLogoUrlFromDomainName } from '~/utils'; - -import { useCommandMenu } from '../../hooks/useCommandMenu'; -import { isCommandMenuOpenedState } from '../../states/isCommandMenuOpenedState'; -import { Command, CommandType } from '../../types/Command'; -import { CommandMenuItem } from '../CommandMenuItem'; -import { - StyledDialog, - StyledEmpty, - StyledGroup, - StyledInput, - StyledList, -} from '../CommandMenuStyles'; - -export const WrapperCommandMenu = ({ - values, - people, - companies, - activities, -}: { - values: Command[]; - people: Array<{ - __typename?: 'Person'; - id: string; - phone?: string | null; - email?: string | null; - city?: string | null; - firstName?: string | null; - lastName?: string | null; - displayName: string; - avatarUrl?: string | null; - createdAt: string; - }>; - companies: Array<{ - __typename?: 'Company'; - address: string; - createdAt: string; - domainName: string; - employees?: number | null; - linkedinUrl?: string | null; - xUrl?: string | null; - annualRecurringRevenue?: number | null; - idealCustomerProfile: boolean; - id: string; - name: string; - _activityCount: number; - accountOwner?: { - __typename?: 'User'; - id: string; - email: string; - displayName: string; - avatarUrl?: string | null; - } | null; - }>; - activities: Array<{ - __typename?: 'Activity'; - id: string; - title?: string | null; - body?: string | null; - }>; -}) => { - const { openCommandMenu, closeCommandMenu } = useCommandMenu(); - const openActivityRightDrawer = useOpenActivityRightDrawer(); - const isCommandMenuOpened = useRecoilValue(isCommandMenuOpenedState); - const [search, setSearch] = useState(''); - const commandMenuCommands = values; - - const peopleData = people.filter((i) => - search.length > 0 - ? (i.firstName - ? i.firstName?.toLowerCase().includes(search.toLowerCase()) - : false) || - (i.lastName - ? i.lastName?.toLowerCase().includes(search.toLowerCase()) - : false) - : false, - ); - - const companyData = companies.filter((i) => - search.length > 0 - ? i.name - ? i.name?.toLowerCase().includes(search.toLowerCase()) - : false - : false, - ); - - const activityData = activities.filter((i) => - search.length > 0 - ? (i.title - ? i.title?.toLowerCase().includes(search.toLowerCase()) - : false) || - (i.body ? i.body?.toLowerCase().includes(search.toLowerCase()) : false) - : false, - ); - - useScopedHotkeys( - 'ctrl+k,meta+k', - () => { - openCommandMenu(); - }, - AppHotkeyScope.CommandMenu, - [openCommandMenu], - ); - - const checkInShortcuts = (cmd: Command, search: string) => { - if (cmd.shortcuts && cmd.shortcuts.length > 0) { - return cmd.shortcuts - .join('') - .toLowerCase() - .includes(search.toLowerCase()); - } - return false; - }; - - const checkInLabels = (cmd: Command, search: string) => { - if (cmd.label) { - return cmd.label.toLowerCase().includes(search.toLowerCase()); - } - return false; - }; - - const matchingNavigateCommand = commandMenuCommands.filter( - (cmd) => - (search.length > 0 - ? checkInShortcuts(cmd, search) || checkInLabels(cmd, search) - : true) && cmd.type === CommandType.Navigate, - ); - - const matchingCreateCommand = commandMenuCommands.filter( - (cmd) => - (search.length > 0 - ? checkInShortcuts(cmd, search) || checkInLabels(cmd, search) - : true) && cmd.type === CommandType.Create, - ); - - return ( - { - if (!opened) { - closeCommandMenu(); - } - }} - shouldFilter={false} - label="Global Command Menu" - > - - - {matchingCreateCommand.length < 1 && - matchingNavigateCommand.length < 1 && - peopleData.length < 1 && - companyData.length < 1 && - activityData.length < 1 && ( - No results found. - )} - {matchingCreateCommand.length > 0 && ( - - {matchingCreateCommand.map((cmd) => ( - - ))} - - )} - {matchingNavigateCommand.length > 0 && ( - - {matchingNavigateCommand.map((cmd) => ( - - ))} - - )} - {peopleData.length > 0 && ( - - {peopleData.map((person) => ( - ( - - )} - /> - ))} - - )} - {companyData.length > 0 && ( - - {companyData.map((company) => ( - ( - - )} - /> - ))} - - )} - {activityData.length > 0 && ( - - {activityData.map((activity) => ( - openActivityRightDrawer(activity.id)} - /> - ))} - - )} - - - ); -}; diff --git a/front/src/modules/companies/graphql/fragments/companyFieldsFragment.ts b/front/src/modules/companies/graphql/fragments/companyFieldsFragment.ts index a100c16d2..e0dcc3048 100644 --- a/front/src/modules/companies/graphql/fragments/companyFieldsFragment.ts +++ b/front/src/modules/companies/graphql/fragments/companyFieldsFragment.ts @@ -28,6 +28,8 @@ export const BASE_ACCOUNT_OWNER_FRAGMENT = gql` `; export const COMPANY_FIELDS_FRAGMENT = gql` + ${BASE_COMPANY_FIELDS_FRAGMENT} + ${BASE_ACCOUNT_OWNER_FRAGMENT} fragment companyFieldsFragment on Company { accountOwner { ...baseAccountOwnerFragment diff --git a/front/src/modules/companies/graphql/optimistic-effect-definitions/getCompaniesOptimisticEffectDefinition.ts b/front/src/modules/companies/graphql/optimistic-effect-definitions/getCompaniesOptimisticEffectDefinition.ts index 03092d52c..c30f4d4e4 100644 --- a/front/src/modules/companies/graphql/optimistic-effect-definitions/getCompaniesOptimisticEffectDefinition.ts +++ b/front/src/modules/companies/graphql/optimistic-effect-definitions/getCompaniesOptimisticEffectDefinition.ts @@ -1,9 +1,11 @@ -import { Company, GetCompaniesDocument } from '~/generated/graphql'; +import { Company } from '~/generated/graphql'; + +import { GET_COMPANIES } from '../queries/getCompanies'; export const getCompaniesOptimisticEffectDefinition = { key: 'generic-entity-table-data-companies', typename: 'Company', - query: GetCompaniesDocument, + query: GET_COMPANIES, resolver: ({ currentData, newData, diff --git a/front/src/modules/companies/graphql/queries/getCompanies.ts b/front/src/modules/companies/graphql/queries/getCompanies.ts index 9146a188e..50c42beb4 100644 --- a/front/src/modules/companies/graphql/queries/getCompanies.ts +++ b/front/src/modules/companies/graphql/queries/getCompanies.ts @@ -1,6 +1,9 @@ import { gql } from '@apollo/client'; +import { COMPANY_FIELDS_FRAGMENT } from '../fragments/companyFieldsFragment'; + export const GET_COMPANIES = gql` + ${COMPANY_FIELDS_FRAGMENT} query GetCompanies( $orderBy: [CompanyOrderByWithRelationInput!] $where: CompanyWhereInput diff --git a/front/src/modules/people/graphql/optimistic-effect-definitions/getPeopleOptimisticEffectDefinition.ts b/front/src/modules/people/graphql/optimistic-effect-definitions/getPeopleOptimisticEffectDefinition.ts index 32fa570a7..3a07458e2 100644 --- a/front/src/modules/people/graphql/optimistic-effect-definitions/getPeopleOptimisticEffectDefinition.ts +++ b/front/src/modules/people/graphql/optimistic-effect-definitions/getPeopleOptimisticEffectDefinition.ts @@ -1,9 +1,11 @@ -import { GetPeopleDocument, Person } from '~/generated/graphql'; +import { Person } from '~/generated/graphql'; + +import { GET_PEOPLE } from '../queries/getPeople'; export const getPeopleOptimisticEffectDefinition = { key: 'generic-entity-table-data-people', typename: 'Person', - query: GetPeopleDocument, + query: GET_PEOPLE, resolver: ({ currentData, newData, diff --git a/front/src/modules/people/graphql/queries/getPeople.ts b/front/src/modules/people/graphql/queries/getPeople.ts index 2cad80675..fd00ae789 100644 --- a/front/src/modules/people/graphql/queries/getPeople.ts +++ b/front/src/modules/people/graphql/queries/getPeople.ts @@ -1,6 +1,9 @@ import { gql } from '@apollo/client'; +import { PERSON_FIELDS_FRAGMENT } from '../fragments/personFieldsFragment'; + export const GET_PEOPLE = gql` + ${PERSON_FIELDS_FRAGMENT} query GetPeople( $orderBy: [PersonOrderByWithRelationInput!] $where: PersonWhereInput diff --git a/front/src/modules/ui/dropdown/hooks/useDropdownButton.ts b/front/src/modules/ui/dropdown/hooks/useDropdownButton.ts index eff1a569d..a1e373401 100644 --- a/front/src/modules/ui/dropdown/hooks/useDropdownButton.ts +++ b/front/src/modules/ui/dropdown/hooks/useDropdownButton.ts @@ -25,7 +25,6 @@ export const useDropdownButton = ({ dropdownId }: { dropdownId: string }) => { ); const closeDropdownButton = () => { - console.log('closeDropdownButton', dropdownId); goBackToPreviousHotkeyScope(); setIsDropdownButtonOpen(false); }; diff --git a/front/src/testing/mock-data/users.ts b/front/src/testing/mock-data/users.ts index 0b9ea41b6..1c0a33833 100644 --- a/front/src/testing/mock-data/users.ts +++ b/front/src/testing/mock-data/users.ts @@ -1,5 +1,7 @@ import { + Activity, ColorScheme, + Company, User, UserSettings, Workspace, @@ -30,6 +32,10 @@ type MockedUser = Pick< UserSettings, 'id' | 'colorScheme' | 'locale' | '__typename' >; + assignedActivities: Array; + authoredActivities: Array; + companies: Array; + comments: Array; }; settings: Pick; }; @@ -68,6 +74,10 @@ export const mockedUsersData: Array = [ locale: 'en', colorScheme: ColorScheme.System, }, + assignedActivities: [], + authoredActivities: [], + companies: [], + comments: [], }, settings: { id: '7dfbc3f7-6e5e-4128-957e-8d86808cde9y', @@ -103,6 +113,10 @@ export const mockedUsersData: Array = [ locale: 'en', colorScheme: ColorScheme.System, }, + assignedActivities: [], + authoredActivities: [], + companies: [], + comments: [], }, settings: { id: '7dfbc3f7-6e5e-4128-957e-8d86808cdt7a', @@ -142,6 +156,10 @@ export const mockedOnboardingUsersData: Array = [ locale: 'en', colorScheme: ColorScheme.System, }, + assignedActivities: [], + authoredActivities: [], + companies: [], + comments: [], }, settings: { id: '7dfbc3f7-6e5e-4128-957e-8d86808cde9y', @@ -178,6 +196,10 @@ export const mockedOnboardingUsersData: Array = [ locale: 'en', colorScheme: ColorScheme.System, }, + assignedActivities: [], + authoredActivities: [], + companies: [], + comments: [], }, settings: { id: '7dfbc3f7-6e5e-4128-957e-8d86808cde9y',