Skip to content

Commit 0688d52

Browse files
committed
feat: Add ability to create Legacy Libraries
1 parent 191be55 commit 0688d52

10 files changed

Lines changed: 543 additions & 2 deletions

File tree

src/index.jsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import messages from './i18n';
1919
import {
2020
ComponentPicker,
2121
CreateLibrary,
22+
CreateLegacyLibrary,
2223
LibraryLayout,
2324
PreviewChangesEmbed,
2425
} from './library-authoring';
@@ -67,6 +68,7 @@ const App = () => {
6768
<Route path="/libraries" element={<StudioHome />} />
6869
<Route path="/libraries-v1" element={<StudioHome />} />
6970
<Route path="/libraries-v1/migrate" element={<LegacyLibMigrationPage />} />
71+
<Route path="/libraries-v1/create" element={<CreateLegacyLibrary />} />
7072
<Route path="/library/create" element={<CreateLibrary />} />
7173
<Route path="/library/:libraryId/*" element={<LibraryLayout />} />
7274
<Route
Lines changed: 195 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,195 @@
1+
import React from 'react';
2+
import type MockAdapter from 'axios-mock-adapter';
3+
import userEvent from '@testing-library/user-event';
4+
5+
import {
6+
fireEvent,
7+
initializeMocks,
8+
render,
9+
screen,
10+
waitFor,
11+
} from '@src/testUtils';
12+
import studioHomeMock from '@src/studio-home/__mocks__/studioHomeMock';
13+
import { getStudioHomeApiUrl } from '@src/studio-home/data/api';
14+
import { getApiWaffleFlagsUrl } from '@src/data/api';
15+
import { CreateLibrary } from '.';
16+
import { getContentLibraryV2CreateApiUrl } from './data/api';
17+
18+
const mockNavigate = jest.fn();
19+
let axiosMock: MockAdapter;
20+
21+
jest.mock('react-router-dom', () => ({
22+
...jest.requireActual('react-router-dom'),
23+
useNavigate: () => mockNavigate,
24+
}));
25+
26+
jest.mock('@src/generic/data/apiHooks', () => ({
27+
...jest.requireActual('@src/generic/data/apiHooks'),
28+
useOrganizationListData: () => ({
29+
data: ['org1', 'org2', 'org3', 'org4', 'org5'],
30+
isLoading: false,
31+
}),
32+
}));
33+
34+
describe('<CreateLibrary />', () => {
35+
beforeEach(() => {
36+
axiosMock = initializeMocks().axiosMock;
37+
axiosMock
38+
.onGet(getApiWaffleFlagsUrl(undefined))
39+
.reply(200, {});
40+
});
41+
42+
afterEach(() => {
43+
jest.clearAllMocks();
44+
axiosMock.restore();
45+
});
46+
47+
test('call api data with correct data', async () => {
48+
const user = userEvent.setup();
49+
axiosMock.onGet(getStudioHomeApiUrl()).reply(200, studioHomeMock);
50+
axiosMock.onPost(getContentLibraryV2CreateApiUrl()).reply(200, {
51+
id: 'library-id',
52+
});
53+
54+
render(<CreateLibrary />);
55+
56+
const titleInput = await screen.findByRole('textbox', { name: /library name/i });
57+
await user.click(titleInput);
58+
await user.type(titleInput, 'Test Library Name');
59+
60+
const orgInput = await screen.findByRole('combobox', { name: /organization/i });
61+
await user.click(orgInput);
62+
await user.type(orgInput, 'org1');
63+
await user.tab();
64+
65+
const slugInput = await screen.findByRole('textbox', { name: /library id/i });
66+
await user.click(slugInput);
67+
await user.type(slugInput, 'test_library_slug');
68+
69+
fireEvent.click(await screen.findByRole('button', { name: /create/i }));
70+
await waitFor(() => {
71+
expect(axiosMock.history.post.length).toBe(1);
72+
expect(axiosMock.history.post[0].data).toBe(
73+
'{"description":"","title":"Test Library Name","org":"org1","slug":"test_library_slug"}',
74+
);
75+
expect(mockNavigate).toHaveBeenCalledWith('/library/library-id');
76+
});
77+
});
78+
79+
test('cannot create new org unless allowed', async () => {
80+
const user = userEvent.setup();
81+
axiosMock.onGet(getStudioHomeApiUrl()).reply(200, studioHomeMock);
82+
axiosMock.onPost(getContentLibraryV2CreateApiUrl()).reply(200, {
83+
id: 'library-id',
84+
});
85+
86+
render(<CreateLibrary />);
87+
88+
const titleInput = await screen.findByRole('textbox', { name: /library name/i });
89+
await user.click(titleInput);
90+
await user.type(titleInput, 'Test Library Name');
91+
92+
// We cannot create a new org, and so we're restricted to the allowed list
93+
const orgOptions = screen.getByTestId('autosuggest-iconbutton');
94+
await user.click(orgOptions);
95+
expect(screen.getByText('org1')).toBeInTheDocument();
96+
['org2', 'org3', 'org4', 'org5'].forEach((org) => expect(screen.queryByText(org)).not.toBeInTheDocument());
97+
98+
const orgInput = await screen.findByRole('combobox', { name: /organization/i });
99+
await user.click(orgInput);
100+
await user.type(orgInput, 'NewOrg');
101+
await user.tab();
102+
103+
const slugInput = await screen.findByRole('textbox', { name: /library id/i });
104+
await user.click(slugInput);
105+
await user.type(slugInput, 'test_library_slug');
106+
107+
fireEvent.click(await screen.findByRole('button', { name: /create/i }));
108+
await waitFor(() => {
109+
expect(axiosMock.history.post.length).toBe(0);
110+
});
111+
expect(await screen.findByText('Required field.')).toBeInTheDocument();
112+
});
113+
114+
test('can create new org if allowed', async () => {
115+
const user = userEvent.setup();
116+
axiosMock.onGet(getStudioHomeApiUrl()).reply(200, {
117+
...studioHomeMock,
118+
allow_to_create_new_org: true,
119+
});
120+
axiosMock.onPost(getContentLibraryV2CreateApiUrl()).reply(200, {
121+
id: 'library-id',
122+
});
123+
124+
render(<CreateLibrary />);
125+
126+
const titleInput = await screen.findByRole('textbox', { name: /library name/i });
127+
await user.click(titleInput);
128+
await user.type(titleInput, 'Test Library Name');
129+
130+
// We can create a new org, so we're also allowed to use any existing org
131+
const orgOptions = screen.getByTestId('autosuggest-iconbutton');
132+
await user.click(orgOptions);
133+
['org1', 'org2', 'org3', 'org4', 'org5'].forEach((org) => expect(screen.queryByText(org)).toBeInTheDocument());
134+
135+
const orgInput = await screen.findByRole('combobox', { name: /organization/i });
136+
await user.click(orgInput);
137+
await user.type(orgInput, 'NewOrg');
138+
await user.tab();
139+
140+
const slugInput = await screen.findByRole('textbox', { name: /library id/i });
141+
await user.click(slugInput);
142+
await user.type(slugInput, 'test_library_slug');
143+
144+
fireEvent.click(await screen.findByRole('button', { name: /create/i }));
145+
await waitFor(() => {
146+
expect(axiosMock.history.post.length).toBe(1);
147+
expect(axiosMock.history.post[0].data).toBe(
148+
'{"description":"","title":"Test Library Name","org":"NewOrg","slug":"test_library_slug"}',
149+
);
150+
expect(mockNavigate).toHaveBeenCalledWith('/library/library-id');
151+
});
152+
});
153+
154+
test('show api error', async () => {
155+
const user = userEvent.setup();
156+
axiosMock.onGet(getStudioHomeApiUrl()).reply(200, studioHomeMock);
157+
axiosMock.onPost(getContentLibraryV2CreateApiUrl()).reply(400, {
158+
field: 'Error message',
159+
});
160+
render(<CreateLibrary />);
161+
162+
const titleInput = await screen.findByRole('textbox', { name: /library name/i });
163+
await user.click(titleInput);
164+
await user.type(titleInput, 'Test Library Name');
165+
166+
const orgInput = await screen.findByTestId('autosuggest-textbox-input');
167+
await user.click(orgInput);
168+
await user.type(orgInput, 'org1');
169+
await user.tab();
170+
171+
const slugInput = await screen.findByRole('textbox', { name: /library id/i });
172+
await user.click(slugInput);
173+
await user.type(slugInput, 'test_library_slug');
174+
175+
fireEvent.click(await screen.findByRole('button', { name: /create/i }));
176+
await waitFor(async () => {
177+
expect(axiosMock.history.post.length).toBe(1);
178+
expect(axiosMock.history.post[0].data).toBe(
179+
'{"description":"","title":"Test Library Name","org":"org1","slug":"test_library_slug"}',
180+
);
181+
expect(mockNavigate).not.toHaveBeenCalled();
182+
const errorMessage = 'Request failed with status code 400{ "field": "Error message" }';
183+
expect(await screen.findByRole('alert')).toHaveTextContent(errorMessage);
184+
});
185+
});
186+
187+
test('cancel creating library navigates to libraries page', async () => {
188+
render(<CreateLibrary />);
189+
190+
fireEvent.click(await screen.findByRole('button', { name: /cancel/i }));
191+
await waitFor(() => {
192+
expect(mockNavigate).toHaveBeenCalledWith('/libraries');
193+
});
194+
});
195+
});

0 commit comments

Comments
 (0)