Skip to content

Commit

Permalink
test: update test coverage
Browse files Browse the repository at this point in the history
  • Loading branch information
bradenmacdonald committed Sep 10, 2024
1 parent 3ef8fce commit cc90757
Show file tree
Hide file tree
Showing 9 changed files with 60 additions and 11 deletions.
2 changes: 1 addition & 1 deletion src/editors/EditorContainer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ const EditorContainer: React.FC<Props> = ({
}) => {
const { blockType, blockId } = useParams();
if (blockType === undefined || blockId === undefined) {
// This shouldn't be possible; it's just here to satisfy the type checker.
// istanbul ignore next - This shouldn't be possible; it's just here to satisfy the type checker.
return <div>Error: missing URL parameters</div>;
}
return (
Expand Down
4 changes: 2 additions & 2 deletions src/generic/toast-context/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,11 @@ export interface ToastProviderProps {
* Global context to keep track of popup message(s) that appears to user after
* they take an action like creating or deleting something.
*/
export const ToastContext = React.createContext({
export const ToastContext = React.createContext<ToastContextData>({
toastMessage: null,
showToast: () => {},
closeToast: () => {},
} as ToastContextData);
});

/**
* React component to provide `ToastContext` to the app
Expand Down
1 change: 1 addition & 0 deletions src/library-authoring/LibraryAuthoringPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@ const LibraryAuthoringPage = () => {

const { libraryId } = useParams();
if (!libraryId) {
// istanbul ignore next - This shouldn't be possible; it's just here to satisfy the type checker.
throw new Error('Rendered without libraryId URL parameter');
}
const { data: libraryData, isLoading } = useContentLibrary(libraryId);
Expand Down
3 changes: 2 additions & 1 deletion src/library-authoring/LibraryLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ const LibraryLayout = () => {
const queryClient = useQueryClient();

if (libraryId === undefined) {
throw new Error('Error: route is missing libraryId.'); // Should never happen
// istanbul ignore next - This shouldn't be possible; it's just here to satisfy the type checker.
throw new Error('Error: route is missing libraryId.');
}

const navigate = useNavigate();
Expand Down
23 changes: 19 additions & 4 deletions src/library-authoring/add-content/AddContentWorkflow.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -47,11 +47,8 @@ const renderOpts = {
};

describe('AddContentWorkflow test', () => {
beforeEach(() => {
initializeMocks();
});

it('can create an HTML component', async () => {
initializeMocks();
render(<LibraryLayout />, renderOpts);

// Click "New [Component]"
Expand Down Expand Up @@ -86,4 +83,22 @@ describe('AddContentWorkflow test', () => {
fireEvent.click(saveButton);
expect(saveSpy).toHaveBeenCalledTimes(1);
});

it('can create a Problem component', async () => {
const { mockShowToast } = initializeMocks();
render(<LibraryLayout />, renderOpts);

// Click "New [Component]"
const newComponentButton = await screen.findByRole('button', { name: /New/ });
fireEvent.click(newComponentButton);

// Pre-condition - this is NOT shown yet:
expect(screen.queryByText('Content created successfully.')).not.toBeInTheDocument();

// Click "Problem" to create a capa problem component
fireEvent.click(await screen.findByRole('button', { name: /Problem/ }));

// We haven't yet implemented the problem editor, so we expect only a toast to appear
await waitFor(() => expect(mockShowToast).toHaveBeenCalledWith('Content created successfully.'));
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ interface Props {
usageKey: string;
}

/* istanbul ignore next */
export const ComponentDeveloperInfo: React.FC<Props> = ({ usageKey }) => {
const { data: olx, isLoading: isOLXLoading } = useXBlockOLX(usageKey);
return (
Expand Down
11 changes: 11 additions & 0 deletions src/library-authoring/data/api.mocks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,9 @@ export async function mockCreateLibraryBlock(
if (args.blockType === 'html' && args.libraryId === mockContentLibrary.libraryId) {
return mockCreateLibraryBlock.newHtmlData;
}
if (args.blockType === 'problem' && args.libraryId === mockContentLibrary.libraryId) {
return mockCreateLibraryBlock.newProblemData;
}
throw new Error(`mockCreateLibraryBlock doesn't know how to mock ${JSON.stringify(args)}`);
}
mockCreateLibraryBlock.newHtmlData = {
Expand All @@ -125,6 +128,14 @@ mockCreateLibraryBlock.newHtmlData = {
hasUnpublishedChanges: true,
tagsCount: 0,
} satisfies api.CreateBlockDataResponse;
mockCreateLibraryBlock.newProblemData = {
id: 'lb:Axim:TEST:problem:prob1',
defKey: 'prob1',
blockType: 'problem',
displayName: 'New Problem',
hasUnpublishedChanges: true,
tagsCount: 0,
} satisfies api.CreateBlockDataResponse;
/** Apply this mock. Returns a spy object that can tell you if it's been called. */
mockCreateLibraryBlock.applyMock = () => (
jest.spyOn(api, 'createLibraryBlock').mockImplementation(mockCreateLibraryBlock)
Expand Down
1 change: 1 addition & 0 deletions src/library-authoring/data/apiHooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,7 @@ export const useUpdateXBlockFields = (usageKey: string) => {
});
};

/* istanbul ignore next */ // This is only used in developer builds, and the associated UI doesn't work in test or prod
export const useXBlockOLX = (usageKey: string) => (
useQuery({
queryKey: libraryAuthoringQueryKeys.xblockOLX(usageKey),
Expand Down
25 changes: 22 additions & 3 deletions src/testUtils.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
*/
import React from 'react';
import { AxiosError } from 'axios';
import { jest } from '@jest/globals';
import { initializeMockApp } from '@edx/frontend-platform';
import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth';
import { IntlProvider } from '@edx/frontend-platform/i18n';
Expand All @@ -21,13 +22,21 @@ import {
Routes,
} from 'react-router-dom';

import { ToastContext, type ToastContextData } from './generic/toast-context';
import initializeReduxStore from './store';

/** @deprecated */
let reduxStore;
let queryClient;
let axiosMock: MockAdapter;

/** To use this: `const { mockShowToast } = initializeMocks()` and `expect(mockShowToast).toHaveBeenCalled()` */
let mockToastContext: ToastContextData = {
showToast: jest.fn(),
closeToast: jest.fn(),
toastMessage: null,
};

export interface RouteOptions {
/** The URL path, like '/libraries/:libraryId' */
path?: string;
Expand Down Expand Up @@ -75,9 +84,11 @@ function makeWrapper({ ...routeArgs }: RouteOptions) {
<AppProvider store={reduxStore} wrapWithRouter={false}>
<IntlProvider locale="en" messages={{}}>
<QueryClientProvider client={queryClient}>
<RouterAndRoute {...routeArgs}>
{children}
</RouterAndRoute>
<ToastContext.Provider value={mockToastContext}>
<RouterAndRoute {...routeArgs}>
{children}
</RouterAndRoute>
</ToastContext.Provider>
</QueryClientProvider>
</IntlProvider>
</AppProvider>
Expand Down Expand Up @@ -114,9 +125,17 @@ export function initializeMocks({ user = defaultUser } = {}) {
});
axiosMock = new MockAdapter(getAuthenticatedHttpClient());

// Reset `mockToastContext` for this current test
mockToastContext = {
showToast: jest.fn(),
closeToast: jest.fn(),
toastMessage: null,
};

return {
reduxStore,
axiosMock,
mockShowToast: mockToastContext.showToast,
};
}

Expand Down

0 comments on commit cc90757

Please sign in to comment.