Skip to content

Commit 93e9a8a

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

10 files changed

Lines changed: 546 additions & 3 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: 202 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,202 @@
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 { CreateLegacyLibrary } from '.';
16+
import { getContentLibraryV1CreateApiUrl } 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('<CreateLegacyLibrary />', () => {
35+
const windowLocationAssign = window.location.assign;
36+
const mockWindowLocationAssign = jest.fn();
37+
38+
beforeEach(() => {
39+
axiosMock = initializeMocks().axiosMock;
40+
axiosMock
41+
.onGet(getApiWaffleFlagsUrl(undefined))
42+
.reply(200, {});
43+
Object.defineProperty(window, 'location', {
44+
value: { assign: mockWindowLocationAssign }
45+
});
46+
});
47+
48+
afterEach(() => {
49+
jest.clearAllMocks();
50+
axiosMock.restore();
51+
window.location.assign = windowLocationAssign;
52+
});
53+
54+
test('call api data with correct data', async () => {
55+
const user = userEvent.setup();
56+
axiosMock.onGet(getStudioHomeApiUrl()).reply(200, studioHomeMock);
57+
axiosMock.onPost(getContentLibraryV1CreateApiUrl()).reply(200, {
58+
id: 'library-id',
59+
});
60+
61+
render(<CreateLegacyLibrary />);
62+
63+
const titleInput = await screen.findByRole('textbox', { name: /library name/i });
64+
await user.click(titleInput);
65+
await user.type(titleInput, 'Test Library Name');
66+
67+
const orgInput = await screen.findByRole('combobox', { name: /organization/i });
68+
await user.click(orgInput);
69+
await user.type(orgInput, 'org1');
70+
await user.tab();
71+
72+
const slugInput = await screen.findByRole('textbox', { name: /library id/i });
73+
await user.click(slugInput);
74+
await user.type(slugInput, 'test_library_slug');
75+
76+
fireEvent.click(await screen.findByRole('button', { name: /create/i }));
77+
await waitFor(() => {
78+
expect(axiosMock.history.post.length).toBe(1);
79+
expect(axiosMock.history.post[0].data).toBe(
80+
'{"display_name":"Test Library Name","org":"org1","number":"test_library_slug"}',
81+
);
82+
expect(mockWindowLocationAssign).toHaveBeenCalledWith('http://localhost:18010/library/library-id');
83+
});
84+
});
85+
86+
test('cannot create new org unless allowed', async () => {
87+
const user = userEvent.setup();
88+
axiosMock.onGet(getStudioHomeApiUrl()).reply(200, studioHomeMock);
89+
axiosMock.onPost(getContentLibraryV1CreateApiUrl()).reply(200, {
90+
id: 'library-id',
91+
});
92+
93+
render(<CreateLegacyLibrary />);
94+
95+
const titleInput = await screen.findByRole('textbox', { name: /library name/i });
96+
await user.click(titleInput);
97+
await user.type(titleInput, 'Test Library Name');
98+
99+
// We cannot create a new org, and so we're restricted to the allowed list
100+
const orgOptions = screen.getByTestId('autosuggest-iconbutton');
101+
await user.click(orgOptions);
102+
expect(screen.getByText('org1')).toBeInTheDocument();
103+
['org2', 'org3', 'org4', 'org5'].forEach((org) => expect(screen.queryByText(org)).not.toBeInTheDocument());
104+
105+
const orgInput = await screen.findByRole('combobox', { name: /organization/i });
106+
await user.click(orgInput);
107+
await user.type(orgInput, 'NewOrg');
108+
await user.tab();
109+
110+
const slugInput = await screen.findByRole('textbox', { name: /library id/i });
111+
await user.click(slugInput);
112+
await user.type(slugInput, 'test_library_slug');
113+
114+
fireEvent.click(await screen.findByRole('button', { name: /create/i }));
115+
await waitFor(() => {
116+
expect(axiosMock.history.post.length).toBe(0);
117+
});
118+
expect(await screen.findByText('Required field.')).toBeInTheDocument();
119+
});
120+
121+
test('can create new org if allowed', async () => {
122+
const user = userEvent.setup();
123+
axiosMock.onGet(getStudioHomeApiUrl()).reply(200, {
124+
...studioHomeMock,
125+
allow_to_create_new_org: true,
126+
});
127+
axiosMock.onPost(getContentLibraryV1CreateApiUrl()).reply(200, {
128+
id: 'library-id',
129+
});
130+
131+
render(<CreateLegacyLibrary />);
132+
133+
const titleInput = await screen.findByRole('textbox', { name: /library name/i });
134+
await user.click(titleInput);
135+
await user.type(titleInput, 'Test Library Name');
136+
137+
// We can create a new org, so we're also allowed to use any existing org
138+
const orgOptions = screen.getByTestId('autosuggest-iconbutton');
139+
await user.click(orgOptions);
140+
['org1', 'org2', 'org3', 'org4', 'org5'].forEach((org) => expect(screen.queryByText(org)).toBeInTheDocument());
141+
142+
const orgInput = await screen.findByRole('combobox', { name: /organization/i });
143+
await user.click(orgInput);
144+
await user.type(orgInput, 'NewOrg');
145+
await user.tab();
146+
147+
const slugInput = await screen.findByRole('textbox', { name: /library id/i });
148+
await user.click(slugInput);
149+
await user.type(slugInput, 'test_library_slug');
150+
151+
fireEvent.click(await screen.findByRole('button', { name: /create/i }));
152+
await waitFor(() => {
153+
expect(axiosMock.history.post.length).toBe(1);
154+
expect(axiosMock.history.post[0].data).toBe(
155+
'{"display_name":"Test Library Name","org":"NewOrg","number":"test_library_slug"}',
156+
);
157+
expect(mockWindowLocationAssign).toHaveBeenCalledWith('http://localhost:18010/library/library-id');
158+
});
159+
});
160+
161+
test('show api error', async () => {
162+
const user = userEvent.setup();
163+
axiosMock.onGet(getStudioHomeApiUrl()).reply(200, studioHomeMock);
164+
axiosMock.onPost(getContentLibraryV1CreateApiUrl()).reply(400, {
165+
field: 'Error message',
166+
});
167+
render(<CreateLegacyLibrary />);
168+
169+
const titleInput = await screen.findByRole('textbox', { name: /library name/i });
170+
await user.click(titleInput);
171+
await user.type(titleInput, 'Test Library Name');
172+
173+
const orgInput = await screen.findByTestId('autosuggest-textbox-input');
174+
await user.click(orgInput);
175+
await user.type(orgInput, 'org1');
176+
await user.tab();
177+
178+
const slugInput = await screen.findByRole('textbox', { name: /library id/i });
179+
await user.click(slugInput);
180+
await user.type(slugInput, 'test_library_slug');
181+
182+
fireEvent.click(await screen.findByRole('button', { name: /create/i }));
183+
await waitFor(async () => {
184+
expect(axiosMock.history.post.length).toBe(1);
185+
expect(axiosMock.history.post[0].data).toBe(
186+
'{"display_name":"Test Library Name","org":"org1","number":"test_library_slug"}',
187+
);
188+
expect(mockNavigate).not.toHaveBeenCalled();
189+
const errorMessage = 'Request failed with status code 400{ "field": "Error message" }';
190+
expect(await screen.findByRole('alert')).toHaveTextContent(errorMessage);
191+
});
192+
});
193+
194+
test('cancel creating library navigates to libraries page', async () => {
195+
render(<CreateLegacyLibrary />);
196+
197+
fireEvent.click(await screen.findByRole('button', { name: /cancel/i }));
198+
await waitFor(() => {
199+
expect(mockNavigate).toHaveBeenCalledWith('/libraries-v1');
200+
});
201+
});
202+
});

0 commit comments

Comments
 (0)