From a505f8c1279c0f0ab2e19baefbab75c36ab76518 Mon Sep 17 00:00:00 2001 From: XnpioChV Date: Thu, 26 Sep 2024 17:25:40 -0500 Subject: [PATCH] refactor: Tests of ContentTagsDrawer --- .../ContentTagsDrawer.test.jsx | 749 +++--------------- src/content-tags-drawer/data/api.mocks.ts | 378 +++++++++ 2 files changed, 483 insertions(+), 644 deletions(-) create mode 100644 src/content-tags-drawer/data/api.mocks.ts diff --git a/src/content-tags-drawer/ContentTagsDrawer.test.jsx b/src/content-tags-drawer/ContentTagsDrawer.test.jsx index 7e81370e3e..82899075bf 100644 --- a/src/content-tags-drawer/ContentTagsDrawer.test.jsx +++ b/src/content-tags-drawer/ContentTagsDrawer.test.jsx @@ -1,613 +1,120 @@ -import React from 'react'; -import { IntlProvider } from '@edx/frontend-platform/i18n'; -import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; import { act, fireEvent, + initializeMocks, render, waitFor, screen, within, -} from '@testing-library/react'; -import { useParams } from 'react-router-dom'; - +} from '../testUtils'; import ContentTagsDrawer from './ContentTagsDrawer'; -import { - useContentTaxonomyTagsData, - useContentData, - useTaxonomyTagsData, - useContentTaxonomyTagsUpdater, -} from './data/apiHooks'; -import { getTaxonomyListData } from '../taxonomy/data/api'; import messages from './messages'; import { ContentTagsDrawerSheetContext } from './common/context'; -import { languageExportId } from './utils'; - -const contentId = 'block-v1:SampleTaxonomyOrg1+STC1+2023_1+type@vertical+block@7f47fe2dbcaf47c5a071671c741fe1ab'; +import { + mockContentData, + mockContentTaxonomyTagsData, + mockTaxonomyListData, + mockTaxonomyTagsData, +} from './data/api.mocks'; +import { getContentTaxonomyTagsApiUrl } from './data/api'; + +const path = '/content/:contentId/*'; const mockOnClose = jest.fn(); -const mockMutate = jest.fn(); const mockSetBlockingSheet = jest.fn(); const mockNavigate = jest.fn(); +mockContentTaxonomyTagsData.applyMock(); +mockTaxonomyListData.applyMock(); +mockTaxonomyTagsData.applyMock(); +mockContentData.applyMock(); + +const { + stagedTagsId, + otherTagsId, + languageWithTagsId, + languageWithoutTagsId, + largeTagsId, + emptyTagsId, +} = mockContentTaxonomyTagsData; jest.mock('react-router-dom', () => ({ ...jest.requireActual('react-router-dom'), - useParams: jest.fn(() => ({ - contentId, - })), useNavigate: () => mockNavigate, })); -// FIXME: replace these mocks with API mocks -jest.mock('./data/apiHooks', () => ({ - useContentTaxonomyTagsData: jest.fn(() => {}), - useContentData: jest.fn(() => ({ - isSuccess: false, - data: {}, - })), - useContentTaxonomyTagsUpdater: jest.fn(() => ({ - isError: false, - mutate: mockMutate, - })), - useTaxonomyTagsData: jest.fn(() => ({ - hasMorePages: false, - tagPages: { - isLoading: true, - isError: false, - canAddTag: false, - data: [], - }, - })), -})); - -jest.mock('../taxonomy/data/api', () => ({ - // By default, the mock taxonomy list will never load (promise never resolves): - getTaxonomyListData: jest.fn(), -})); - -const queryClient = new QueryClient(); - -const RootWrapper = (params) => ( - - - - - - - +const renderDrawer = (contentId, drawerParams = {}) => ( + render( + + + , + { path, params: { contentId } }, + ) ); describe('', () => { beforeEach(async () => { - jest.clearAllMocks(); - await queryClient.resetQueries(); - // By default, we mock the API call with a promise that never resolves. - // You can override this in specific test. - getTaxonomyListData.mockReturnValue(new Promise(() => {})); - useContentTaxonomyTagsUpdater.mockReturnValue({ - isError: false, - mutate: mockMutate, - }); - useParams.mockReturnValue({ - contentId, - }); + initializeMocks(); }); - const setupMockDataForStagedTagsTesting = () => { - useContentTaxonomyTagsData.mockReturnValue({ - isSuccess: true, - data: { - taxonomies: [ - { - name: 'Taxonomy 1', - taxonomyId: 123, - canTagObject: true, - tags: [ - { - value: 'Tag 1', - lineage: ['Tag 1'], - canDeleteObjecttag: true, - }, - { - value: 'Tag 2', - lineage: ['Tag 2'], - canDeleteObjecttag: true, - }, - ], - }, - ], - }, - }); - getTaxonomyListData.mockResolvedValue({ - results: [ - { - id: 123, - name: 'Taxonomy 1', - description: 'This is a description 1', - canTagObject: true, - }, - ], - }); - - useTaxonomyTagsData.mockReturnValue({ - hasMorePages: false, - canAddTag: false, - tagPages: { - isLoading: false, - isError: false, - data: [{ - value: 'Tag 1', - externalId: null, - childCount: 0, - depth: 0, - parentValue: null, - id: 12345, - subTagsUrl: null, - canChangeTag: false, - canDeleteTag: false, - }, { - value: 'Tag 2', - externalId: null, - childCount: 0, - depth: 0, - parentValue: null, - id: 12346, - subTagsUrl: null, - canChangeTag: false, - canDeleteTag: false, - }, { - value: 'Tag 3', - externalId: null, - childCount: 0, - depth: 0, - parentValue: null, - id: 12347, - subTagsUrl: null, - canChangeTag: false, - canDeleteTag: false, - }], - }, - }); - }; - - const setupMockDataWithOtherTagsTestings = () => { - useContentTaxonomyTagsData.mockReturnValue({ - isSuccess: true, - data: { - taxonomies: [ - { - name: 'Taxonomy 1', - taxonomyId: 123, - canTagObject: true, - tags: [ - { - value: 'Tag 1', - lineage: ['Tag 1'], - canDeleteObjecttag: true, - }, - { - value: 'Tag 2', - lineage: ['Tag 2'], - canDeleteObjecttag: true, - }, - ], - }, - { - name: 'Taxonomy 2', - taxonomyId: 1234, - canTagObject: false, - tags: [ - { - value: 'Tag 3', - lineage: ['Tag 3'], - canDeleteObjecttag: true, - }, - { - value: 'Tag 4', - lineage: ['Tag 4'], - canDeleteObjecttag: true, - }, - ], - }, - ], - }, - }); - getTaxonomyListData.mockResolvedValue({ - results: [ - { - id: 123, - name: 'Taxonomy 1', - description: 'This is a description 1', - canTagObject: true, - }, - ], - }); - - useTaxonomyTagsData.mockReturnValue({ - hasMorePages: false, - canAddTag: false, - tagPages: { - isLoading: false, - isError: false, - data: [{ - value: 'Tag 1', - externalId: null, - childCount: 0, - depth: 0, - parentValue: null, - id: 12345, - subTagsUrl: null, - canChangeTag: false, - canDeleteTag: false, - }, { - value: 'Tag 2', - externalId: null, - childCount: 0, - depth: 0, - parentValue: null, - id: 12346, - subTagsUrl: null, - canChangeTag: false, - canDeleteTag: false, - }, { - value: 'Tag 3', - externalId: null, - childCount: 0, - depth: 0, - parentValue: null, - id: 12347, - subTagsUrl: null, - canChangeTag: false, - canDeleteTag: false, - }], - }, - }); - }; - - const setupMockDataLanguageTaxonomyTestings = (hasTags) => { - useContentTaxonomyTagsData.mockReturnValue({ - isSuccess: true, - data: { - taxonomies: [ - { - name: 'Languages', - taxonomyId: 123, - exportId: languageExportId, - canTagObject: true, - tags: hasTags ? [ - { - value: 'Tag 1', - lineage: ['Tag 1'], - canDeleteObjecttag: true, - }, - ] : [], - }, - { - name: 'Taxonomy 1', - taxonomyId: 1234, - canTagObject: true, - tags: [ - { - value: 'Tag 1', - lineage: ['Tag 1'], - canDeleteObjecttag: true, - }, - { - value: 'Tag 2', - lineage: ['Tag 2'], - canDeleteObjecttag: true, - }, - ], - }, - ], - }, - }); - getTaxonomyListData.mockResolvedValue({ - results: [ - { - id: 123, - name: 'Languages', - description: 'This is a description 1', - exportId: languageExportId, - canTagObject: true, - }, - { - id: 1234, - name: 'Taxonomy 1', - description: 'This is a description 2', - canTagObject: true, - }, - ], - }); - - useTaxonomyTagsData.mockReturnValue({ - hasMorePages: false, - canAddTag: false, - tagPages: { - isLoading: false, - isError: false, - data: [{ - value: 'Tag 1', - externalId: null, - childCount: 0, - depth: 0, - parentValue: null, - id: 12345, - subTagsUrl: null, - canChangeTag: false, - canDeleteTag: false, - }], - }, - }); - }; - - const setupLargeMockDataForStagedTagsTesting = () => { - useContentTaxonomyTagsData.mockReturnValue({ - isSuccess: true, - data: { - taxonomies: [ - { - name: 'Taxonomy 1', - taxonomyId: 123, - canTagObject: true, - tags: [ - { - value: 'Tag 1', - lineage: ['Tag 1'], - canDeleteObjecttag: true, - }, - { - value: 'Tag 2', - lineage: ['Tag 2'], - canDeleteObjecttag: true, - }, - ], - }, - { - name: 'Taxonomy 2', - taxonomyId: 124, - canTagObject: true, - tags: [ - { - value: 'Tag 1', - lineage: ['Tag 1'], - canDeleteObjecttag: true, - }, - ], - }, - { - name: 'Taxonomy 3', - taxonomyId: 125, - canTagObject: true, - tags: [ - { - value: 'Tag 1.1.1', - lineage: ['Tag 1', 'Tag 1.1', 'Tag 1.1.1'], - canDeleteObjecttag: true, - }, - ], - }, - { - name: '(B) Taxonomy 4', - taxonomyId: 126, - canTagObject: true, - tags: [], - }, - { - name: '(A) Taxonomy 5', - taxonomyId: 127, - canTagObject: true, - tags: [], - }, - ], - }, - }); - getTaxonomyListData.mockResolvedValue({ - results: [ - { - id: 123, - name: 'Taxonomy 1', - description: 'This is a description 1', - canTagObject: true, - }, - { - id: 124, - name: 'Taxonomy 2', - description: 'This is a description 2', - canTagObject: true, - }, - { - id: 125, - name: 'Taxonomy 3', - description: 'This is a description 3', - canTagObject: true, - }, - { - id: 127, - name: '(A) Taxonomy 5', - description: 'This is a description 5', - canTagObject: true, - }, - { - id: 126, - name: '(B) Taxonomy 4', - description: 'This is a description 4', - canTagObject: true, - }, - ], - }); - - useTaxonomyTagsData.mockReturnValue({ - hasMorePages: false, - canAddTag: false, - tagPages: { - isLoading: false, - isError: false, - data: [{ - value: 'Tag 1', - externalId: null, - childCount: 0, - depth: 0, - parentValue: null, - id: 12345, - subTagsUrl: null, - canChangeTag: false, - canDeleteTag: false, - }, { - value: 'Tag 2', - externalId: null, - childCount: 0, - depth: 0, - parentValue: null, - id: 12346, - subTagsUrl: null, - canChangeTag: false, - canDeleteTag: false, - }, { - value: 'Tag 3', - externalId: null, - childCount: 0, - depth: 0, - parentValue: null, - id: 12347, - subTagsUrl: null, - canChangeTag: false, - canDeleteTag: false, - }], - }, - }); - }; - - it('should render page and page title correctly', () => { - setupMockDataForStagedTagsTesting(); - const { getByText } = render(); - expect(getByText('Manage tags')).toBeInTheDocument(); + afterEach(() => { + jest.clearAllMocks(); }); - it('should throw error when contentId is undefined', () => { - useParams.mockReturnValue({ - contentId: undefined, - }); - expect(() => { - render(); - }).toThrow('Error: contentId cannot be null.'); + it('should render page and page title correctly', () => { + renderDrawer(stagedTagsId); + expect(screen.getByText('Manage tags')).toBeInTheDocument(); }); it('shows spinner before the content data query is complete', async () => { await act(async () => { - const { getAllByRole } = render(); - const spinner = getAllByRole('status')[0]; + renderDrawer(stagedTagsId); + const spinner = screen.getAllByRole('status')[0]; expect(spinner.textContent).toEqual('Loading'); // Uses }); }); it('shows spinner before the taxonomy tags query is complete', async () => { await act(async () => { - const { getAllByRole } = render(); - const spinner = getAllByRole('status')[1]; + renderDrawer(stagedTagsId); + const spinner = screen.getAllByRole('status')[1]; expect(spinner.textContent).toEqual('Loading...'); // Uses }); }); it('shows the content display name after the query is complete in drawer variant', async () => { - useContentData.mockReturnValue({ - isSuccess: true, - data: { - displayName: 'Unit 1', - }, - }); - render(); + renderDrawer('test'); + expect(await screen.findByText('Loading...')).toBeInTheDocument(); expect(await screen.findByText('Unit 1')).toBeInTheDocument(); expect(await screen.findByText('Manage tags')).toBeInTheDocument(); }); it('shows the content display name after the query is complete in component variant', async () => { - useContentData.mockReturnValue({ - isSuccess: true, - data: { - displayName: 'Unit 1', - }, - }); - render(); + renderDrawer('test', { variant: 'component' }); expect(await screen.findByText('Loading...')).toBeInTheDocument(); expect(screen.queryByText('Unit 1')).not.toBeInTheDocument(); expect(screen.queryByText('Manage tags')).not.toBeInTheDocument(); }); it('shows content using params', async () => { - useContentData.mockReturnValue({ - isSuccess: true, - data: { - displayName: 'Unit 1', - }, - }); - render(); + renderDrawer(undefined, { id: 'test' }); + expect(await screen.findByText('Loading...')).toBeInTheDocument(); expect(screen.getByText('Unit 1')).toBeInTheDocument(); + expect(screen.queryByText('Manage tags')).toBeInTheDocument(); }); it('shows the taxonomies data including tag numbers after the query is complete', async () => { - useContentTaxonomyTagsData.mockReturnValue({ - isSuccess: true, - data: { - taxonomies: [ - { - name: 'Taxonomy 1', - taxonomyId: 123, - canTagObject: true, - tags: [ - { - value: 'Tag 1', - lineage: ['Tag 1'], - canDeleteObjecttag: true, - }, - { - value: 'Tag 2', - lineage: ['Tag 2'], - canDeleteObjecttag: true, - }, - ], - }, - { - name: 'Taxonomy 2', - taxonomyId: 124, - canTagObject: true, - tags: [ - { - value: 'Tag 3', - lineage: ['Tag 3'], - canDeleteObjecttag: true, - }, - ], - }, - ], - }, - }); - getTaxonomyListData.mockResolvedValue({ - results: [{ - id: 123, - name: 'Taxonomy 1', - description: 'This is a description 1', - canTagObject: false, - }, { - id: 124, - name: 'Taxonomy 2', - description: 'This is a description 2', - canTagObject: false, - }], - }); await act(async () => { - const { container, getByText } = render(); - await waitFor(() => { expect(getByText('Taxonomy 1')).toBeInTheDocument(); }); - expect(getByText('Taxonomy 1')).toBeInTheDocument(); - expect(getByText('Taxonomy 2')).toBeInTheDocument(); + const { container } = renderDrawer(largeTagsId); + await waitFor(() => { expect(screen.getByText('Taxonomy 1')).toBeInTheDocument(); }); + expect(screen.getByText('Taxonomy 1')).toBeInTheDocument(); + expect(screen.getByText('Taxonomy 2')).toBeInTheDocument(); const tagCountBadges = container.getElementsByClassName('taxonomy-tags-count-chip'); - expect(tagCountBadges[0].textContent).toBe('2'); - expect(tagCountBadges[1].textContent).toBe('1'); + expect(tagCountBadges[0].textContent).toBe('3'); + expect(tagCountBadges[1].textContent).toBe('2'); }); }); it('should be read only on first render on drawer variant', async () => { - setupMockDataForStagedTagsTesting(); - render(); + renderDrawer(stagedTagsId); expect(await screen.findByText('Taxonomy 1')).toBeInTheDocument(); expect(screen.getByRole('button', { name: /close/i })); expect(screen.getByRole('button', { name: /edit tags/i })); @@ -626,8 +133,7 @@ describe('', () => { }); it('should be read only on first render on component variant', async () => { - setupMockDataForStagedTagsTesting(); - render(); + renderDrawer(stagedTagsId, { variant: 'component' }); expect(await screen.findByText('Taxonomy 1')).toBeInTheDocument(); expect(screen.getByRole('button', { name: /manage tags/i })); @@ -645,8 +151,7 @@ describe('', () => { }); it('should change to edit mode when click on `Edit tags` on drawer variant', async () => { - setupMockDataForStagedTagsTesting(); - render(); + renderDrawer(stagedTagsId); expect(await screen.findByText('Taxonomy 1')).toBeInTheDocument(); const editTagsButton = screen.getByRole('button', { name: /edit tags/i, @@ -669,8 +174,7 @@ describe('', () => { }); it('should change to edit mode when click on `Manage tags` on component variant', async () => { - setupMockDataForStagedTagsTesting(); - render(); + renderDrawer(stagedTagsId, { variant: 'component' }); expect(await screen.findByText('Taxonomy 1')).toBeInTheDocument(); const manageTagsButton = screen.getByRole('button', { name: /manage tags/i, @@ -693,8 +197,7 @@ describe('', () => { }); it('should change to read mode when click on `Cancel` on drawer variant', async () => { - setupMockDataForStagedTagsTesting(); - render(); + renderDrawer(stagedTagsId); expect(await screen.findByText('Taxonomy 1')).toBeInTheDocument(); const editTagsButton = screen.getByRole('button', { name: /edit tags/i, @@ -720,8 +223,7 @@ describe('', () => { }); it('should change to read mode when click on `Cancel` on component variant', async () => { - setupMockDataForStagedTagsTesting(); - render(); + renderDrawer(stagedTagsId, { variant: 'component' }); expect(await screen.findByText('Taxonomy 1')).toBeInTheDocument(); const manageTagsButton = screen.getByRole('button', { name: /manage tags/i, @@ -746,21 +248,8 @@ describe('', () => { expect(screen.queryByRole('button', { name: /save/i })).not.toBeInTheDocument(); }); - it('shows spinner when loading commit tags', async () => { - setupMockDataForStagedTagsTesting(); - useContentTaxonomyTagsUpdater.mockReturnValue({ - status: 'loading', - isError: false, - mutate: mockMutate, - }); - render(); - expect(await screen.findByText('Taxonomy 1')).toBeInTheDocument(); - expect(screen.getByRole('status')).toBeInTheDocument(); - }); - it('should test adding a content tag to the staged tags for a taxonomy', async () => { - setupMockDataForStagedTagsTesting(); - render(); + renderDrawer(stagedTagsId); expect(await screen.findByText('Taxonomy 1')).toBeInTheDocument(); // To edit mode @@ -775,7 +264,7 @@ describe('', () => { fireEvent.mouseDown(addTagsButton); // Tag 3 should only appear in dropdown selector, (i.e. the dropdown is open, since Tag 3 is not applied) - expect(screen.getAllByText('Tag 3').length).toBe(1); + expect((await screen.findAllByText('Tag 3')).length).toBe(1); // Click to check Tag 3 const tag3 = screen.getByText('Tag 3'); @@ -786,8 +275,7 @@ describe('', () => { }); it('should test removing a staged content from a taxonomy', async () => { - setupMockDataForStagedTagsTesting(); - render(); + renderDrawer(stagedTagsId); expect(await screen.findByText('Taxonomy 1')).toBeInTheDocument(); // To edit mode @@ -802,7 +290,7 @@ describe('', () => { fireEvent.mouseDown(addTagsButton); // Tag 3 should only appear in dropdown selector, (i.e. the dropdown is open, since Tag 3 is not applied) - expect(screen.getAllByText('Tag 3').length).toBe(1); + expect((await screen.findAllByText('Tag 3')).length).toBe(1); // Click to check Tag 3 const tag3 = screen.getByText('Tag 3'); @@ -817,11 +305,9 @@ describe('', () => { }); it('should test clearing staged tags for a taxonomy', async () => { - setupMockDataForStagedTagsTesting(); - const { container, - } = render(); + } = renderDrawer(stagedTagsId); expect(await screen.findByText('Taxonomy 1')).toBeInTheDocument(); // To edit mode @@ -836,7 +322,7 @@ describe('', () => { fireEvent.mouseDown(addTagsButton); // Tag 3 should only appear in dropdown selector, (i.e. the dropdown is open, since Tag 3 is not applied) - expect(screen.getAllByText('Tag 3').length).toBe(1); + expect((await screen.findAllByText('Tag 3')).length).toBe(1); // Click to check Tag 3 const tag3 = screen.getByText('Tag 3'); @@ -855,8 +341,7 @@ describe('', () => { }); it('should test adding global staged tags and cancel', async () => { - setupMockDataForStagedTagsTesting(); - render(); + renderDrawer(stagedTagsId); expect(await screen.findByText('Taxonomy 1')).toBeInTheDocument(); // To edit mode @@ -871,7 +356,7 @@ describe('', () => { fireEvent.mouseDown(addTagsButton); // Click to check Tag 3 - const tag3 = screen.getByText(/tag 3/i); + const tag3 = await screen.findByText(/tag 3/i); fireEvent.click(tag3); // Click "Add tags" to save to global staged tags @@ -888,8 +373,7 @@ describe('', () => { }); it('should test delete feched tags and cancel', async () => { - setupMockDataForStagedTagsTesting(); - render(); + renderDrawer(stagedTagsId); expect(await screen.findByText('Taxonomy 1')).toBeInTheDocument(); // To edit mode @@ -899,7 +383,7 @@ describe('', () => { fireEvent.click(editTagsButton); // Delete the tag - const tag = screen.getByText(/tag 2/i); + const tag = await screen.findByText(/tag 2/i); const deleteButton = within(tag).getByRole('button', { name: /delete/i, }); @@ -915,8 +399,7 @@ describe('', () => { }); it('should test delete global staged tags and cancel', async () => { - setupMockDataForStagedTagsTesting(); - render(); + renderDrawer(stagedTagsId); expect(await screen.findByText('Taxonomy 1')).toBeInTheDocument(); // To edit mode @@ -931,7 +414,7 @@ describe('', () => { fireEvent.mouseDown(addTagsButton); // Click to check Tag 3 - const tag3 = screen.getByText(/tag 3/i); + const tag3 = await screen.findByText(/tag 3/i); fireEvent.click(tag3); // Click "Add tags" to save to global staged tags @@ -957,8 +440,7 @@ describe('', () => { }); it('should test add removed feched tags and cancel', async () => { - setupMockDataForStagedTagsTesting(); - render(); + renderDrawer(stagedTagsId); expect(await screen.findByText('Taxonomy 1')).toBeInTheDocument(); // To edit mode @@ -968,7 +450,7 @@ describe('', () => { fireEvent.click(editTagsButton); // Delete the tag - const tag = screen.getByText(/tag 2/i); + const tag = await screen.findByText(/tag 2/i); const deleteButton = within(tag).getByRole('button', { name: /delete/i, }); @@ -982,7 +464,7 @@ describe('', () => { fireEvent.mouseDown(addTagsButton); // Click to check Tag 2 - const tag2 = screen.getByText(/tag 2/i); + const tag2 = await screen.findByText(/tag 2/i); fireEvent.click(tag2); // Click "Add tags" to save to global staged tags @@ -999,8 +481,7 @@ describe('', () => { }); it('should call onClose when cancel is clicked', async () => { - setupMockDataForStagedTagsTesting(); - render(); + renderDrawer(stagedTagsId, { onClose: mockOnClose }); const cancelButton = await screen.findByRole('button', { name: /close/i, @@ -1014,7 +495,7 @@ describe('', () => { it('should call closeManageTagsDrawer when Escape key is pressed and no selectable box is active', () => { const postMessageSpy = jest.spyOn(window.parent, 'postMessage'); - const { container } = render(); + const { container } = renderDrawer(stagedTagsId); fireEvent.keyDown(container, { key: 'Escape', @@ -1026,7 +507,7 @@ describe('', () => { }); it('should call `onClose` when Escape key is pressed and no selectable box is active', () => { - const { container } = render(); + const { container } = renderDrawer(stagedTagsId, { onClose: mockOnClose }); fireEvent.keyDown(container, { key: 'Escape', @@ -1038,7 +519,7 @@ describe('', () => { it('should not call closeManageTagsDrawer when Escape key is pressed and a selectable box is active', () => { const postMessageSpy = jest.spyOn(window.parent, 'postMessage'); - const { container } = render(); + const { container } = renderDrawer(stagedTagsId); // Simulate that the selectable box is open by adding an element with the data attribute const selectableBox = document.createElement('div'); @@ -1058,7 +539,7 @@ describe('', () => { }); it('should not call `onClose` when Escape key is pressed and a selectable box is active', () => { - const { container } = render(); + const { container } = renderDrawer(stagedTagsId, { onClose: mockOnClose }); // Simulate that the selectable box is open by adding an element with the data attribute const selectableBox = document.createElement('div'); @@ -1077,8 +558,7 @@ describe('', () => { it('should not call closeManageTagsDrawer when Escape key is pressed and container is blocked', () => { const postMessageSpy = jest.spyOn(window.parent, 'postMessage'); - - const { container } = render(); + const { container } = renderDrawer(stagedTagsId, { blockingSheet: true }); fireEvent.keyDown(container, { key: 'Escape', }); @@ -1089,7 +569,10 @@ describe('', () => { }); it('should not call `onClose` when Escape key is pressed and container is blocked', () => { - const { container } = render(); + const { container } = renderDrawer(stagedTagsId, { + blockingSheet: true, + onClose: mockOnClose, + }); fireEvent.keyDown(container, { key: 'Escape', }); @@ -1098,8 +581,10 @@ describe('', () => { }); it('should call `setBlockingSheet` on add a tag', async () => { - setupMockDataForStagedTagsTesting(); - render(); + renderDrawer(stagedTagsId, { + blockingSheet: true, + setBlockingSheet: mockSetBlockingSheet, + }); expect(await screen.findByText('Taxonomy 1')).toBeInTheDocument(); expect(mockSetBlockingSheet).toHaveBeenCalledWith(false); @@ -1116,7 +601,7 @@ describe('', () => { fireEvent.mouseDown(addTagsButton); // Click to check Tag 3 - const tag3 = screen.getByText(/tag 3/i); + const tag3 = await screen.findByText(/tag 3/i); fireEvent.click(tag3); // Click "Add tags" to save to global staged tags @@ -1127,8 +612,10 @@ describe('', () => { }); it('should call `setBlockingSheet` on delete a tag', async () => { - setupMockDataForStagedTagsTesting(); - render(); + renderDrawer(stagedTagsId, { + blockingSheet: true, + setBlockingSheet: mockSetBlockingSheet, + }); expect(await screen.findByText('Taxonomy 1')).toBeInTheDocument(); expect(mockSetBlockingSheet).toHaveBeenCalledWith(false); @@ -1150,8 +637,10 @@ describe('', () => { }); it('should call `updateTags` mutation on save', async () => { - setupMockDataForStagedTagsTesting(); - render(); + const { axiosMock } = initializeMocks(); + const url = getContentTaxonomyTagsApiUrl(stagedTagsId); + axiosMock.onPut(url).reply(200); + renderDrawer(stagedTagsId); expect(await screen.findByText('Taxonomy 1')).toBeInTheDocument(); const editTagsButton = screen.getByRole('button', { name: /edit tags/i, @@ -1163,12 +652,11 @@ describe('', () => { }); fireEvent.click(saveButton); - expect(mockMutate).toHaveBeenCalled(); + await waitFor(() => expect(axiosMock.history.put[0].url).toEqual(url)); }); it('should taxonomies must be ordered', async () => { - setupLargeMockDataForStagedTagsTesting(); - render(); + renderDrawer(largeTagsId); expect(await screen.findByText('Taxonomy 1')).toBeInTheDocument(); // First, taxonomies with content sorted by count implicit @@ -1188,18 +676,14 @@ describe('', () => { }); it('should not show "Other tags" section', async () => { - setupMockDataForStagedTagsTesting(); - - render(); + renderDrawer(stagedTagsId); expect(await screen.findByText('Taxonomy 1')).toBeInTheDocument(); expect(screen.queryByText('Other tags')).not.toBeInTheDocument(); }); it('should show "Other tags" section', async () => { - setupMockDataWithOtherTagsTestings(); - - render(); + renderDrawer(otherTagsId); expect(await screen.findByText('Taxonomy 1')).toBeInTheDocument(); expect(screen.getByText('Other tags')).toBeInTheDocument(); @@ -1209,8 +693,7 @@ describe('', () => { }); it('should test delete "Other tags" and cancel', async () => { - setupMockDataWithOtherTagsTestings(); - render(); + renderDrawer(otherTagsId); expect(await screen.findByText('Taxonomy 2')).toBeInTheDocument(); // To edit mode @@ -1236,40 +719,18 @@ describe('', () => { }); it('should show Language Taxonomy', async () => { - setupMockDataLanguageTaxonomyTestings(true); - render(); + renderDrawer(languageWithTagsId); expect(await screen.findByText('Languages')).toBeInTheDocument(); }); it('should hide Language Taxonomy', async () => { - setupMockDataLanguageTaxonomyTestings(false); - render(); + renderDrawer(languageWithoutTagsId); expect(await screen.findByText('Taxonomy 1')).toBeInTheDocument(); - expect(screen.queryByText('Languages')).not.toBeInTheDocument(); }); it('should show empty drawer message', async () => { - useContentTaxonomyTagsData.mockReturnValue({ - isSuccess: true, - data: { - taxonomies: [], - }, - }); - getTaxonomyListData.mockResolvedValue({ - results: [], - }); - useTaxonomyTagsData.mockReturnValue({ - hasMorePages: false, - canAddTag: false, - tagPages: { - isLoading: false, - isError: false, - data: [], - }, - }); - - render(); + renderDrawer(emptyTagsId); expect(await screen.findByText(/to use tags, please or contact your administrator\./i)).toBeInTheDocument(); const enableButton = screen.getByRole('button', { name: /enable a taxonomy/i, diff --git a/src/content-tags-drawer/data/api.mocks.ts b/src/content-tags-drawer/data/api.mocks.ts new file mode 100644 index 0000000000..ce1d50c05c --- /dev/null +++ b/src/content-tags-drawer/data/api.mocks.ts @@ -0,0 +1,378 @@ +import * as api from './api'; +import * as taxonomyApi from '../../taxonomy/data/api'; +import { languageExportId } from '../utils'; + +/** + * Mock for `getContentTaxonomyTagsData()` + */ +export async function mockContentTaxonomyTagsData(contentId: string): Promise { + const thisMock = mockContentTaxonomyTagsData; + switch (contentId) { + case thisMock.stagedTagsId: return thisMock.stagedTags; + case thisMock.otherTagsId: return thisMock.otherTags; + case thisMock.languageWithTagsId: return thisMock.languageWithTags; + case thisMock.languageWithoutTagsId: return thisMock.languageWithoutTags; + case thisMock.largeTagsId: return thisMock.largeTags; + case thisMock.emptyTagsId: return thisMock.emptyTags; + default: throw new Error(`No mock has been set up for contentId "${contentId}"`); + } +} +mockContentTaxonomyTagsData.stagedTagsId = 'block-v1:StagedTagsOrg+STC1+2023_1+type@vertical+block@stagedTagsId'; +mockContentTaxonomyTagsData.stagedTags = { + taxonomies: [ + { + name: 'Taxonomy 1', + taxonomyId: 123, + canTagObject: true, + tags: [ + { + value: 'Tag 1', + lineage: ['Tag 1'], + canDeleteObjecttag: true, + }, + { + value: 'Tag 2', + lineage: ['Tag 2'], + canDeleteObjecttag: true, + }, + ], + }, + ], +}; +mockContentTaxonomyTagsData.otherTagsId = 'block-v1:StagedTagsOrg+STC1+2023_1+type@vertical+block@otherTagsId'; +mockContentTaxonomyTagsData.otherTags = { + taxonomies: [ + { + name: 'Taxonomy 1', + taxonomyId: 123, + canTagObject: true, + tags: [ + { + value: 'Tag 1', + lineage: ['Tag 1'], + canDeleteObjecttag: true, + }, + { + value: 'Tag 2', + lineage: ['Tag 2'], + canDeleteObjecttag: true, + }, + ], + }, + { + name: 'Taxonomy 2', + taxonomyId: 1234, + canTagObject: false, + tags: [ + { + value: 'Tag 3', + lineage: ['Tag 3'], + canDeleteObjecttag: true, + }, + { + value: 'Tag 4', + lineage: ['Tag 4'], + canDeleteObjecttag: true, + }, + ], + }, + ], +}; +mockContentTaxonomyTagsData.languageWithTagsId = 'block-v1:LanguageTagsOrg+STC1+2023_1+type@vertical+block@languageWithTagsId'; +mockContentTaxonomyTagsData.languageWithTags = { + taxonomies: [ + { + name: 'Languages', + taxonomyId: 1234, + exportId: languageExportId, + canTagObject: true, + tags: [ + { + value: 'Tag 1', + lineage: ['Tag 1'], + canDeleteObjecttag: true, + }, + ], + }, + { + name: 'Taxonomy 1', + taxonomyId: 12345, + canTagObject: true, + tags: [ + { + value: 'Tag 1', + lineage: ['Tag 1'], + canDeleteObjecttag: true, + }, + { + value: 'Tag 2', + lineage: ['Tag 2'], + canDeleteObjecttag: true, + }, + ], + }, + ], +}; +mockContentTaxonomyTagsData.languageWithoutTagsId = 'block-v1:LanguageTagsOrg+STC1+2023_1+type@vertical+block@languageWithoutTagsId'; +mockContentTaxonomyTagsData.languageWithoutTags = { + taxonomies: [ + { + name: 'Languages', + taxonomyId: 1234, + exportId: languageExportId, + canTagObject: true, + tags: [], + }, + { + name: 'Taxonomy 1', + taxonomyId: 12345, + canTagObject: true, + tags: [ + { + value: 'Tag 1', + lineage: ['Tag 1'], + canDeleteObjecttag: true, + }, + { + value: 'Tag 2', + lineage: ['Tag 2'], + canDeleteObjecttag: true, + }, + ], + }, + ], +}; +mockContentTaxonomyTagsData.largeTagsId = 'block-v1:LargeTagsOrg+STC1+2023_1+type@vertical+block@largeTagsId'; +mockContentTaxonomyTagsData.largeTags = { + taxonomies: [ + { + name: 'Taxonomy 1', + taxonomyId: 123, + canTagObject: true, + tags: [ + { + value: 'Tag 1', + lineage: ['Tag 1'], + canDeleteObjecttag: true, + }, + { + value: 'Tag 2', + lineage: ['Tag 2'], + canDeleteObjecttag: true, + }, + ], + }, + { + name: 'Taxonomy 2', + taxonomyId: 124, + canTagObject: true, + tags: [ + { + value: 'Tag 1', + lineage: ['Tag 1'], + canDeleteObjecttag: true, + }, + ], + }, + { + name: 'Taxonomy 3', + taxonomyId: 125, + canTagObject: true, + tags: [ + { + value: 'Tag 1.1.1', + lineage: ['Tag 1', 'Tag 1.1', 'Tag 1.1.1'], + canDeleteObjecttag: true, + }, + ], + }, + { + name: '(B) Taxonomy 4', + taxonomyId: 126, + canTagObject: true, + tags: [], + }, + { + name: '(A) Taxonomy 5', + taxonomyId: 127, + canTagObject: true, + tags: [], + }, + ], +}; +mockContentTaxonomyTagsData.emptyTagsId = 'block-v1:EmptyTagsOrg+STC1+2023_1+type@vertical+block@emptyTagsId'; +mockContentTaxonomyTagsData.emptyTags = { + taxonomies: [], +}; +mockContentTaxonomyTagsData.applyMock = () => jest.spyOn(api, 'getContentTaxonomyTagsData').mockImplementation(mockContentTaxonomyTagsData); + +/** + * Mock for `getTaxonomyListData()` + */ +export async function mockTaxonomyListData(org: string): Promise { + const thisMock = mockTaxonomyListData; + switch (org) { + case thisMock.stagedTagsOrg: return thisMock.stagedTags; + case thisMock.languageTagsOrg: return thisMock.languageTags; + case thisMock.largeTagsOrg: return thisMock.largeTags; + case thisMock.emptyTagsOrg: return thisMock.emptyTags; + default: throw new Error(`No mock has been set up for org "${org}"`); + } +} +mockTaxonomyListData.stagedTagsOrg = 'StagedTagsOrg'; +mockTaxonomyListData.stagedTags = { + results: [ + { + id: 123, + name: 'Taxonomy 1', + description: 'This is a description 1', + canTagObject: true, + }, + ], +}; +mockTaxonomyListData.languageTagsOrg = 'LanguageTagsOrg'; +mockTaxonomyListData.languageTags = { + results: [ + { + id: 1234, + name: 'Languages', + description: 'This is a description 1', + exportId: languageExportId, + canTagObject: true, + }, + { + id: 12345, + name: 'Taxonomy 1', + description: 'This is a description 2', + canTagObject: true, + }, + ], +}; +mockTaxonomyListData.largeTagsOrg = 'LargeTagsOrg'; +mockTaxonomyListData.largeTags = { + results: [ + { + id: 123, + name: 'Taxonomy 1', + description: 'This is a description 1', + canTagObject: true, + }, + { + id: 124, + name: 'Taxonomy 2', + description: 'This is a description 2', + canTagObject: true, + }, + { + id: 125, + name: 'Taxonomy 3', + description: 'This is a description 3', + canTagObject: true, + }, + { + id: 127, + name: '(A) Taxonomy 5', + description: 'This is a description 5', + canTagObject: true, + }, + { + id: 126, + name: '(B) Taxonomy 4', + description: 'This is a description 4', + canTagObject: true, + }, + ], +}; +mockTaxonomyListData.emptyTagsOrg = 'EmptyTagsOrg'; +mockTaxonomyListData.emptyTags = { + results: [], +}; +mockTaxonomyListData.applyMock = () => jest.spyOn(taxonomyApi, 'getTaxonomyListData').mockImplementation(mockTaxonomyListData); + +/** + * Mock for `getTaxonomyTagsData()` + */ +export async function mockTaxonomyTagsData(taxonomyId: number): Promise { + const thisMock = mockTaxonomyTagsData; + switch (taxonomyId) { + case thisMock.stagedTagsTaxonomy: return thisMock.stagedTags; + case thisMock.languageTagsTaxonomy: return thisMock.languageTags; + default: throw new Error(`No mock has been set up for taxonomyId "${taxonomyId}"`); + } +} +mockTaxonomyTagsData.stagedTagsTaxonomy = 123; +mockTaxonomyTagsData.stagedTags = { + count: 3, + currentPage: 1, + next: null, + numPages: 1, + previous: null, + start: 1, + results: [ + { + value: 'Tag 1', + externalId: null, + childCount: 0, + depth: 0, + parentValue: null, + id: 12345, + subTagsUrl: null, + canChangeTag: false, + canDeleteTag: false, + }, + { + value: 'Tag 2', + externalId: null, + childCount: 0, + depth: 0, + parentValue: null, + id: 12346, + subTagsUrl: null, + canChangeTag: false, + canDeleteTag: false, + }, + { + value: 'Tag 3', + externalId: null, + childCount: 0, + depth: 0, + parentValue: null, + id: 12347, + subTagsUrl: null, + canChangeTag: false, + canDeleteTag: false, + }, + ], +}; +mockTaxonomyTagsData.languageTagsTaxonomy = 1234; +mockTaxonomyTagsData.languageTags = { + count: 1, + currentPage: 1, + next: null, + numPages: 1, + previous: null, + start: 1, + results: [{ + value: 'Tag 1', + externalId: null, + childCount: 0, + depth: 0, + parentValue: null, + id: 12345, + subTagsUrl: null, + canChangeTag: false, + canDeleteTag: false, + }], +}; +mockTaxonomyTagsData.applyMock = () => jest.spyOn(api, 'getTaxonomyTagsData').mockImplementation(mockTaxonomyTagsData); + +/** + * Mock for `getContentData()` + */ +export async function mockContentData(): Promise { + return mockContentData.data; +} +mockContentData.data = { + displayName: 'Unit 1', +}; +mockContentData.applyMock = () => jest.spyOn(api, 'getContentData').mockImplementation(mockContentData);