diff --git a/src/library-authoring/create-library/CreateLibrary.test.tsx b/src/library-authoring/create-library/CreateLibrary.test.tsx
index cf323b1aca..d129cfed16 100644
--- a/src/library-authoring/create-library/CreateLibrary.test.tsx
+++ b/src/library-authoring/create-library/CreateLibrary.test.tsx
@@ -1,18 +1,21 @@
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 { getApiWaffleFlagsUrl } from '../../data/api';
import { CreateLibrary } from '.';
import { getContentLibraryV2CreateApiUrl } from './data/api';
-let store;
const mockNavigate = jest.fn();
let axiosMock: MockAdapter;
@@ -29,66 +32,41 @@ jest.mock('../../generic/data/apiHooks', () => ({
}),
}));
-const queryClient = new QueryClient({
- defaultOptions: {
- queries: {
- retry: false,
- },
- },
-});
-
-const RootWrapper = () => (
-
-
-
-
-
-
-
-);
-
describe('', () => {
beforeEach(() => {
- initializeMockApp({
- authenticatedUser: {
- userId: 3,
- username: 'abc123',
- administrator: true,
- roles: [],
- },
- });
- store = initializeStore();
-
- axiosMock = new MockAdapter(getAuthenticatedHttpClient());
+ axiosMock = initializeMocks().axiosMock;
+ axiosMock
+ .onGet(getApiWaffleFlagsUrl(undefined))
+ .reply(200, {});
});
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();
+ render();
- 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 +76,115 @@ describe('', () => {
});
});
+ test('cannot create new org unless allowed', async () => {
+ axiosMock.onGet(getStudioHomeApiUrl()).reply(200, studioHomeMock);
+ axiosMock.onPost(getContentLibraryV2CreateApiUrl()).reply(200, {
+ id: 'library-id',
+ });
+
+ render();
+
+ const titleInput = await screen.findByRole('textbox', { name: /library name/i });
+ userEvent.click(titleInput);
+ userEvent.type(titleInput, 'Test Library Name');
+
+ // 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 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(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();
+
+ 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',
});
- const { getByRole, getByTestId } = render();
+ render();
- 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 = getByTestId('autosuggest-textbox-input');
+ const orgInput = await screen.findByTestId('autosuggest-textbox-input');
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 }));
- await waitFor(() => {
+ 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();
+ render();
- fireEvent.click(getByRole('button', { name: /cancel/i }));
+ fireEvent.click(await screen.findByRole('button', { name: /cancel/i }));
await waitFor(() => {
expect(mockNavigate).toHaveBeenCalledWith('/libraries');
});
diff --git a/src/library-authoring/create-library/CreateLibrary.tsx b/src/library-authoring/create-library/CreateLibrary.tsx
index 1731984ec5..c4f3695c7e 100644
--- a/src/library-authoring/create-library/CreateLibrary.tsx
+++ b/src/library-authoring/create-library/CreateLibrary.tsx
@@ -19,6 +19,7 @@ import FormikErrorFeedback from '../../generic/FormikErrorFeedback';
import AlertError from '../../generic/alert-error';
import { useOrganizationListData } from '../../generic/data/apiHooks';
import SubHeader from '../../generic/sub-header/SubHeader';
+import { useStudioHome } from '../../studio-home/hooks';
import { useCreateLibraryV2 } from './data/apiHooks';
import messages from './messages';
@@ -38,10 +39,23 @@ const CreateLibrary = () => {
} = useCreateLibraryV2();
const {
- data: organizationListData,
+ data: allOrganizations,
isLoading: isOrganizationListLoading,
} = useOrganizationListData();
+ const {
+ studioHomeData: {
+ allowedOrganizationsForLibraries,
+ allowToCreateNewOrg,
+ },
+ } = useStudioHome();
+
+ const organizations = (
+ allowToCreateNewOrg
+ ? allOrganizations
+ : allowedOrganizationsForLibraries
+ ) || [];
+
const handleOnClickCancel = () => {
navigate('/libraries');
};
@@ -100,12 +114,17 @@ const CreateLibrary = () => {
formikProps.setFieldValue('org', event.selectionId)}
+ onChange={(event) => formikProps.setFieldValue(
+ 'org',
+ allowToCreateNewOrg
+ ? (event.selectionId || event.userProvidedText)
+ : event.selectionId,
+ )}
placeholder={intl.formatMessage(messages.orgPlaceholder)}
>
- {organizationListData ? organizationListData.map((org) => (
+ {organizations.map((org) => (
{org}
- )) : []}
+ ))}
{intl.formatMessage(messages.orgHelp)}
diff --git a/src/studio-home/__mocks__/studioHomeMock.js b/src/studio-home/__mocks__/studioHomeMock.js
index dcd313c511..cc135f259a 100644
--- a/src/studio-home/__mocks__/studioHomeMock.js
+++ b/src/studio-home/__mocks__/studioHomeMock.js
@@ -76,4 +76,5 @@ module.exports = {
platformName: 'Your Platform Name Here',
userIsActive: true,
allowToCreateNewOrg: false,
+ allowedOrganizationsForLibraries: ['org1'],
};