From 3d13092aedc1e63fc0a312f3c9c79c7723f59797 Mon Sep 17 00:00:00 2001 From: Teddy Date: Fri, 15 Nov 2024 17:13:29 +0100 Subject: [PATCH] test(@leav/ui): add tests on explorer --- libs/ui/src/_gqlTypes/index.ts | 3 +- libs/ui/src/components/Explorer/DataView.tsx | 4 +- .../src/components/Explorer/Explorer.test.tsx | 249 ++++++++++++++++++ libs/ui/src/components/Explorer/Explorer.tsx | 4 +- .../_queries/libraryDataQuery.graphql | 1 + .../src/components/Explorer/useEditAction.tsx | 2 +- 6 files changed, 257 insertions(+), 6 deletions(-) create mode 100644 libs/ui/src/components/Explorer/Explorer.test.tsx diff --git a/libs/ui/src/_gqlTypes/index.ts b/libs/ui/src/_gqlTypes/index.ts index dade69ce0..5285b95b3 100644 --- a/libs/ui/src/_gqlTypes/index.ts +++ b/libs/ui/src/_gqlTypes/index.ts @@ -1356,7 +1356,7 @@ export type ExplorerLibraryDataQueryVariables = Exact<{ }>; -export type ExplorerLibraryDataQuery = { libraries?: { list: Array<{ label?: any | null }> } | null }; +export type ExplorerLibraryDataQuery = { libraries?: { list: Array<{ id: string, label?: any | null }> } | null }; export const RecordIdentityFragmentDoc = gql` fragment RecordIdentity on Record { @@ -4013,6 +4013,7 @@ export const ExplorerLibraryDataDocument = gql` query ExplorerLibraryData($libraryId: ID!) { libraries(filters: {id: [$libraryId]}) { list { + id label } } diff --git a/libs/ui/src/components/Explorer/DataView.tsx b/libs/ui/src/components/Explorer/DataView.tsx index 961386961..2990e0fd1 100644 --- a/libs/ui/src/components/Explorer/DataView.tsx +++ b/libs/ui/src/components/Explorer/DataView.tsx @@ -47,8 +47,8 @@ export const DataView: FunctionComponent = ({ return isLessThanFourActions ? ( - {actions.map(({label, icon, isDanger, callback}) => ( - + {actions.map(({label, icon, isDanger, callback}, index) => ( + {label} ))} diff --git a/libs/ui/src/components/Explorer/Explorer.test.tsx b/libs/ui/src/components/Explorer/Explorer.test.tsx new file mode 100644 index 000000000..13ac55d98 --- /dev/null +++ b/libs/ui/src/components/Explorer/Explorer.test.tsx @@ -0,0 +1,249 @@ +// Copyright LEAV Solutions 2017 until 2023/11/05, Copyright Aristid from 2023/11/06 +// This file is released under LGPL V3 +// License text available at https://www.gnu.org/licenses/lgpl-3.0.txt +import {render, screen, within} from '_ui/_tests/testUtils'; +import {Explorer} from '_ui/index'; +import * as gqlTypes from '_ui/_gqlTypes'; +import {Mockify} from '@leav/utils'; +import userEvent from '@testing-library/user-event'; +import {Fa500Px, FaAccessibleIcon, FaBeer, FaJs, FaXbox} from 'react-icons/fa'; +import {mockRecord} from '_ui/__mocks__/common/record'; +import {IItemAction} from './_types'; + +jest.mock('_ui/components/RecordEdition/EditRecordModal', () => ({ + EditRecordModal: () =>
EditRecordModal
+})); + +describe('Explorer', () => { + const mockExplorerQueryResult: Mockify = { + loading: false, + called: true, + data: { + records: { + list: [ + { + id: '613982168', + whoAmI: { + id: '613982168', + label: 'Halloween 2025', + subLabel: 'Du mercredi 6 novembre 2024 au lundi 9 décembre 2024', + color: null, + library: { + id: 'campaigns', + label: { + en: 'Campaigns', + fr: 'Campagnes' + } + }, + preview: null + }, + properties: [ + { + attributeId: 'id', + values: [ + { + attribute: { + type: 'simple' + }, + valuePayload: '613982168' + } + ] + } + ] + }, + { + id: '612694174', + whoAmI: { + id: '612694174', + label: 'Foire aux vins 2024 - semaine 1', + subLabel: 'Du mercredi 30 octobre 2024 au lundi 25 novembre 2024', + color: null, + library: { + id: 'campaigns', + label: { + en: 'Campaigns', + fr: 'Campagnes' + } + }, + preview: null + }, + properties: [ + { + attributeId: 'id', + values: [ + { + attribute: { + type: 'simple' + }, + valuePayload: '612694174' + } + ] + } + ] + } + ] + } + } + }; + + const mockLibraryDataQueryResult: Mockify = { + loading: false, + called: true, + data: { + libraries: { + list: [ + { + id: 'campaigns', + label: { + en: 'Campaigns', + fr: 'Campagnes' + } + } + ] + } + } + }; + + beforeAll(() => { + jest.spyOn(gqlTypes, 'useExplorerQuery').mockImplementation( + () => mockExplorerQueryResult as gqlTypes.ExplorerQueryResult + ); + + jest.spyOn(gqlTypes, 'useExplorerLibraryDataQuery').mockImplementation( + () => mockLibraryDataQueryResult as gqlTypes.ExplorerLibraryDataQueryResult + ); + }); + + let user; + beforeEach(() => { + user = userEvent.setup(); + }); + + test('Should display library label as title', () => { + render(); + + expect(screen.getByText('Campagnes')).toBeInTheDocument(); + }); + + test('Should display custom title', () => { + render(); + + expect(screen.getByText("Here's my explorer!")).toBeInTheDocument(); + }); + + test('Should display the list of records in a table', async () => { + render(); + + expect(screen.getByRole('table')).toBeVisible(); + expect(screen.getAllByRole('row')).toHaveLength(3); // 1 header row + 2 records + expect(screen.getByText('Halloween 2025')).toBeInTheDocument(); + expect(screen.getByText('Foire aux vins 2024 - semaine 1')).toBeInTheDocument(); + }); + + test('Should be able to deactivate a record with default actions', async () => { + const mockDeactivateMutation = jest.fn().mockReturnValue({ + data: { + deactivateRecords: [ + { + id: 42, + whoAmI: mockRecord + } + ] + } + }); + + jest.spyOn(gqlTypes, 'useDeactivateRecordsMutation').mockImplementation(() => [ + mockDeactivateMutation, + {loading: false, called: false, client: {} as any, reset: jest.fn()} + ]); + + render(); + + const firstRecordRow = screen.getAllByRole('row')[1]; + await user.click(within(firstRecordRow).getByRole('button', {name: /deactivate/})); + + expect(await screen.findByText('records_deactivation.confirm_one')).toBeVisible(); + await user.click(screen.getByText(/submit/)); + + expect(mockDeactivateMutation).toHaveBeenCalled(); + }); + + test('Should be able to edit a record with default actions', async () => { + render(); + + const firstRecordRow = screen.getAllByRole('row')[1]; + user.click(within(firstRecordRow).getByRole('button', {name: /edit-item/})); + expect(await screen.findByText('EditRecordModal')).toBeVisible(); + }); + + test('Should display the list of records with custom actions', async () => { + const customActionCb = jest.fn(); + const customActions: IItemAction[] = [ + { + icon: , + label: 'Custom action', + callback: customActionCb + } + ]; + render(); + + const firstRecordRow = screen.getAllByRole('row')[1]; + + await user.click(within(firstRecordRow).queryByRole('button', {name: /Custom action/})); + + expect(customActionCb).toHaveBeenCalled(); + }); + + test('Should display the list of records with a lot of custom actions', async () => { + const customActions: IItemAction[] = [ + { + label: 'Test 1', + icon: , + callback: jest.fn() + }, + { + label: 'Test 2', + icon: , + callback: jest.fn() + }, + { + label: 'Test 3', + icon: , + callback: jest.fn() + }, + { + label: 'Test 4', + icon: , + callback: jest.fn() + } + ]; + render(); + + const firstRecordRow = screen.getAllByRole('row')[1]; + await user.hover(within(firstRecordRow).getByRole('button', {name: /more-actions/})); + + expect(await screen.findByRole('menuitem', {name: /Test 1/})).toBeVisible(); + expect(await screen.findByRole('menuitem', {name: /Test 2/})).toBeVisible(); + expect(await screen.findByRole('menuitem', {name: /Test 3/})).toBeVisible(); + expect(await screen.findByRole('menuitem', {name: /Test 4/})).toBeVisible(); + + await user.click(screen.getByRole('menuitem', {name: /Test 1/})); + + expect(customActions[0].callback).toHaveBeenCalled(); + }); + + test('Should display the list of records with no actions', () => { + render(); + + const firstRecordRow = screen.getAllByRole('row')[1]; + expect(within(firstRecordRow).queryByRole('button')).not.toBeInTheDocument(); + }); + + test('Should be able to create a new record', async () => { + render(); + + user.click(screen.getByRole('button', {name: /create/})); + + expect(await screen.findByText('EditRecordModal')).toBeVisible(); + }); +}); diff --git a/libs/ui/src/components/Explorer/Explorer.tsx b/libs/ui/src/components/Explorer/Explorer.tsx index bb0affc9f..3f33ae481 100644 --- a/libs/ui/src/components/Explorer/Explorer.tsx +++ b/libs/ui/src/components/Explorer/Explorer.tsx @@ -15,7 +15,7 @@ import {ExplorerTitle} from './ExplorerTitle'; interface IExplorerProps { library: string; - itemActions: IItemAction[]; + itemActions?: IItemAction[]; title?: string; defaultActionsForItem?: | [] @@ -81,7 +81,7 @@ export const Explorer: FunctionComponent = ({ )} diff --git a/libs/ui/src/components/Explorer/_queries/libraryDataQuery.graphql b/libs/ui/src/components/Explorer/_queries/libraryDataQuery.graphql index 9e9fbfef3..d9212fc86 100644 --- a/libs/ui/src/components/Explorer/_queries/libraryDataQuery.graphql +++ b/libs/ui/src/components/Explorer/_queries/libraryDataQuery.graphql @@ -1,6 +1,7 @@ query ExplorerLibraryData($libraryId: ID!) { libraries(filters: {id: [$libraryId]} ) { list { + id label } } diff --git a/libs/ui/src/components/Explorer/useEditAction.tsx b/libs/ui/src/components/Explorer/useEditAction.tsx index 82eb82580..3b36a3a86 100644 --- a/libs/ui/src/components/Explorer/useEditAction.tsx +++ b/libs/ui/src/components/Explorer/useEditAction.tsx @@ -4,7 +4,7 @@ import {useState} from 'react'; import {FaPen} from 'react-icons/fa'; import {useSharedTranslation} from '_ui/hooks/useSharedTranslation'; -import {EditRecordModal} from '_ui/components'; +import {EditRecordModal} from '_ui/components/RecordEdition/EditRecordModal'; import {RecordFilterCondition, useExplorerLazyQuery} from '_ui/_gqlTypes'; import {ActionHook, IItemAction, IItemData} from './_types';