import userEvent from '@testing-library/user-event'; import type MockAdapter from 'axios-mock-adapter'; import { initializeMocks, render as baseRender, screen, waitFor, waitForElementToBeRemoved, within, } from '../../testUtils'; import { LibraryProvider } from '../common/context'; import { type CollectionHit } from '../../search-manager'; import CollectionCard from './CollectionCard'; import messages from './messages'; import { getLibraryCollectionApiUrl, getLibraryCollectionRestoreApiUrl } from '../data/api'; const CollectionHitSample: CollectionHit = { id: 'lib-collectionorg1democourse-collection-display-name', type: 'collection', contextKey: 'lb:org1:Demo_Course', usageKey: 'lib-collection:org1:Demo_Course:collection-display-name', org: 'org1', blockId: 'collection-display-name', breadcrumbs: [{ displayName: 'Demo Lib' }], displayName: 'Collection Display Name', description: 'Collection description', formatted: { displayName: 'Collection Display Formated Name', description: 'Collection description', }, created: 1722434322294, modified: 1722434322294, numChildren: 2, published: { numChildren: 1, }, tags: {}, }; let axiosMock: MockAdapter; let mockShowToast; const render = (ui: React.ReactElement, showOnlyPublished: boolean = false) => baseRender(ui, { extraWrapper: ({ children }) => ( { children } ), }); describe('', () => { beforeEach(() => { const mocks = initializeMocks(); axiosMock = mocks.axiosMock; mockShowToast = mocks.mockShowToast; }); it('should render the card with title and description', () => { render(); expect(screen.queryByText('Collection Display Formated Name')).toBeInTheDocument(); expect(screen.queryByText('Collection description')).toBeInTheDocument(); expect(screen.queryByText('Collection (2)')).toBeInTheDocument(); }); it('should render published content', () => { render(, true); expect(screen.queryByText('Collection Display Formated Name')).toBeInTheDocument(); expect(screen.queryByText('Collection description')).toBeInTheDocument(); expect(screen.queryByText('Collection (1)')).toBeInTheDocument(); }); it('should navigate to the collection if the open menu clicked', async () => { render(); // Open menu expect(screen.getByTestId('collection-card-menu-toggle')).toBeInTheDocument(); userEvent.click(screen.getByTestId('collection-card-menu-toggle')); // Open menu item const openMenuItem = screen.getByRole('link', { name: 'Open' }); expect(openMenuItem).toBeInTheDocument(); expect(openMenuItem).toHaveAttribute('href', '/library/lb:org1:Demo_Course/collection/collection-display-name/'); }); it('should show confirmation box, delete collection and show toast to undo deletion', async () => { const url = getLibraryCollectionApiUrl(CollectionHitSample.contextKey, CollectionHitSample.blockId); axiosMock.onDelete(url).reply(204); render(); expect(screen.queryByText('Collection Display Formated Name')).toBeInTheDocument(); // Open menu let menuBtn = await screen.findByRole('button', { name: messages.collectionCardMenuAlt.defaultMessage }); userEvent.click(menuBtn); // find and click delete menu option. expect(screen.queryByText('Delete')).toBeInTheDocument(); let deleteBtn = await screen.findByRole('button', { name: 'Delete' }); userEvent.click(deleteBtn); // verify confirmation dialog and click on cancel button let dialog = await screen.findByRole('dialog', { name: 'Delete this collection?' }); expect(dialog).toBeInTheDocument(); const cancelBtn = await screen.findByRole('button', { name: 'Cancel' }); userEvent.click(cancelBtn); expect(axiosMock.history.delete.length).toEqual(0); expect(cancelBtn).not.toBeInTheDocument(); // Open menu menuBtn = await screen.findByRole('button', { name: messages.collectionCardMenuAlt.defaultMessage }); userEvent.click(menuBtn); // click on confirm button to delete deleteBtn = await screen.findByRole('button', { name: 'Delete' }); userEvent.click(deleteBtn); dialog = await screen.findByRole('dialog', { name: 'Delete this collection?' }); const confirmBtn = await within(dialog).findByRole('button', { name: 'Delete' }); userEvent.click(confirmBtn); await waitForElementToBeRemoved(() => screen.queryByRole('dialog', { name: 'Delete this collection?' })); await waitFor(() => { expect(axiosMock.history.delete.length).toEqual(1); expect(mockShowToast).toHaveBeenCalled(); }); // Get restore / undo func from the toast const restoreFn = mockShowToast.mock.calls[0][1].onClick; const restoreUrl = getLibraryCollectionRestoreApiUrl(CollectionHitSample.contextKey, CollectionHitSample.blockId); axiosMock.onPost(restoreUrl).reply(200); // restore collection restoreFn(); await waitFor(() => { expect(axiosMock.history.post.length).toEqual(1); expect(mockShowToast).toHaveBeenCalledWith('Undo successful'); }); }); it('should show failed toast on delete collection failure', async () => { const url = getLibraryCollectionApiUrl(CollectionHitSample.contextKey, CollectionHitSample.blockId); axiosMock.onDelete(url).reply(404); render(); expect(screen.queryByText('Collection Display Formated Name')).toBeInTheDocument(); // Open menu const menuBtn = await screen.findByRole('button', { name: messages.collectionCardMenuAlt.defaultMessage }); userEvent.click(menuBtn); // find and click delete menu option. const deleteBtn = await screen.findByRole('button', { name: 'Delete' }); userEvent.click(deleteBtn); const dialog = await screen.findByRole('dialog', { name: 'Delete this collection?' }); const confirmBtn = await within(dialog).findByRole('button', { name: 'Delete' }); userEvent.click(confirmBtn); await waitForElementToBeRemoved(() => screen.queryByRole('dialog', { name: 'Delete this collection?' })); await waitFor(() => { expect(axiosMock.history.delete.length).toEqual(1); expect(mockShowToast).toHaveBeenCalledWith('Failed to delete collection'); }); }); });