@thepalaceproject/circulation-admin 1.43.0-post.2 → 1.43.0-post.20
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CLAUDE.md +26 -12
- package/README.md +11 -5
- package/dist/circulation-admin.js +1 -1
- package/jest.config.js +17 -18
- package/package.json +4 -22
- package/tests/jest/actions.test.ts +962 -0
- package/tests/jest/components/AccountPage.test.tsx +41 -0
- package/tests/jest/components/AdvancedSearchBooleanFilter.test.tsx +231 -0
- package/tests/jest/components/AdvancedSearchBuilder.test.tsx +93 -20
- package/tests/jest/components/AdvancedSearchFilter.test.tsx +142 -0
- package/tests/jest/components/AdvancedSearchFilterInput.test.tsx +212 -0
- package/tests/jest/components/AdvancedSearchFilterViewer.test.tsx +52 -0
- package/tests/jest/components/AdvancedSearchValueFilter.test.tsx +123 -0
- package/tests/jest/components/Announcement.test.tsx +76 -0
- package/tests/jest/components/AnnouncementForm.test.tsx +229 -0
- package/tests/jest/components/AnnouncementsSection.test.tsx +146 -0
- package/tests/jest/components/Autocomplete.test.tsx +67 -0
- package/tests/jest/components/BookCoverEditor.test.tsx +393 -0
- package/tests/jest/components/BookDetails.test.tsx +133 -0
- package/tests/jest/components/BookDetailsContainer.test.tsx +116 -0
- package/tests/jest/components/BookDetailsEditor.test.tsx +275 -0
- package/tests/jest/components/BookDetailsTabContainer.test.tsx +237 -0
- package/tests/jest/components/BookEditForm.test.tsx +352 -0
- package/tests/jest/components/BookEditor.test.tsx +20 -21
- package/tests/jest/components/CatalogPage.test.tsx +158 -0
- package/tests/jest/components/CatalogServices.test.tsx +100 -0
- package/tests/jest/components/ChangePasswordForm.test.tsx +163 -0
- package/tests/jest/components/CirculationEventsDownloadForm.test.tsx +41 -0
- package/tests/jest/components/Classifications.test.tsx +166 -0
- package/tests/jest/components/ClassificationsForm.test.tsx +458 -0
- package/tests/jest/components/ClassificationsTable.test.tsx +57 -0
- package/tests/jest/components/Collections.test.tsx +335 -7
- package/tests/jest/components/ColorPicker.test.tsx +58 -0
- package/tests/jest/components/ComplaintForm.test.tsx +171 -0
- package/tests/jest/components/Complaints.test.tsx +259 -0
- package/tests/jest/components/ConfigPage.test.tsx +58 -0
- package/tests/jest/components/ConfigTabContainer.test.tsx +196 -0
- package/tests/jest/components/ContextProvider.test.tsx +138 -0
- package/tests/jest/components/Contributors.test.tsx +157 -0
- package/tests/jest/components/CustomListEditor.test.tsx +367 -0
- package/tests/jest/components/CustomListEntriesEditor.test.tsx +1240 -0
- package/tests/jest/components/CustomListPage.test.tsx +57 -0
- package/tests/jest/components/CustomListSearch.test.tsx +275 -0
- package/tests/jest/components/CustomLists.test.tsx +621 -4
- package/tests/jest/components/CustomListsForBook.test.tsx +353 -0
- package/tests/jest/components/CustomListsSidebar.test.tsx +251 -0
- package/tests/jest/components/DashboardPage.test.tsx +76 -0
- package/tests/jest/components/DebugAuthentication.test.tsx +1 -5
- package/tests/jest/components/DiagnosticsServiceTabs.test.tsx +141 -0
- package/tests/jest/components/DiagnosticsServiceType.test.tsx +75 -0
- package/tests/jest/components/DiagnosticsTabContainer.test.tsx +147 -0
- package/tests/jest/components/DiscoveryServices.test.tsx +154 -7
- package/tests/jest/components/EditableConfigList.test.tsx +606 -9
- package/tests/jest/components/EditableInput.test.tsx +250 -1
- package/tests/jest/components/EditorField.test.tsx +95 -0
- package/tests/jest/components/EntryPointsContainer.test.tsx +97 -0
- package/tests/jest/components/EntryPointsTabs.test.tsx +88 -0
- package/tests/jest/components/ErrorMessage.test.tsx +97 -0
- package/tests/jest/components/Footer.test.tsx +141 -0
- package/tests/jest/components/GenreForm.test.tsx +152 -0
- package/tests/jest/components/Header.test.tsx +421 -0
- package/tests/jest/components/IndividualAdminEditForm.test.tsx +506 -1
- package/tests/jest/components/IndividualAdmins.test.tsx +124 -8
- package/tests/jest/components/InputList.test.tsx +493 -0
- package/tests/jest/components/InventoryReportRequestModal.test.tsx +16 -42
- package/tests/jest/components/Lane.test.tsx +132 -10
- package/tests/jest/components/LaneCustomListsEditor.test.tsx +450 -0
- package/tests/jest/components/LaneEditor.test.tsx +377 -13
- package/tests/jest/components/LanePage.test.tsx +68 -0
- package/tests/jest/components/Lanes.test.tsx +446 -0
- package/tests/jest/components/LanesSidebar.test.tsx +621 -0
- package/tests/jest/components/LanguageField.test.tsx +40 -0
- package/tests/jest/components/Libraries.test.tsx +48 -0
- package/tests/jest/components/LibraryEditForm.test.tsx +383 -0
- package/tests/jest/components/LibraryRegistration.test.tsx +354 -0
- package/tests/jest/components/LibraryRegistrationForm.test.tsx +203 -0
- package/tests/jest/components/LoadButton.test.tsx +49 -0
- package/tests/jest/components/ManagePatrons.test.tsx +73 -0
- package/tests/jest/components/ManagePatronsForm.test.tsx +180 -0
- package/tests/jest/components/ManagePatronsTabContainer.test.tsx +143 -0
- package/tests/jest/components/MetadataServices.test.tsx +95 -0
- package/tests/jest/components/NeighborhoodAnalyticsForm.test.tsx +167 -0
- package/tests/jest/components/PairedMenus.test.tsx +220 -0
- package/tests/jest/components/PatronAuthServices.test.tsx +114 -0
- package/tests/jest/components/PatronBlockingRulesEditor.test.tsx +3 -1
- package/tests/jest/components/PatronInfo.test.tsx +54 -0
- package/tests/jest/components/ProtocolFormField.test.tsx +443 -7
- package/tests/jest/components/ResetAdobeId.test.tsx +302 -0
- package/tests/jest/components/SaveButton.test.tsx +47 -0
- package/tests/jest/components/SelfTestResult.test.tsx +164 -0
- package/tests/jest/components/SelfTests.test.tsx +601 -0
- package/tests/jest/components/SelfTestsCategory.test.tsx +200 -0
- package/tests/jest/components/SelfTestsTabContainer.test.tsx +216 -0
- package/tests/jest/components/ServiceEditForm.test.tsx +1092 -0
- package/tests/jest/components/ServiceWithRegistrationsEditForm.test.tsx +110 -0
- package/tests/jest/components/SetupPage.test.tsx +41 -0
- package/tests/jest/components/SingleStatListItem.test.tsx +62 -0
- package/tests/jest/components/SitewideAnnouncements.test.tsx +134 -0
- package/tests/jest/components/Stats.test.tsx +4 -15
- package/tests/jest/components/TabContainer.test.tsx +68 -0
- package/tests/jest/components/TextWithEditMode.test.tsx +218 -0
- package/tests/jest/components/Timestamp.test.tsx +74 -0
- package/tests/jest/components/ToolTip.test.tsx +46 -0
- package/tests/jest/components/TroubleshootingCategoryPage.test.tsx +69 -0
- package/tests/jest/components/TroubleshootingPage.test.tsx +94 -0
- package/tests/jest/components/TroubleshootingTabContainer.test.tsx +68 -0
- package/tests/jest/components/UpdatingLoader.test.tsx +36 -0
- package/tests/jest/components/WelcomePage.test.tsx +43 -0
- package/tests/jest/components/WithEditButton.test.tsx +55 -0
- package/tests/jest/components/WithRemoveButton.test.tsx +95 -0
- package/tests/jest/editorAdapter.test.ts +245 -0
- package/tests/jest/features/book.test.ts +75 -80
- package/tests/jest/fixtures/classificationsData.ts +45 -0
- package/tests/jest/fixtures/genreData.ts +811 -0
- package/tests/jest/index.test.tsx +50 -0
- package/tests/jest/models/Admin.test.ts +206 -0
- package/tests/jest/reducers/bookCoverPreview.test.ts +81 -0
- package/tests/jest/reducers/classifications.test.ts +161 -0
- package/tests/jest/reducers/collections.test.ts +96 -0
- package/tests/jest/reducers/complaints.test.ts +166 -0
- package/tests/jest/reducers/createFetchEditReducer.test.ts +220 -0
- package/tests/jest/reducers/createRegisterLibraryReducer.test.ts +51 -0
- package/tests/jest/reducers/customListDetails.test.ts +120 -0
- package/tests/jest/reducers/customListEditor.test.ts +2823 -0
- package/tests/jest/reducers/managePatrons.test.ts +71 -0
- package/tests/jest/testUtils/formDataShim.test.ts +135 -0
- package/tests/jest/testUtils/formDataShim.ts +112 -0
- package/tests/jest/utils/sharedFunctions.test.ts +121 -0
- package/tsconfig.json +3 -4
- package/.nycrc.json +0 -8
- package/testReporter.js +0 -29
- package/tests/jest/README.md +0 -6
|
@@ -1,33 +1,75 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
|
-
import { render, screen } from "@testing-library/react";
|
|
3
|
-
import
|
|
2
|
+
import { render, screen, waitFor, within } from "@testing-library/react";
|
|
3
|
+
import userEvent from "@testing-library/user-event";
|
|
4
4
|
import { LaneData } from "../../../src/interfaces";
|
|
5
5
|
import LaneEditor from "../../../src/components/LaneEditor";
|
|
6
|
+
import * as navigate from "../../../src/utils/navigate";
|
|
6
7
|
|
|
7
8
|
// Mock the LaneCustomListsEditor so we can verify that it is or isn't rendered. This serves as an
|
|
8
9
|
// example of how to do something analogous to Enzyme's shallow rendering, when we don't want/need
|
|
9
10
|
// to render the whole component tree down to HTML elements to test something. This technique is
|
|
10
11
|
// useful for testing components in isolation (unit testing), instead of the integration testing
|
|
11
12
|
// that RTL focuses on.
|
|
13
|
+
//
|
|
14
|
+
// The mock is a class component (not a bare function) so that LaneEditor's ref to
|
|
15
|
+
// it resolves to an instance exposing the imperative API that `save()`/`reset()`
|
|
16
|
+
// call (`getCustomListIds()` / `reset()`). It also renders its `filteredCustomLists`
|
|
17
|
+
// and a few buttons that invoke the `changeFilter` / `onUpdate` callbacks, so tests
|
|
18
|
+
// can drive the parent's filtering and custom-list-selection behavior without the
|
|
19
|
+
// real drag-and-drop editor.
|
|
12
20
|
|
|
13
21
|
jest.mock("../../../src/components/LaneCustomListsEditor", () => ({
|
|
14
22
|
__esModule: true,
|
|
15
|
-
default:
|
|
23
|
+
default: class MockLaneCustomListsEditor extends React.Component<any> {
|
|
24
|
+
getCustomListIds() {
|
|
25
|
+
return this.props.customListIds;
|
|
26
|
+
}
|
|
27
|
+
reset() {
|
|
28
|
+
// No-op stand-in; its presence lets LaneEditor.reset() call it via the ref.
|
|
29
|
+
}
|
|
30
|
+
render() {
|
|
31
|
+
const { filteredCustomLists = [], changeFilter, onUpdate } = this.props;
|
|
32
|
+
return (
|
|
33
|
+
<div data-testid="LaneCustomListsEditor">
|
|
34
|
+
<ul data-testid="filtered-custom-lists">
|
|
35
|
+
{filteredCustomLists.map((list: any) => (
|
|
36
|
+
<li key={list.id}>{list.name}</li>
|
|
37
|
+
))}
|
|
38
|
+
</ul>
|
|
39
|
+
<button type="button" onClick={() => changeFilter("shared-in")}>
|
|
40
|
+
filter shared-in
|
|
41
|
+
</button>
|
|
42
|
+
<button type="button" onClick={() => changeFilter("shared-out")}>
|
|
43
|
+
filter shared-out
|
|
44
|
+
</button>
|
|
45
|
+
<button type="button" onClick={() => changeFilter("")}>
|
|
46
|
+
filter all
|
|
47
|
+
</button>
|
|
48
|
+
<button type="button" onClick={() => onUpdate([1, 2])}>
|
|
49
|
+
set lists 1 2
|
|
50
|
+
</button>
|
|
51
|
+
<button type="button" onClick={() => onUpdate([99])}>
|
|
52
|
+
set lists 99
|
|
53
|
+
</button>
|
|
54
|
+
</div>
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
},
|
|
16
58
|
}));
|
|
17
59
|
|
|
18
60
|
const customListsData = [
|
|
19
61
|
{ id: 1, name: "list 1", entries: [], is_owner: true, is_shared: false },
|
|
20
62
|
];
|
|
21
63
|
|
|
22
|
-
const editLane =
|
|
23
|
-
|
|
24
|
-
);
|
|
64
|
+
const editLane = jest
|
|
65
|
+
.fn()
|
|
66
|
+
.mockReturnValue(new Promise<void>((resolve) => resolve()));
|
|
25
67
|
|
|
26
|
-
const deleteLane =
|
|
27
|
-
|
|
28
|
-
);
|
|
68
|
+
const deleteLane = jest
|
|
69
|
+
.fn()
|
|
70
|
+
.mockReturnValue(new Promise<void>((resolve) => resolve()));
|
|
29
71
|
|
|
30
|
-
const toggleLaneVisibility =
|
|
72
|
+
const toggleLaneVisibility = jest.fn();
|
|
31
73
|
|
|
32
74
|
function createLaneData(displayName: string, isAutomated: boolean): LaneData {
|
|
33
75
|
return {
|
|
@@ -65,7 +107,7 @@ describe("LaneEditor", () => {
|
|
|
65
107
|
editOrCreate="edit"
|
|
66
108
|
editLane={editLane}
|
|
67
109
|
deleteLane={deleteLane}
|
|
68
|
-
findParentOfLane={
|
|
110
|
+
findParentOfLane={jest.fn().mockReturnValue(laneData)}
|
|
69
111
|
toggleLaneVisibility={toggleLaneVisibility}
|
|
70
112
|
/>
|
|
71
113
|
);
|
|
@@ -100,7 +142,7 @@ describe("LaneEditor", () => {
|
|
|
100
142
|
editOrCreate="edit"
|
|
101
143
|
editLane={editLane}
|
|
102
144
|
deleteLane={deleteLane}
|
|
103
|
-
findParentOfLane={
|
|
145
|
+
findParentOfLane={jest.fn().mockReturnValue(laneData)}
|
|
104
146
|
toggleLaneVisibility={toggleLaneVisibility}
|
|
105
147
|
/>
|
|
106
148
|
);
|
|
@@ -138,7 +180,7 @@ describe("LaneEditor", () => {
|
|
|
138
180
|
editOrCreate="edit"
|
|
139
181
|
editLane={editLane}
|
|
140
182
|
deleteLane={deleteLane}
|
|
141
|
-
findParentOfLane={
|
|
183
|
+
findParentOfLane={jest.fn().mockReturnValue(laneData)}
|
|
142
184
|
toggleLaneVisibility={toggleLaneVisibility}
|
|
143
185
|
/>
|
|
144
186
|
);
|
|
@@ -146,3 +188,325 @@ describe("LaneEditor", () => {
|
|
|
146
188
|
expect(screen.queryByTestId("LaneCustomListsEditor")).toBeNull();
|
|
147
189
|
});
|
|
148
190
|
});
|
|
191
|
+
|
|
192
|
+
// These drive the real name field and the mocked custom-lists editor's callbacks,
|
|
193
|
+
// asserting on the resulting DOM and on the props passed to the injected
|
|
194
|
+
// `editLane` / `deleteLane` / `toggleLaneVisibility` handlers. Navigation is
|
|
195
|
+
// asserted by spying on `navigate.navigateTo`.
|
|
196
|
+
describe("LaneEditor — name, list editing, and actions", () => {
|
|
197
|
+
const twoLists = [
|
|
198
|
+
{ id: 1, name: "list 1", entries: [], is_owner: true, is_shared: false },
|
|
199
|
+
{ id: 2, name: "list 2", entries: [], is_owner: false, is_shared: true },
|
|
200
|
+
];
|
|
201
|
+
|
|
202
|
+
const resolved = () => jest.fn().mockResolvedValue(undefined);
|
|
203
|
+
|
|
204
|
+
it("shows the lane name, id, and visible status", () => {
|
|
205
|
+
const lane = createLaneData("Custom Lane", false);
|
|
206
|
+
render(
|
|
207
|
+
<LaneEditor
|
|
208
|
+
library="library"
|
|
209
|
+
lane={lane}
|
|
210
|
+
customLists={customListsData}
|
|
211
|
+
editOrCreate="edit"
|
|
212
|
+
editLane={resolved()}
|
|
213
|
+
deleteLane={resolved()}
|
|
214
|
+
findParentOfLane={() => null}
|
|
215
|
+
toggleLaneVisibility={resolved()}
|
|
216
|
+
/>
|
|
217
|
+
);
|
|
218
|
+
|
|
219
|
+
expect(screen.getByText("Custom Lane")).toBeInTheDocument();
|
|
220
|
+
expect(screen.getByText("ID-1")).toBeInTheDocument();
|
|
221
|
+
expect(screen.getByText(/currently visible/i)).toBeInTheDocument();
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
it("updates the visible status when the lane prop changes", () => {
|
|
225
|
+
const visibleLane = createLaneData("Lane", false);
|
|
226
|
+
const hiddenLane = { ...visibleLane, visible: false };
|
|
227
|
+
const shared = {
|
|
228
|
+
library: "library",
|
|
229
|
+
customLists: customListsData,
|
|
230
|
+
editOrCreate: "edit",
|
|
231
|
+
editLane: resolved(),
|
|
232
|
+
deleteLane: resolved(),
|
|
233
|
+
findParentOfLane: () => null,
|
|
234
|
+
toggleLaneVisibility: resolved(),
|
|
235
|
+
};
|
|
236
|
+
|
|
237
|
+
const { rerender } = render(<LaneEditor {...shared} lane={visibleLane} />);
|
|
238
|
+
expect(screen.getByText(/currently visible/i)).toBeInTheDocument();
|
|
239
|
+
|
|
240
|
+
rerender(<LaneEditor {...shared} lane={hiddenLane} />);
|
|
241
|
+
expect(screen.getByText(/currently hidden/i)).toBeInTheDocument();
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
it("shows when the lane's parent is hidden", () => {
|
|
245
|
+
const hiddenLane = { ...createLaneData("Lane", false), visible: false };
|
|
246
|
+
const hiddenParent = {
|
|
247
|
+
...createLaneData("Parent", false),
|
|
248
|
+
id: 5,
|
|
249
|
+
visible: false,
|
|
250
|
+
};
|
|
251
|
+
render(
|
|
252
|
+
<LaneEditor
|
|
253
|
+
library="library"
|
|
254
|
+
lane={hiddenLane}
|
|
255
|
+
customLists={customListsData}
|
|
256
|
+
editOrCreate="edit"
|
|
257
|
+
editLane={resolved()}
|
|
258
|
+
deleteLane={resolved()}
|
|
259
|
+
findParentOfLane={() => hiddenParent}
|
|
260
|
+
toggleLaneVisibility={resolved()}
|
|
261
|
+
/>
|
|
262
|
+
);
|
|
263
|
+
|
|
264
|
+
expect(screen.getByText(/parent is currently hidden/i)).toBeInTheDocument();
|
|
265
|
+
});
|
|
266
|
+
|
|
267
|
+
it("toggles the inherit-parent-restrictions checkbox", async () => {
|
|
268
|
+
const user = userEvent.setup();
|
|
269
|
+
const lane = createLaneData("Custom Lane", false);
|
|
270
|
+
const parent = createLaneData("Parent", false);
|
|
271
|
+
render(
|
|
272
|
+
<LaneEditor
|
|
273
|
+
library="library"
|
|
274
|
+
lane={lane}
|
|
275
|
+
customLists={customListsData}
|
|
276
|
+
editOrCreate="edit"
|
|
277
|
+
editLane={resolved()}
|
|
278
|
+
deleteLane={resolved()}
|
|
279
|
+
findParentOfLane={() => parent}
|
|
280
|
+
toggleLaneVisibility={resolved()}
|
|
281
|
+
/>
|
|
282
|
+
);
|
|
283
|
+
|
|
284
|
+
const checkbox = screen.getByRole("checkbox", {
|
|
285
|
+
name: /inherit restrictions from parent/i,
|
|
286
|
+
});
|
|
287
|
+
expect(checkbox).toBeChecked();
|
|
288
|
+
expect(
|
|
289
|
+
screen.queryByRole("button", { name: /cancel changes/i })
|
|
290
|
+
).toBeNull();
|
|
291
|
+
|
|
292
|
+
await user.click(checkbox);
|
|
293
|
+
|
|
294
|
+
expect(checkbox).not.toBeChecked();
|
|
295
|
+
expect(
|
|
296
|
+
screen.getByRole("button", { name: /cancel changes/i })
|
|
297
|
+
).toBeInTheDocument();
|
|
298
|
+
});
|
|
299
|
+
|
|
300
|
+
it("filters the custom lists shown in the editor", async () => {
|
|
301
|
+
const user = userEvent.setup();
|
|
302
|
+
const lane = createLaneData("Custom Lane", false);
|
|
303
|
+
render(
|
|
304
|
+
<LaneEditor
|
|
305
|
+
library="library"
|
|
306
|
+
lane={lane}
|
|
307
|
+
customLists={twoLists}
|
|
308
|
+
editOrCreate="edit"
|
|
309
|
+
editLane={resolved()}
|
|
310
|
+
deleteLane={resolved()}
|
|
311
|
+
findParentOfLane={() => null}
|
|
312
|
+
toggleLaneVisibility={resolved()}
|
|
313
|
+
/>
|
|
314
|
+
);
|
|
315
|
+
|
|
316
|
+
const filtered = () => within(screen.getByTestId("filtered-custom-lists"));
|
|
317
|
+
|
|
318
|
+
// The default filter is "owned".
|
|
319
|
+
expect(filtered().getByText("list 1")).toBeInTheDocument();
|
|
320
|
+
expect(filtered().queryByText("list 2")).toBeNull();
|
|
321
|
+
|
|
322
|
+
await user.click(screen.getByRole("button", { name: /filter shared-in/i }));
|
|
323
|
+
expect(filtered().getByText("list 2")).toBeInTheDocument();
|
|
324
|
+
expect(filtered().queryByText("list 1")).toBeNull();
|
|
325
|
+
|
|
326
|
+
// "shared-out" keeps only owned-and-shared lists; neither qualifies.
|
|
327
|
+
await user.click(
|
|
328
|
+
screen.getByRole("button", { name: /filter shared-out/i })
|
|
329
|
+
);
|
|
330
|
+
expect(filtered().queryByText("list 1")).toBeNull();
|
|
331
|
+
expect(filtered().queryByText("list 2")).toBeNull();
|
|
332
|
+
|
|
333
|
+
await user.click(screen.getByRole("button", { name: /filter all/i }));
|
|
334
|
+
expect(filtered().getByText("list 1")).toBeInTheDocument();
|
|
335
|
+
expect(filtered().getByText("list 2")).toBeInTheDocument();
|
|
336
|
+
});
|
|
337
|
+
|
|
338
|
+
it("marks changes when the selected custom lists change", async () => {
|
|
339
|
+
const user = userEvent.setup();
|
|
340
|
+
const lane = createLaneData("Custom Lane", false);
|
|
341
|
+
render(
|
|
342
|
+
<LaneEditor
|
|
343
|
+
library="library"
|
|
344
|
+
lane={lane}
|
|
345
|
+
customLists={twoLists}
|
|
346
|
+
editOrCreate="edit"
|
|
347
|
+
editLane={resolved()}
|
|
348
|
+
deleteLane={resolved()}
|
|
349
|
+
findParentOfLane={() => null}
|
|
350
|
+
toggleLaneVisibility={resolved()}
|
|
351
|
+
/>
|
|
352
|
+
);
|
|
353
|
+
|
|
354
|
+
expect(
|
|
355
|
+
screen.queryByRole("button", { name: /cancel changes/i })
|
|
356
|
+
).toBeNull();
|
|
357
|
+
|
|
358
|
+
// Same number of lists, different contents.
|
|
359
|
+
await user.click(screen.getByRole("button", { name: /set lists 99/i }));
|
|
360
|
+
expect(
|
|
361
|
+
screen.getByRole("button", { name: /cancel changes/i })
|
|
362
|
+
).toBeInTheDocument();
|
|
363
|
+
|
|
364
|
+
// Different number of lists.
|
|
365
|
+
await user.click(screen.getByRole("button", { name: /set lists 1 2/i }));
|
|
366
|
+
expect(
|
|
367
|
+
screen.getByRole("button", { name: /cancel changes/i })
|
|
368
|
+
).toBeInTheDocument();
|
|
369
|
+
});
|
|
370
|
+
|
|
371
|
+
it("deletes the lane when the delete button is clicked", async () => {
|
|
372
|
+
const user = userEvent.setup();
|
|
373
|
+
const lane = createLaneData("Custom Lane", false);
|
|
374
|
+
const deleteLane = resolved();
|
|
375
|
+
render(
|
|
376
|
+
<LaneEditor
|
|
377
|
+
library="library"
|
|
378
|
+
lane={lane}
|
|
379
|
+
customLists={customListsData}
|
|
380
|
+
editOrCreate="edit"
|
|
381
|
+
editLane={resolved()}
|
|
382
|
+
deleteLane={deleteLane}
|
|
383
|
+
findParentOfLane={() => null}
|
|
384
|
+
toggleLaneVisibility={resolved()}
|
|
385
|
+
/>
|
|
386
|
+
);
|
|
387
|
+
|
|
388
|
+
await user.click(screen.getByRole("button", { name: /delete lane/i }));
|
|
389
|
+
|
|
390
|
+
expect(deleteLane).toHaveBeenCalledTimes(1);
|
|
391
|
+
expect(deleteLane).toHaveBeenCalledWith(lane);
|
|
392
|
+
});
|
|
393
|
+
|
|
394
|
+
it("toggles lane visibility when the hide button is clicked", async () => {
|
|
395
|
+
const user = userEvent.setup();
|
|
396
|
+
const lane = createLaneData("Custom Lane", false);
|
|
397
|
+
const toggleLaneVisibility = resolved();
|
|
398
|
+
render(
|
|
399
|
+
<LaneEditor
|
|
400
|
+
library="library"
|
|
401
|
+
lane={lane}
|
|
402
|
+
customLists={customListsData}
|
|
403
|
+
editOrCreate="edit"
|
|
404
|
+
editLane={resolved()}
|
|
405
|
+
deleteLane={resolved()}
|
|
406
|
+
findParentOfLane={() => null}
|
|
407
|
+
toggleLaneVisibility={toggleLaneVisibility}
|
|
408
|
+
/>
|
|
409
|
+
);
|
|
410
|
+
|
|
411
|
+
await user.click(screen.getByRole("button", { name: /hide lane/i }));
|
|
412
|
+
|
|
413
|
+
expect(toggleLaneVisibility).toHaveBeenCalledWith(lane, false);
|
|
414
|
+
});
|
|
415
|
+
|
|
416
|
+
it("saves the lane, building a FormData payload", async () => {
|
|
417
|
+
const user = userEvent.setup();
|
|
418
|
+
const lane = createLaneData("Custom Lane", false);
|
|
419
|
+
const parent = { ...createLaneData("Parent", false), id: 7 };
|
|
420
|
+
const editLane = resolved();
|
|
421
|
+
render(
|
|
422
|
+
<LaneEditor
|
|
423
|
+
library="library"
|
|
424
|
+
lane={lane}
|
|
425
|
+
customLists={customListsData}
|
|
426
|
+
editOrCreate="edit"
|
|
427
|
+
editLane={editLane}
|
|
428
|
+
deleteLane={resolved()}
|
|
429
|
+
findParentOfLane={() => parent}
|
|
430
|
+
toggleLaneVisibility={resolved()}
|
|
431
|
+
/>
|
|
432
|
+
);
|
|
433
|
+
|
|
434
|
+
await user.click(screen.getByRole("button", { name: /save lane/i }));
|
|
435
|
+
await waitFor(() => expect(editLane).toHaveBeenCalledTimes(1));
|
|
436
|
+
|
|
437
|
+
const formData = editLane.mock.calls[0][0];
|
|
438
|
+
expect(formData.get("id")).toBe("1");
|
|
439
|
+
expect(formData.get("parent_id")).toBe("7");
|
|
440
|
+
expect(formData.get("display_name")).toBe("Custom Lane");
|
|
441
|
+
expect(formData.get("custom_list_ids")).toBe(JSON.stringify([1]));
|
|
442
|
+
expect(formData.get("inherit_parent_restrictions")).toBe("true");
|
|
443
|
+
});
|
|
444
|
+
|
|
445
|
+
it("navigates to the edit page after creating a new lane", async () => {
|
|
446
|
+
const user = userEvent.setup();
|
|
447
|
+
const editLane = resolved();
|
|
448
|
+
const navSpy = jest
|
|
449
|
+
.spyOn(navigate, "navigateTo")
|
|
450
|
+
.mockImplementation(() => undefined);
|
|
451
|
+
render(
|
|
452
|
+
<LaneEditor
|
|
453
|
+
library="library"
|
|
454
|
+
customLists={twoLists}
|
|
455
|
+
responseBody="5"
|
|
456
|
+
editLane={editLane}
|
|
457
|
+
deleteLane={resolved()}
|
|
458
|
+
findParentOfLane={() => null}
|
|
459
|
+
toggleLaneVisibility={resolved()}
|
|
460
|
+
/>
|
|
461
|
+
);
|
|
462
|
+
|
|
463
|
+
// A new lane's name field starts in edit mode.
|
|
464
|
+
await user.type(screen.getByRole("textbox"), "new lane name");
|
|
465
|
+
await user.click(screen.getByRole("button", { name: /save lane/i }));
|
|
466
|
+
|
|
467
|
+
await waitFor(() =>
|
|
468
|
+
expect(navSpy).toHaveBeenCalledWith("/admin/web/lanes/library/edit/5")
|
|
469
|
+
);
|
|
470
|
+
|
|
471
|
+
navSpy.mockRestore();
|
|
472
|
+
});
|
|
473
|
+
|
|
474
|
+
it("cancels changes, resetting the form", async () => {
|
|
475
|
+
const user = userEvent.setup();
|
|
476
|
+
const lane = createLaneData("Custom Lane", false);
|
|
477
|
+
const parent = createLaneData("Parent", false);
|
|
478
|
+
render(
|
|
479
|
+
<LaneEditor
|
|
480
|
+
library="library"
|
|
481
|
+
lane={lane}
|
|
482
|
+
customLists={customListsData}
|
|
483
|
+
editOrCreate="edit"
|
|
484
|
+
editLane={resolved()}
|
|
485
|
+
deleteLane={resolved()}
|
|
486
|
+
findParentOfLane={() => parent}
|
|
487
|
+
toggleLaneVisibility={resolved()}
|
|
488
|
+
/>
|
|
489
|
+
);
|
|
490
|
+
|
|
491
|
+
const checkbox = screen.getByRole("checkbox", {
|
|
492
|
+
name: /inherit restrictions from parent/i,
|
|
493
|
+
});
|
|
494
|
+
expect(checkbox).toBeChecked();
|
|
495
|
+
|
|
496
|
+
await user.click(checkbox);
|
|
497
|
+
expect(checkbox).not.toBeChecked();
|
|
498
|
+
|
|
499
|
+
await user.click(screen.getByRole("button", { name: /cancel changes/i }));
|
|
500
|
+
|
|
501
|
+
// reset() restores the inherited-restrictions state...
|
|
502
|
+
expect(
|
|
503
|
+
screen.getByRole("checkbox", {
|
|
504
|
+
name: /inherit restrictions from parent/i,
|
|
505
|
+
})
|
|
506
|
+
).toBeChecked();
|
|
507
|
+
// ...and, with no remaining changes, hides the cancel button.
|
|
508
|
+
expect(
|
|
509
|
+
screen.queryByRole("button", { name: /cancel changes/i })
|
|
510
|
+
).toBeNull();
|
|
511
|
+
});
|
|
512
|
+
});
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { screen, waitForElementToBeRemoved } from "@testing-library/react";
|
|
3
|
+
import * as fetchMock from "fetch-mock-jest";
|
|
4
|
+
|
|
5
|
+
import { renderWithProviders } from "../testUtils/withProviders";
|
|
6
|
+
|
|
7
|
+
// Header and Footer are page chrome; Header pulls in the react-router v3 context
|
|
8
|
+
// that renderWithProviders does not supply. Mock them to markers.
|
|
9
|
+
jest.mock("../../../src/components/Header", () => ({
|
|
10
|
+
__esModule: true,
|
|
11
|
+
default: () => <div data-testid="header" />,
|
|
12
|
+
}));
|
|
13
|
+
jest.mock("../../../src/components/Footer", () => ({
|
|
14
|
+
__esModule: true,
|
|
15
|
+
default: () => <div data-testid="footer" />,
|
|
16
|
+
}));
|
|
17
|
+
|
|
18
|
+
// The real Lanes child (and its sidebar/editor) renders React Router `Link`s,
|
|
19
|
+
// which need router context renderWithProviders does not supply. Mock `Link` to
|
|
20
|
+
// a marker, matching Lane.test.tsx.
|
|
21
|
+
jest.mock("react-router", () => ({
|
|
22
|
+
...jest.requireActual("react-router"),
|
|
23
|
+
Link: (props) => <div data-testid="Link">{props.children}</div>,
|
|
24
|
+
}));
|
|
25
|
+
|
|
26
|
+
import LanePage from "../../../src/components/LanePage";
|
|
27
|
+
|
|
28
|
+
describe("LanePage", () => {
|
|
29
|
+
beforeAll(() => {
|
|
30
|
+
Object.assign(fetchMock.config, { fetch, Headers, Request, Response });
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
beforeEach(() => {
|
|
34
|
+
// Lanes fetches lanes and custom lists on mount; stub the network so no real
|
|
35
|
+
// request is made.
|
|
36
|
+
fetchMock.mock("*", { lanes: [], custom_lists: [] });
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
afterEach(() => {
|
|
40
|
+
fetchMock.restore();
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it("shows lanes with info from params", async () => {
|
|
44
|
+
const params = {
|
|
45
|
+
library: "library",
|
|
46
|
+
editOrCreate: "edit",
|
|
47
|
+
identifier: "5",
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
const { container } = renderWithProviders(<LanePage params={params} />);
|
|
51
|
+
|
|
52
|
+
// Header and Footer chrome are composed around the body.
|
|
53
|
+
expect(screen.getByTestId("header")).toBeInTheDocument();
|
|
54
|
+
expect(screen.getByTestId("footer")).toBeInTheDocument();
|
|
55
|
+
|
|
56
|
+
// The real Lanes child renders its container...
|
|
57
|
+
expect(container.querySelector("main.lanes-container")).toBeInTheDocument();
|
|
58
|
+
// ...and, because editOrCreate is "edit", its lane editor panel.
|
|
59
|
+
expect(container.querySelector(".lane-editor")).toBeInTheDocument();
|
|
60
|
+
|
|
61
|
+
// While its on-mount fetches (lanes and custom lists) are in flight, Lanes
|
|
62
|
+
// shows a loading indicator. Wait for it to clear so those fetches settle
|
|
63
|
+
// and their state updates flush, instead of racing a fixed-length timer.
|
|
64
|
+
await waitForElementToBeRemoved(() =>
|
|
65
|
+
screen.queryByRole("dialog", { name: "Loading" })
|
|
66
|
+
);
|
|
67
|
+
});
|
|
68
|
+
});
|