Files
frontend-app-authoring/src/generic/library-reference-card/LibraryReferenceCard.test.tsx
Chris Chávez 7c1eb59f18 feat: Make selectable component cards & Component Info Sidebar [FC-0114] (#2880)
- Changes in the Unit sidebar context to enable selected components
- Implements the component info sidebar.
- Implements the container/component selection when opening the align sidebar
2026-02-19 17:02:26 -05:00

260 lines
7.8 KiB
TypeScript

import { getXBlockApiUrl } from '@src/course-outline/data/api';
import { render, screen, initializeMocks } from '@src/testUtils';
import { userEvent } from '@testing-library/user-event';
import { LibraryReferenceCard } from './LibraryReferenceCard';
let axiosMock;
const upstreamData = {
id: 'lct:UNIX:CIT1:subsection:99d7e14e2d6f4ab7989dc0d948f917df',
name: 'upstream subsection',
};
const sectionData = {
id: 'block-v1:UNIX+UX1+2025_T3+type@chapter+block@asafd',
displayName: 'downstream section',
upstreamInfo: {
upstreamRef: 'lct:UNIX:CIT1:section:d12323',
upstreamName: 'upstream section',
downstreamKey: 'block-v1:UNIX+UX1+2025_T3+type@chapter+block@asafd',
versionAvailable: 2,
versionDeclined: null,
readyToSync: false,
topLevelParentKey: null,
downstreamCustomized: [],
},
};
const itemData = {
id: 'block-v1:UNIX+UX1+2025_T3+type@sequential+block@sequential45d4d95a',
displayName: 'downstream subsection',
upstreamInfo: {
upstreamRef: upstreamData.id,
upstreamName: upstreamData.name,
downstreamKey: 'block-v1:UNIX+UX1+2025_T3+type@sequential+block@sequential45d4d95a',
versionAvailable: 2,
versionDeclined: null,
readyToSync: false,
topLevelParentKey: null,
downstreamCustomized: [],
},
};
const mockUseCourseAuthoringContext = jest.fn().mockReturnValue({
openUnlinkModal: jest.fn(),
courseId: 'course1',
});
jest.mock('@src/CourseAuthoringContext', () => ({
useCourseAuthoringContext: () => mockUseCourseAuthoringContext(),
}));
const mockPostChange = jest.fn();
const mockOpenContainerInfoSidebar = jest.fn();
const mockOpenSyncModal = jest.fn();
jest.mock('@src/hooks', () => ({
useToggleWithValue: () => [false, {}, mockOpenSyncModal, jest.fn()],
}));
describe('LibraryReferenceCard', () => {
beforeEach(() => {
const mocks = initializeMocks();
axiosMock = mocks.axiosMock;
axiosMock
.onGet(getXBlockApiUrl(sectionData.id))
.reply(200, sectionData);
axiosMock
.onGet(getXBlockApiUrl(itemData.id))
.reply(200, itemData);
});
it('renders the LibraryReferenceCard normally', async () => {
render(
<LibraryReferenceCard
itemId={itemData.id}
sectionId={sectionData.id}
postChange={mockPostChange}
goToParent={mockOpenContainerInfoSidebar}
/>,
);
expect(await screen.findByText(/Library Reference/)).toBeInTheDocument();
});
it('renders the LibraryReferenceCard error message', async () => {
const user = userEvent.setup();
const data = {
...itemData,
upstreamInfo: {
...itemData.upstreamInfo,
errorMessage: 'some error',
},
};
axiosMock
.onGet(getXBlockApiUrl(itemData.id))
.reply(200, data);
render(
<LibraryReferenceCard
itemId={itemData.id}
sectionId={sectionData.id}
postChange={mockPostChange}
goToParent={mockOpenContainerInfoSidebar}
/>,
);
expect(await screen.findByText(
`The link between ${itemData.displayName} and the library version has been broken. To edit or make changes, unlink component.`,
)).toBeInTheDocument();
await user.click(await screen.findByRole('button', { name: 'Unlink from library' }));
expect(mockUseCourseAuthoringContext().openUnlinkModal).toHaveBeenCalledWith({
value: data,
sectionId: sectionData.id,
});
});
it('renders the LibraryReferenceCard ready to sync', async () => {
const user = userEvent.setup();
axiosMock
.onGet(getXBlockApiUrl(itemData.id))
.reply(200, {
...itemData,
upstreamInfo: {
...itemData.upstreamInfo,
readyToSync: true,
},
});
render(
<LibraryReferenceCard
itemId={itemData.id}
sectionId={sectionData.id}
postChange={mockPostChange}
goToParent={mockOpenContainerInfoSidebar}
/>,
);
expect(await screen.findByText(
`${itemData.displayName} has available updates`,
)).toBeInTheDocument();
await user.click(await screen.findByRole('button', { name: 'Review Updates' }));
expect(mockOpenSyncModal).toHaveBeenCalled();
});
it('renders the LibraryReferenceCard customized text', async () => {
axiosMock
.onGet(getXBlockApiUrl(itemData.id))
.reply(200, {
...itemData,
upstreamInfo: {
...itemData.upstreamInfo,
downstreamCustomized: ['displayName'],
},
});
render(
<LibraryReferenceCard
itemId={itemData.id}
sectionId={sectionData.id}
postChange={mockPostChange}
goToParent={mockOpenContainerInfoSidebar}
/>,
);
expect(await screen.findByText(
`${itemData.displayName} has been modified in this course.`,
)).toBeInTheDocument();
});
it('renders the LibraryReferenceCard with top level error message', async () => {
const user = userEvent.setup();
axiosMock
.onGet(getXBlockApiUrl(itemData.id))
.reply(200, {
...itemData,
upstreamInfo: {
...itemData.upstreamInfo,
topLevelParentKey: sectionData.upstreamInfo.downstreamKey,
errorMessage: 'some error',
},
});
render(
<LibraryReferenceCard
itemId={itemData.id}
sectionId={sectionData.id}
postChange={mockPostChange}
goToParent={mockOpenContainerInfoSidebar}
/>,
);
expect(await screen.findByText(
`${itemData.displayName} was reused as part of a section which has a broken link. To recieve library updates to this component, unlink the broken link.`,
)).toBeInTheDocument();
await user.click(await screen.findByRole('button', { name: 'Unlink section' }));
// should call unlink with parent section data
expect(mockUseCourseAuthoringContext().openUnlinkModal).toHaveBeenCalledWith({
value: sectionData,
sectionId: sectionData.id,
});
});
it('renders the LibraryReferenceCard with top level ready to sync', async () => {
const user = userEvent.setup();
axiosMock
.onGet(getXBlockApiUrl(itemData.id))
.reply(200, {
...itemData,
upstreamInfo: {
...itemData.upstreamInfo,
topLevelParentKey: sectionData.upstreamInfo.downstreamKey,
},
});
const parentData = {
...sectionData,
upstreamInfo: {
...sectionData.upstreamInfo,
readyToSync: true,
},
};
axiosMock
.onGet(getXBlockApiUrl(sectionData.id))
.reply(200, parentData);
render(
<LibraryReferenceCard
itemId={itemData.id}
sectionId={sectionData.id}
postChange={mockPostChange}
goToParent={mockOpenContainerInfoSidebar}
/>,
);
expect(await screen.findByText(
`${itemData.displayName} was reused as part of a section which has updates available.`,
)).toBeInTheDocument();
await user.click(await screen.findByRole('button', { name: 'Review Updates' }));
expect(mockOpenSyncModal).toHaveBeenCalledWith(parentData);
});
it('renders the LibraryReferenceCard with top level go to parent option', async () => {
const user = userEvent.setup();
axiosMock
.onGet(getXBlockApiUrl(itemData.id))
.reply(200, {
...itemData,
upstreamInfo: {
...itemData.upstreamInfo,
topLevelParentKey: sectionData.upstreamInfo.downstreamKey,
},
});
render(
<LibraryReferenceCard
itemId={itemData.id}
sectionId={sectionData.id}
postChange={mockPostChange}
goToParent={mockOpenContainerInfoSidebar}
/>,
);
expect(await screen.findByText(
`${itemData.displayName} was reused as part of a section.`,
)).toBeInTheDocument();
await user.click(await screen.findByRole('button', { name: 'View section' }));
expect(mockOpenContainerInfoSidebar).toHaveBeenCalledWith(
sectionData.id,
undefined,
sectionData.id,
);
});
});