Files
Chris Chávez dad736f9d1 refactor: Migration of course details to React query (#2724)
- Migrates the `courseDetails` part from the Redux Store to React Query.
- Creates a new `CourseAuthoringContext` 
- Update the pages in `<CourseAuthoringRoutes>` to use the newly created context.
- Migrates some files to Typescript
- Migrates some tests to use `src/testUtils.tsx`
2025-12-06 00:14:32 +00:00

159 lines
5.2 KiB
JavaScript

import {
render,
act,
fireEvent,
waitFor,
queryByRole,
queryByTestId,
queryByText,
getByRole,
waitForElementToBeRemoved,
initializeMocks,
} from 'CourseAuthoring/testUtils';
import ReactDOM from 'react-dom';
import { executeThunk } from 'CourseAuthoring/utils';
import PagesAndResourcesProvider from 'CourseAuthoring/pages-and-resources/PagesAndResourcesProvider';
import { CourseAuthoringProvider } from 'CourseAuthoring/CourseAuthoringContext';
import LiveSettings from './Settings';
import {
generateLiveConfigurationApiResponse,
courseId,
initialState,
configurationProviders,
} from './factories/mockApiResponses';
import { fetchLiveConfiguration, fetchLiveProviders } from './data/thunks';
import { providerConfigurationApiUrl, providersApiUrl } from './data/api';
import messages from './messages';
let axiosMock;
let container;
let store;
const liveSettingsUrl = `/course/${courseId}/pages-and-resources/live/settings`;
// Modal creates a portal. Overriding ReactDOM.createPortal allows portals to be tested in jest.
ReactDOM.createPortal = jest.fn(node => node);
const renderComponent = () => {
const wrapper = render(
<PagesAndResourcesProvider courseId={courseId}>
<CourseAuthoringProvider>
<LiveSettings onClose={() => {}} />
</CourseAuthoringProvider>
</PagesAndResourcesProvider>,
{
path: liveSettingsUrl,
routerProps: {
initialEntries: [liveSettingsUrl],
},
params: {
courseId,
},
},
);
container = wrapper.container;
};
const mockStore = async ({
usernameSharing = false,
emailSharing = false,
enabled = true,
piiSharingAllowed = true,
}) => {
const fetchProviderConfigUrl = `${providersApiUrl}/${courseId}/`;
const fetchLiveConfigUrl = `${providerConfigurationApiUrl}/${courseId}/`;
axiosMock.onGet(fetchProviderConfigUrl).reply(200, configurationProviders(emailSharing, usernameSharing));
axiosMock.onGet(fetchLiveConfigUrl).reply(200, generateLiveConfigurationApiResponse(enabled, piiSharingAllowed));
await executeThunk(fetchLiveProviders(courseId), store.dispatch);
await executeThunk(fetchLiveConfiguration(courseId), store.dispatch);
};
describe('LiveSettings', () => {
beforeEach(async () => {
const mocks = initializeMocks({
initialState,
});
store = mocks.reduxStore;
axiosMock = mocks.axiosMock;
});
test('Live Configuration modal is visible', async () => {
renderComponent();
expect(queryByRole(container, 'dialog')).toBeVisible();
});
test('Displays "Configure Live" heading', async () => {
renderComponent();
const headingElement = queryByTestId(container, 'modal-title');
expect(headingElement).toHaveTextContent(messages.heading.defaultMessage);
});
test('Displays title, helper text and badge when live configuration button is enabled', async () => {
await mockStore({ enabled: true });
renderComponent();
const label = container.querySelector('label[for="enable-live-toggle"]');
const helperText = container.querySelector('#enable-live-toggleHelpText');
const enableBadge = queryByTestId(container, 'enable-badge');
expect(label).toHaveTextContent(messages.enableLiveLabel.defaultMessage);
expect(enableBadge).toHaveTextContent('Enabled');
expect(helperText).toHaveTextContent(messages.enableLiveHelp.defaultMessage);
});
test('Displays title, helper text and hides badge when live configuration button is disabled', async () => {
await mockStore({ enabled: false, piiSharingAllowed: false });
renderComponent();
const label = container.querySelector('label[for="enable-live-toggle"]');
const helperText = container.querySelector('#enable-live-toggleHelpText');
expect(label).toHaveTextContent('Live');
expect(label.firstChild).not.toHaveTextContent('Enabled');
expect(helperText).toHaveTextContent(messages.enableLiveHelp.defaultMessage);
});
test('Displays provider heading, helper text and all providers', async () => {
await mockStore({
enabled: true,
piiSharingAllowed: true,
usernameSharing: false,
emailSharing: true,
});
renderComponent();
const spinner = getByRole(container, 'status');
await waitForElementToBeRemoved(spinner);
const providers = queryByRole(container, 'group');
const helperText = queryByTestId(container, 'helper-text');
expect(providers.childElementCount).toBe(2);
expect(providers).toHaveTextContent('Zoom');
expect(providers).toHaveTextContent('BigBlueButton');
expect(helperText).toHaveTextContent(
messages.providerHelperText.defaultMessage.replace('{providerName}', 'Zoom'),
);
});
test('Unable to save error should be shown on submission if a field is empty', async () => {
const apiDefaultResponse = generateLiveConfigurationApiResponse(true, true);
apiDefaultResponse.lti_configuration.lti_1p1_client_key = '';
await mockStore({ emailSharing: false, piiSharingAllowed: false });
renderComponent();
const spinner = getByRole(container, 'status');
await waitForElementToBeRemoved(spinner);
const saveButton = queryByText(container, 'Save');
await waitFor(async () => {
await act(async () => fireEvent.click(saveButton));
expect(queryByRole(container, 'alert')).toBeVisible();
});
});
});