import React from 'react'; import MockAdapter from 'axios-mock-adapter'; import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth'; import { Factory } from 'rosie'; import { render, screen, fireEvent, initializeTestStore, waitFor, authenticatedUser, logUnhandledRequests, } from '../../../setupTest'; import { BookmarkButton } from './index'; import { getBookmarksBaseUrl } from './data/api'; describe('Bookmark Button', () => { let axiosMock; let store; const courseMetadata = Factory.build('courseMetadata'); const mockData = { isProcessing: false, }; const nonBookmarkedUnitBlock = Factory.build( 'block', { type: 'vertical' }, { courseId: courseMetadata.id }, ); const bookmarkedUnitBlock = Factory.build( 'block', { type: 'vertical', bookmarked: true }, { courseId: courseMetadata.id }, ); const unitBlocks = [nonBookmarkedUnitBlock, bookmarkedUnitBlock]; beforeEach(async () => { store = await initializeTestStore({ courseMetadata, unitBlocks }); mockData.unitId = nonBookmarkedUnitBlock.id; axiosMock = new MockAdapter(getAuthenticatedHttpClient()); const bookmarkUrl = getBookmarksBaseUrl(); axiosMock.onPost(bookmarkUrl).reply(200, { }); const bookmarkDeleteUrlRegExp = new RegExp(`${bookmarkUrl}*,*`); axiosMock.onDelete(bookmarkDeleteUrlRegExp).reply(200, { }); logUnhandledRequests(axiosMock); }); it('handles adding bookmark', async () => { render(); const button = screen.getByRole('button', { name: 'Bookmark this page' }); expect(button).not.toHaveClass('disabled'); fireEvent.click(button); await waitFor(() => expect(axiosMock.history.post).toHaveLength(1)); expect(axiosMock.history.post[0].data).toEqual(JSON.stringify({ usage_id: nonBookmarkedUnitBlock.id })); expect(store.getState().models.units[nonBookmarkedUnitBlock.id].bookmarked).toBeTruthy(); }); it('does not handle adding bookmark when processing', async () => { render(); const button = screen.getByRole('button', { name: 'Bookmark this page' }); expect(button).toHaveClass('disabled'); fireEvent.click(button); // HACK: We don't have a function we could reliably await here, so this test relies on the timeout of `waitFor`. await expect(waitFor( () => expect(axiosMock.history.post).toHaveLength(1), { timeout: 100 }, )).rejects.toThrowError(/expect.*toHaveLength.*/); expect(store.getState().models.units[nonBookmarkedUnitBlock.id].bookmarked).toBeFalsy(); }); it('handles removing bookmark', async () => { render(); const button = screen.getByRole('button', { name: 'Bookmarked' }); fireEvent.click(button); await waitFor(() => expect(axiosMock.history.delete).toHaveLength(1)); expect(axiosMock.history.delete[0].url).toContain(`${authenticatedUser.username},${bookmarkedUnitBlock.id}`); expect(store.getState().models.units[bookmarkedUnitBlock.id].bookmarked).toBeFalsy(); }); it('does not handle removing bookmark when processing', async () => { render(); const button = screen.getByRole('button', { name: 'Bookmarked' }); expect(button).toHaveClass('disabled'); fireEvent.click(button); // HACK: We don't have a function we could reliably await here, so this test relies on the timeout of `waitFor`. await expect(waitFor( () => expect(axiosMock.history.delete).toHaveLength(1), { timeout: 100 }, )).rejects.toThrowError(/expect.*toHaveLength.*/); expect(store.getState().models.units[bookmarkedUnitBlock.id].bookmarked).toBeTruthy(); }); });