|
|
|
|
@@ -1,18 +1,20 @@
|
|
|
|
|
import React from 'react';
|
|
|
|
|
import MockAdapter from 'axios-mock-adapter';
|
|
|
|
|
import { initializeMockApp } from '@edx/frontend-platform';
|
|
|
|
|
import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth';
|
|
|
|
|
import { IntlProvider } from '@edx/frontend-platform/i18n';
|
|
|
|
|
import { AppProvider } from '@edx/frontend-platform/react';
|
|
|
|
|
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
|
|
|
|
import { fireEvent, render, waitFor } from '@testing-library/react';
|
|
|
|
|
import type MockAdapter from 'axios-mock-adapter';
|
|
|
|
|
import userEvent from '@testing-library/user-event';
|
|
|
|
|
|
|
|
|
|
import initializeStore from '../../store';
|
|
|
|
|
import {
|
|
|
|
|
act,
|
|
|
|
|
fireEvent,
|
|
|
|
|
initializeMocks,
|
|
|
|
|
render,
|
|
|
|
|
screen,
|
|
|
|
|
waitFor,
|
|
|
|
|
} from '../../testUtils';
|
|
|
|
|
import { studioHomeMock } from '../../studio-home/__mocks__';
|
|
|
|
|
import { getStudioHomeApiUrl } from '../../studio-home/data/api';
|
|
|
|
|
import { CreateLibrary } from '.';
|
|
|
|
|
import { getContentLibraryV2CreateApiUrl } from './data/api';
|
|
|
|
|
|
|
|
|
|
let store;
|
|
|
|
|
const mockNavigate = jest.fn();
|
|
|
|
|
let axiosMock: MockAdapter;
|
|
|
|
|
|
|
|
|
|
@@ -29,66 +31,38 @@ jest.mock('../../generic/data/apiHooks', () => ({
|
|
|
|
|
}),
|
|
|
|
|
}));
|
|
|
|
|
|
|
|
|
|
const queryClient = new QueryClient({
|
|
|
|
|
defaultOptions: {
|
|
|
|
|
queries: {
|
|
|
|
|
retry: false,
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const RootWrapper = () => (
|
|
|
|
|
<AppProvider store={store}>
|
|
|
|
|
<IntlProvider locale="en" messages={{}}>
|
|
|
|
|
<QueryClientProvider client={queryClient}>
|
|
|
|
|
<CreateLibrary />
|
|
|
|
|
</QueryClientProvider>
|
|
|
|
|
</IntlProvider>
|
|
|
|
|
</AppProvider>
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
describe('<CreateLibrary />', () => {
|
|
|
|
|
beforeEach(() => {
|
|
|
|
|
initializeMockApp({
|
|
|
|
|
authenticatedUser: {
|
|
|
|
|
userId: 3,
|
|
|
|
|
username: 'abc123',
|
|
|
|
|
administrator: true,
|
|
|
|
|
roles: [],
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
store = initializeStore();
|
|
|
|
|
|
|
|
|
|
axiosMock = new MockAdapter(getAuthenticatedHttpClient());
|
|
|
|
|
axiosMock = initializeMocks().axiosMock;
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
afterEach(() => {
|
|
|
|
|
jest.clearAllMocks();
|
|
|
|
|
axiosMock.restore();
|
|
|
|
|
queryClient.clear();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
test('call api data with correct data', async () => {
|
|
|
|
|
axiosMock.onGet(getStudioHomeApiUrl()).reply(200, studioHomeMock);
|
|
|
|
|
axiosMock.onPost(getContentLibraryV2CreateApiUrl()).reply(200, {
|
|
|
|
|
id: 'library-id',
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const { getByRole } = render(<RootWrapper />);
|
|
|
|
|
render(<CreateLibrary />);
|
|
|
|
|
|
|
|
|
|
const titleInput = getByRole('textbox', { name: /library name/i });
|
|
|
|
|
const titleInput = await screen.findByRole('textbox', { name: /library name/i });
|
|
|
|
|
userEvent.click(titleInput);
|
|
|
|
|
userEvent.type(titleInput, 'Test Library Name');
|
|
|
|
|
|
|
|
|
|
const orgInput = getByRole('combobox', { name: /organization/i });
|
|
|
|
|
const orgInput = await screen.findByRole('combobox', { name: /organization/i });
|
|
|
|
|
userEvent.click(orgInput);
|
|
|
|
|
userEvent.type(orgInput, 'org1');
|
|
|
|
|
userEvent.tab();
|
|
|
|
|
act(() => userEvent.tab());
|
|
|
|
|
|
|
|
|
|
const slugInput = getByRole('textbox', { name: /library id/i });
|
|
|
|
|
const slugInput = await screen.findByRole('textbox', { name: /library id/i });
|
|
|
|
|
userEvent.click(slugInput);
|
|
|
|
|
userEvent.type(slugInput, 'test_library_slug');
|
|
|
|
|
|
|
|
|
|
fireEvent.click(getByRole('button', { name: /create/i }));
|
|
|
|
|
fireEvent.click(await screen.findByRole('button', { name: /create/i }));
|
|
|
|
|
await waitFor(() => {
|
|
|
|
|
expect(axiosMock.history.post.length).toBe(1);
|
|
|
|
|
expect(axiosMock.history.post[0].data).toBe(
|
|
|
|
|
@@ -98,41 +72,115 @@ describe('<CreateLibrary />', () => {
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
test('show api error', async () => {
|
|
|
|
|
axiosMock.onPost(getContentLibraryV2CreateApiUrl()).reply(400, {
|
|
|
|
|
field: 'Error message',
|
|
|
|
|
test('cannot create new org unless allowed', async () => {
|
|
|
|
|
axiosMock.onGet(getStudioHomeApiUrl()).reply(200, studioHomeMock);
|
|
|
|
|
axiosMock.onPost(getContentLibraryV2CreateApiUrl()).reply(200, {
|
|
|
|
|
id: 'library-id',
|
|
|
|
|
});
|
|
|
|
|
const { getByRole, getByTestId } = render(<RootWrapper />);
|
|
|
|
|
|
|
|
|
|
const titleInput = getByRole('textbox', { name: /library name/i });
|
|
|
|
|
render(<CreateLibrary />);
|
|
|
|
|
|
|
|
|
|
const titleInput = await screen.findByRole('textbox', { name: /library name/i });
|
|
|
|
|
userEvent.click(titleInput);
|
|
|
|
|
userEvent.type(titleInput, 'Test Library Name');
|
|
|
|
|
|
|
|
|
|
const orgInput = getByTestId('autosuggest-textbox-input');
|
|
|
|
|
userEvent.click(orgInput);
|
|
|
|
|
userEvent.type(orgInput, 'org1');
|
|
|
|
|
userEvent.tab();
|
|
|
|
|
// We cannot create a new org, and so we're restricted to the allowed list
|
|
|
|
|
const orgOptions = screen.getByTestId('autosuggest-iconbutton');
|
|
|
|
|
userEvent.click(orgOptions);
|
|
|
|
|
expect(screen.getByText('org1')).toBeInTheDocument();
|
|
|
|
|
['org2', 'org3', 'org4', 'org5'].forEach((org) => expect(screen.queryByText(org)).not.toBeInTheDocument());
|
|
|
|
|
|
|
|
|
|
const slugInput = getByRole('textbox', { name: /library id/i });
|
|
|
|
|
const orgInput = await screen.findByRole('combobox', { name: /organization/i });
|
|
|
|
|
userEvent.click(orgInput);
|
|
|
|
|
userEvent.type(orgInput, 'NewOrg');
|
|
|
|
|
act(() => userEvent.tab());
|
|
|
|
|
|
|
|
|
|
const slugInput = await screen.findByRole('textbox', { name: /library id/i });
|
|
|
|
|
userEvent.click(slugInput);
|
|
|
|
|
userEvent.type(slugInput, 'test_library_slug');
|
|
|
|
|
|
|
|
|
|
fireEvent.click(getByRole('button', { name: /create/i }));
|
|
|
|
|
fireEvent.click(await screen.findByRole('button', { name: /create/i }));
|
|
|
|
|
await waitFor(() => {
|
|
|
|
|
expect(axiosMock.history.post.length).toBe(0);
|
|
|
|
|
});
|
|
|
|
|
expect(await screen.findByText('Required field.')).toBeInTheDocument();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
test('can create new org if allowed', async () => {
|
|
|
|
|
axiosMock.onGet(getStudioHomeApiUrl()).reply(200, {
|
|
|
|
|
...studioHomeMock,
|
|
|
|
|
allow_to_create_new_org: true,
|
|
|
|
|
});
|
|
|
|
|
axiosMock.onPost(getContentLibraryV2CreateApiUrl()).reply(200, {
|
|
|
|
|
id: 'library-id',
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
render(<CreateLibrary />);
|
|
|
|
|
|
|
|
|
|
const titleInput = await screen.findByRole('textbox', { name: /library name/i });
|
|
|
|
|
userEvent.click(titleInput);
|
|
|
|
|
userEvent.type(titleInput, 'Test Library Name');
|
|
|
|
|
|
|
|
|
|
// We can create a new org, so we're also allowed to use any existing org
|
|
|
|
|
const orgOptions = screen.getByTestId('autosuggest-iconbutton');
|
|
|
|
|
userEvent.click(orgOptions);
|
|
|
|
|
['org1', 'org2', 'org3', 'org4', 'org5'].forEach((org) => expect(screen.queryByText(org)).toBeInTheDocument());
|
|
|
|
|
|
|
|
|
|
const orgInput = await screen.findByRole('combobox', { name: /organization/i });
|
|
|
|
|
userEvent.click(orgInput);
|
|
|
|
|
userEvent.type(orgInput, 'NewOrg');
|
|
|
|
|
act(() => userEvent.tab());
|
|
|
|
|
|
|
|
|
|
const slugInput = await screen.findByRole('textbox', { name: /library id/i });
|
|
|
|
|
userEvent.click(slugInput);
|
|
|
|
|
userEvent.type(slugInput, 'test_library_slug');
|
|
|
|
|
|
|
|
|
|
fireEvent.click(await screen.findByRole('button', { name: /create/i }));
|
|
|
|
|
await waitFor(() => {
|
|
|
|
|
expect(axiosMock.history.post.length).toBe(1);
|
|
|
|
|
expect(axiosMock.history.post[0].data).toBe(
|
|
|
|
|
'{"description":"","title":"Test Library Name","org":"NewOrg","slug":"test_library_slug"}',
|
|
|
|
|
);
|
|
|
|
|
expect(mockNavigate).toHaveBeenCalledWith('/library/library-id');
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
test('show api error', async () => {
|
|
|
|
|
axiosMock.onGet(getStudioHomeApiUrl()).reply(200, studioHomeMock);
|
|
|
|
|
axiosMock.onPost(getContentLibraryV2CreateApiUrl()).reply(400, {
|
|
|
|
|
field: 'Error message',
|
|
|
|
|
});
|
|
|
|
|
render(<CreateLibrary />);
|
|
|
|
|
|
|
|
|
|
const titleInput = await screen.findByRole('textbox', { name: /library name/i });
|
|
|
|
|
userEvent.click(titleInput);
|
|
|
|
|
userEvent.type(titleInput, 'Test Library Name');
|
|
|
|
|
|
|
|
|
|
const orgInput = await screen.findByTestId('autosuggest-textbox-input');
|
|
|
|
|
userEvent.click(orgInput);
|
|
|
|
|
userEvent.type(orgInput, 'org1');
|
|
|
|
|
act(() => userEvent.tab());
|
|
|
|
|
|
|
|
|
|
const slugInput = await screen.findByRole('textbox', { name: /library id/i });
|
|
|
|
|
userEvent.click(slugInput);
|
|
|
|
|
userEvent.type(slugInput, 'test_library_slug');
|
|
|
|
|
|
|
|
|
|
fireEvent.click(await screen.findByRole('button', { name: /create/i }));
|
|
|
|
|
await waitFor(async () => {
|
|
|
|
|
expect(axiosMock.history.post.length).toBe(1);
|
|
|
|
|
expect(axiosMock.history.post[0].data).toBe(
|
|
|
|
|
'{"description":"","title":"Test Library Name","org":"org1","slug":"test_library_slug"}',
|
|
|
|
|
);
|
|
|
|
|
expect(mockNavigate).not.toHaveBeenCalled();
|
|
|
|
|
expect(getByRole('alert')).toHaveTextContent('Request failed with status code 400');
|
|
|
|
|
expect(getByRole('alert')).toHaveTextContent('{"field":"Error message"}');
|
|
|
|
|
expect(await screen.findByRole('alert')).toHaveTextContent('Request failed with status code 400');
|
|
|
|
|
expect(await screen.findByRole('alert')).toHaveTextContent('{"field":"Error message"}');
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
test('cancel creating library navigates to libraries page', async () => {
|
|
|
|
|
const { getByRole } = render(<RootWrapper />);
|
|
|
|
|
render(<CreateLibrary />);
|
|
|
|
|
|
|
|
|
|
fireEvent.click(getByRole('button', { name: /cancel/i }));
|
|
|
|
|
fireEvent.click(await screen.findByRole('button', { name: /cancel/i }));
|
|
|
|
|
await waitFor(() => {
|
|
|
|
|
expect(mockNavigate).toHaveBeenCalledWith('/libraries');
|
|
|
|
|
});
|
|
|
|
|
|