Skip to content

Commit f1bb5d8

Browse files
committed
feat: Add ability to create Legacy Libraries
1 parent 9f16041 commit f1bb5d8

11 files changed

Lines changed: 548 additions & 9 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+
const realWindowLocationAssign = window.location.assign;
20+
let axiosMock: MockAdapter;
21+
22+
jest.mock('react-router-dom', () => ({
23+
...jest.requireActual('react-router-dom'),
24+
useNavigate: () => mockNavigate,
25+
}));
26+
27+
jest.mock('@src/generic/data/apiHooks', () => ({
28+
...jest.requireActual('@src/generic/data/apiHooks'),
29+
useOrganizationListData: () => ({
30+
data: ['org1', 'org2', 'org3', 'org4', 'org5'],
31+
isLoading: false,
32+
}),
33+
}));
34+
35+
describe('<CreateLegacyLibrary />', () => {
36+
beforeEach(() => {
37+
axiosMock = initializeMocks().axiosMock;
38+
axiosMock
39+
.onGet(getApiWaffleFlagsUrl(undefined))
40+
.reply(200, {});
41+
Object.defineProperty(window, 'location', {
42+
value: { assign: jest.fn() },
43+
});
44+
});
45+
46+
afterEach(() => {
47+
jest.clearAllMocks();
48+
axiosMock.restore();
49+
window.location.assign = realWindowLocationAssign;
50+
});
51+
52+
test('call api data with correct data', async () => {
53+
const user = userEvent.setup();
54+
axiosMock.onGet(getStudioHomeApiUrl()).reply(200, studioHomeMock);
55+
axiosMock.onPost(getContentLibraryV1CreateApiUrl()).reply(200, {
56+
id: 'library-id',
57+
url: '/library/library-id',
58+
});
59+
60+
render(<CreateLegacyLibrary />);
61+
62+
const titleInput = await screen.findByRole('textbox', { name: /library name/i });
63+
await user.click(titleInput);
64+
await user.type(titleInput, 'Test Library Name');
65+
66+
const orgInput = await screen.findByRole('combobox', { name: /organization/i });
67+
await user.click(orgInput);
68+
await user.type(orgInput, 'org1');
69+
await user.tab();
70+
71+
const slugInput = await screen.findByRole('textbox', { name: /library id/i });
72+
await user.click(slugInput);
73+
await user.type(slugInput, 'test_library_slug');
74+
75+
fireEvent.click(await screen.findByRole('button', { name: /create/i }));
76+
await waitFor(() => {
77+
expect(axiosMock.history.post.length).toBe(1);
78+
expect(axiosMock.history.post[0].data).toBe(
79+
'{"display_name":"Test Library Name","org":"org1","number":"test_library_slug"}',
80+
);
81+
expect(window.location.assign).toHaveBeenCalledWith('http://localhost:18010/library/library-id');
82+
});
83+
});
84+
85+
test('cannot create new org unless allowed', async () => {
86+
const user = userEvent.setup();
87+
axiosMock.onGet(getStudioHomeApiUrl()).reply(200, studioHomeMock);
88+
axiosMock.onPost(getContentLibraryV1CreateApiUrl()).reply(200, {
89+
id: 'library-id',
90+
url: '/library/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+
url: '/library/library-id',
130+
});
131+
132+
render(<CreateLegacyLibrary />);
133+
134+
const titleInput = await screen.findByRole('textbox', { name: /library name/i });
135+
await user.click(titleInput);
136+
await user.type(titleInput, 'Test Library Name');
137+
138+
// We can create a new org, so we're also allowed to use any existing org
139+
const orgOptions = screen.getByTestId('autosuggest-iconbutton');
140+
await user.click(orgOptions);
141+
['org1', 'org2', 'org3', 'org4', 'org5'].forEach((org) => expect(screen.queryByText(org)).toBeInTheDocument());
142+
143+
const orgInput = await screen.findByRole('combobox', { name: /organization/i });
144+
await user.click(orgInput);
145+
await user.type(orgInput, 'NewOrg');
146+
await user.tab();
147+
148+
const slugInput = await screen.findByRole('textbox', { name: /library id/i });
149+
await user.click(slugInput);
150+
await user.type(slugInput, 'test_library_slug');
151+
152+
fireEvent.click(await screen.findByRole('button', { name: /create/i }));
153+
await waitFor(() => {
154+
expect(axiosMock.history.post.length).toBe(1);
155+
expect(axiosMock.history.post[0].data).toBe(
156+
'{"display_name":"Test Library Name","org":"NewOrg","number":"test_library_slug"}',
157+
);
158+
expect(window.location.assign).toHaveBeenCalledWith('http://localhost:18010/library/library-id');
159+
});
160+
});
161+
162+
test('show api error', async () => {
163+
const user = userEvent.setup();
164+
axiosMock.onGet(getStudioHomeApiUrl()).reply(200, studioHomeMock);
165+
axiosMock.onPost(getContentLibraryV1CreateApiUrl()).reply(400, {
166+
field: 'Error message',
167+
});
168+
render(<CreateLegacyLibrary />);
169+
170+
const titleInput = await screen.findByRole('textbox', { name: /library name/i });
171+
await user.click(titleInput);
172+
await user.type(titleInput, 'Test Library Name');
173+
174+
const orgInput = await screen.findByRole('combobox', { name: /organization/i });
175+
await user.click(orgInput);
176+
await user.type(orgInput, 'org1');
177+
await user.tab();
178+
179+
const slugInput = await screen.findByRole('textbox', { name: /library id/i });
180+
await user.click(slugInput);
181+
await user.type(slugInput, 'test_library_slug');
182+
183+
await user.click(await screen.findByRole('button', { name: /create/i }));
184+
await waitFor(async () => {
185+
expect(axiosMock.history.post.length).toBe(1);
186+
expect(axiosMock.history.post[0].data).toBe(
187+
'{"display_name":"Test Library Name","org":"org1","number":"test_library_slug"}',
188+
);
189+
expect(mockNavigate).not.toHaveBeenCalled();
190+
});
191+
await screen.findByText('Request failed with status code 400');
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)