@thepalaceproject/circulation-admin 1.43.0-post.1 → 1.43.0-post.11

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.
Files changed (94) hide show
  1. package/CLAUDE.md +16 -7
  2. package/dist/circulation-admin.js +1 -1
  3. package/eslint.config.mjs +9 -1
  4. package/jest.config.js +56 -9
  5. package/package.json +6 -16
  6. package/tests/jest/actions.test.ts +962 -0
  7. package/tests/jest/components/AccountPage.test.tsx +41 -0
  8. package/tests/jest/components/AdvancedSearchBooleanFilter.test.tsx +231 -0
  9. package/tests/jest/components/AdvancedSearchFilter.test.tsx +142 -0
  10. package/tests/jest/components/AdvancedSearchFilterInput.test.tsx +212 -0
  11. package/tests/jest/components/AdvancedSearchFilterViewer.test.tsx +52 -0
  12. package/tests/jest/components/AdvancedSearchValueFilter.test.tsx +123 -0
  13. package/tests/jest/components/Announcement.test.tsx +76 -0
  14. package/tests/jest/components/Autocomplete.test.tsx +67 -0
  15. package/tests/jest/components/BookDetails.test.tsx +133 -0
  16. package/tests/jest/components/BookDetailsContainer.test.tsx +116 -0
  17. package/tests/jest/components/BookDetailsEditor.test.tsx +275 -0
  18. package/tests/jest/components/BookEditForm.test.tsx +352 -0
  19. package/tests/jest/components/CatalogServices.test.tsx +63 -0
  20. package/tests/jest/components/CirculationEventsDownloadForm.test.tsx +41 -0
  21. package/tests/jest/components/Classifications.test.tsx +166 -0
  22. package/tests/jest/components/ClassificationsTable.test.tsx +57 -0
  23. package/tests/jest/components/ColorPicker.test.tsx +58 -0
  24. package/tests/jest/components/ComplaintForm.test.tsx +171 -0
  25. package/tests/jest/components/Complaints.test.tsx +259 -0
  26. package/tests/jest/components/ConfigPage.test.tsx +58 -0
  27. package/tests/jest/components/ContextProvider.test.tsx +138 -0
  28. package/tests/jest/components/CustomListEditor.test.tsx +367 -0
  29. package/tests/jest/components/CustomListPage.test.tsx +57 -0
  30. package/tests/jest/components/CustomListSearch.test.tsx +275 -0
  31. package/tests/jest/components/CustomListsForBook.test.tsx +315 -0
  32. package/tests/jest/components/CustomListsSidebar.test.tsx +251 -0
  33. package/tests/jest/components/DashboardPage.test.tsx +76 -0
  34. package/tests/jest/components/DebugAuthentication.test.tsx +1 -5
  35. package/tests/jest/components/DiagnosticsServiceTabs.test.tsx +141 -0
  36. package/tests/jest/components/DiagnosticsServiceType.test.tsx +75 -0
  37. package/tests/jest/components/DiagnosticsTabContainer.test.tsx +147 -0
  38. package/tests/jest/components/EditableConfigList.test.tsx +8 -2
  39. package/tests/jest/components/EntryPointsContainer.test.tsx +97 -0
  40. package/tests/jest/components/EntryPointsTabs.test.tsx +88 -0
  41. package/tests/jest/components/ErrorMessage.test.tsx +97 -0
  42. package/tests/jest/components/Footer.test.tsx +141 -0
  43. package/tests/jest/components/InventoryReportRequestModal.test.tsx +16 -42
  44. package/tests/jest/components/LaneEditor.test.tsx +2 -6
  45. package/tests/jest/components/LanePage.test.tsx +68 -0
  46. package/tests/jest/components/LanguageField.test.tsx +40 -0
  47. package/tests/jest/components/LibraryEditForm.test.tsx +383 -0
  48. package/tests/jest/components/LibraryRegistrationForm.test.tsx +203 -0
  49. package/tests/jest/components/LoadButton.test.tsx +49 -0
  50. package/tests/jest/components/ManagePatrons.test.tsx +73 -0
  51. package/tests/jest/components/ManagePatronsForm.test.tsx +180 -0
  52. package/tests/jest/components/ManagePatronsTabContainer.test.tsx +143 -0
  53. package/tests/jest/components/MetadataServices.test.tsx +59 -0
  54. package/tests/jest/components/PatronAuthServices.test.tsx +114 -0
  55. package/tests/jest/components/PatronBlockingRulesEditor.test.tsx +3 -1
  56. package/tests/jest/components/PatronInfo.test.tsx +54 -0
  57. package/tests/jest/components/ResetAdobeId.test.tsx +302 -0
  58. package/tests/jest/components/SaveButton.test.tsx +47 -0
  59. package/tests/jest/components/SelfTestResult.test.tsx +164 -0
  60. package/tests/jest/components/SelfTestsCategory.test.tsx +200 -0
  61. package/tests/jest/components/ServiceWithRegistrationsEditForm.test.tsx +110 -0
  62. package/tests/jest/components/SetupPage.test.tsx +41 -0
  63. package/tests/jest/components/SingleStatListItem.test.tsx +62 -0
  64. package/tests/jest/components/SitewideAnnouncements.test.tsx +134 -0
  65. package/tests/jest/components/Stats.test.tsx +4 -8
  66. package/tests/jest/components/TabContainer.test.tsx +68 -0
  67. package/tests/jest/components/Timestamp.test.tsx +74 -0
  68. package/tests/jest/components/TroubleshootingCategoryPage.test.tsx +69 -0
  69. package/tests/jest/components/TroubleshootingPage.test.tsx +94 -0
  70. package/tests/jest/components/TroubleshootingTabContainer.test.tsx +68 -0
  71. package/tests/jest/components/UpdatingLoader.test.tsx +36 -0
  72. package/tests/jest/components/WelcomePage.test.tsx +43 -0
  73. package/tests/jest/components/WithEditButton.test.tsx +55 -0
  74. package/tests/jest/components/WithRemoveButton.test.tsx +95 -0
  75. package/tests/jest/editorAdapter.test.ts +245 -0
  76. package/tests/jest/index.test.tsx +50 -0
  77. package/tests/jest/legacy-setup.ts +13 -0
  78. package/tests/jest/legacyJsdomEnvironment.js +49 -0
  79. package/tests/jest/models/Admin.test.ts +206 -0
  80. package/tests/jest/reducers/bookCoverPreview.test.ts +81 -0
  81. package/tests/jest/reducers/classifications.test.ts +161 -0
  82. package/tests/jest/reducers/collections.test.ts +96 -0
  83. package/tests/jest/reducers/complaints.test.ts +166 -0
  84. package/tests/jest/reducers/createFetchEditReducer.test.ts +220 -0
  85. package/tests/jest/reducers/createRegisterLibraryReducer.test.ts +51 -0
  86. package/tests/jest/reducers/customListDetails.test.ts +120 -0
  87. package/tests/jest/reducers/customListEditor.test.ts +2823 -0
  88. package/tests/jest/reducers/managePatrons.test.ts +71 -0
  89. package/tests/jest/testUtils/formDataShim.test.ts +135 -0
  90. package/tests/jest/testUtils/formDataShim.ts +112 -0
  91. package/tests/jest/utils/sharedFunctions.test.ts +121 -0
  92. package/tsconfig.json +3 -4
  93. package/testReporter.js +0 -29
  94. package/tests/jest/components/AdvancedSearchBuilder.test.tsx +0 -38
@@ -0,0 +1,245 @@
1
+ import adapter from "../../src/editorAdapter";
2
+ import {
3
+ OPDSEntry,
4
+ Contributor,
5
+ Series,
6
+ Category,
7
+ Summary,
8
+ } from "opds-feed-parser";
9
+
10
+ describe("editorAdapter", () => {
11
+ it("adapts valid OPDS entry", () => {
12
+ const entry = new OPDSEntry({
13
+ id: "id",
14
+ updated: "updated",
15
+ title: "title",
16
+ authors: [new Contributor({ name: "name", uri: "uri", role: "role" })],
17
+ subtitle: "subtitle",
18
+ contributors: [
19
+ new Contributor({ name: "name2", uri: "uri2", role: "role2" }),
20
+ ],
21
+ series: new Series({ name: "series", position: 2 }),
22
+ categories: [
23
+ new Category({ term: "term", scheme: "scheme", label: "label" }),
24
+ new Category({
25
+ term: "13-16",
26
+ scheme: "http://schema.org/typicalAgeRange",
27
+ label: "age",
28
+ }),
29
+ new Category({
30
+ term: "ya",
31
+ scheme: "http://schema.org/audience",
32
+ label: "ya",
33
+ }),
34
+ new Category({
35
+ term: "fiction",
36
+ scheme: "http://librarysimplified.org/terms/fiction/",
37
+ label: "Fiction",
38
+ }),
39
+ ],
40
+ identifiers: [],
41
+ links: [
42
+ {
43
+ href: "hide",
44
+ rel: "http://librarysimplified.org/terms/rel/hide",
45
+ type: "type",
46
+ title: "title",
47
+ role: "role",
48
+ },
49
+ {
50
+ href: "restore",
51
+ rel: "http://librarysimplified.org/terms/rel/restore",
52
+ type: "type",
53
+ title: "title",
54
+ role: "role",
55
+ },
56
+ {
57
+ href: "refresh",
58
+ rel: "http://librarysimplified.org/terms/rel/refresh",
59
+ type: "type",
60
+ title: "title",
61
+ role: "role",
62
+ },
63
+ {
64
+ href: "edit",
65
+ rel: "edit",
66
+ type: "type",
67
+ title: "title",
68
+ role: "role",
69
+ },
70
+ {
71
+ href: "issues",
72
+ rel: "issues",
73
+ type: "type",
74
+ title: "title",
75
+ role: "role",
76
+ },
77
+ {
78
+ href: "change-cover",
79
+ rel: "http://librarysimplified.org/terms/rel/change_cover",
80
+ type: "type",
81
+ title: "title",
82
+ role: "role",
83
+ },
84
+ {
85
+ href: "cover",
86
+ rel: "http://opds-spec.org/image",
87
+ type: "image/png",
88
+ title: "title",
89
+ role: "role",
90
+ },
91
+ ],
92
+ issued: "issued",
93
+ language: "language",
94
+ rights: "rights",
95
+ publisher: "publisher",
96
+ published: "published",
97
+ summary: new Summary({ content: "content", link: "link" }),
98
+ unparsed: {
99
+ "schema:alternativeHeadline": [{ _: "subtitle" }],
100
+ $: {
101
+ "schema:additionalType": { value: "medium" },
102
+ },
103
+ "bib:publisherImprint": [{ _: "imprint" }],
104
+ "schema:Rating": [
105
+ {
106
+ $: {
107
+ "schema:ratingValue": { value: "0.3" },
108
+ "schema:additionalType": {
109
+ value: "http://librarysimplified.org/terms/rel/quality",
110
+ },
111
+ },
112
+ },
113
+ {
114
+ $: {
115
+ "schema:ratingValue": { value: "4" },
116
+ "schema:additionalType": {
117
+ value: "http://schema.org/ratingValue",
118
+ },
119
+ },
120
+ },
121
+ ],
122
+ },
123
+ });
124
+
125
+ const adapted = adapter(entry);
126
+ expect(adapted.title).toBe("title");
127
+ expect(adapted.authors).toStrictEqual([
128
+ { name: "name", uri: "uri", role: "aut" },
129
+ ]);
130
+ // The adapter flattens `Contributor` instances into plain objects, so assert
131
+ // the shape it produces rather than comparing against `entry.contributors`.
132
+ expect(adapted.contributors).toStrictEqual([
133
+ { name: "name2", uri: "uri2", role: "role2" },
134
+ ]);
135
+ expect(adapted.subtitle).toBe("subtitle");
136
+ expect(adapted.summary).toBe("content");
137
+ expect(adapted.audience).toBe("ya");
138
+ expect(adapted.targetAgeRange).toStrictEqual(["13", "16"]);
139
+ expect(adapted.fiction).toBe(true);
140
+ expect(adapted.categories).toStrictEqual(["label", "age", "ya", "Fiction"]);
141
+ expect(adapted.hideLink).toStrictEqual(entry.links[0]);
142
+ expect(adapted.restoreLink).toStrictEqual(entry.links[1]);
143
+ expect(adapted.refreshLink).toStrictEqual(entry.links[2]);
144
+ expect(adapted.editLink).toStrictEqual(entry.links[3]);
145
+ expect(adapted.issuesLink).toStrictEqual(entry.links[4]);
146
+ expect(adapted.changeCoverLink).toStrictEqual(entry.links[5]);
147
+ expect(adapted.coverUrl).toBe(entry.links[6].href);
148
+ expect(adapted.series).toBe("series");
149
+ expect(adapted.seriesPosition).toBe(2);
150
+ expect(adapted.medium).toBe("medium");
151
+ expect(adapted.language).toBe("language");
152
+ expect(adapted.publisher).toBe("publisher");
153
+ expect(adapted.imprint).toBe("imprint");
154
+ expect(adapted.issued).toBe("issued");
155
+ expect(adapted.rating).toBe("4");
156
+ });
157
+
158
+ it("adapts visibility status from categories", () => {
159
+ const entry = new OPDSEntry({
160
+ id: "id",
161
+ updated: "updated",
162
+ title: "title",
163
+ subtitle: "subtitle",
164
+ authors: [],
165
+ contributors: [],
166
+ series: new Series({ name: "series", position: 2 }),
167
+ categories: [
168
+ new Category({ term: "term", scheme: "scheme", label: "label" }),
169
+ new Category({
170
+ term: "manually-suppressed",
171
+ scheme: "http://palaceproject.io/terms/visibility-status",
172
+ label: "Manually Suppressed",
173
+ }),
174
+ ],
175
+ identifiers: [],
176
+ links: [],
177
+ issued: "issued",
178
+ language: "language",
179
+ rights: "rights",
180
+ publisher: "publisher",
181
+ published: "published",
182
+ summary: new Summary({ content: "content", link: "link" }),
183
+ unparsed: {},
184
+ });
185
+
186
+ const adapted = adapter(entry);
187
+ expect(adapted.visibilityStatus).toBe("manually-suppressed");
188
+ });
189
+
190
+ it("adapts policy-filtered visibility status", () => {
191
+ const entry = new OPDSEntry({
192
+ id: "id",
193
+ updated: "updated",
194
+ title: "title",
195
+ subtitle: "subtitle",
196
+ authors: [],
197
+ contributors: [],
198
+ series: new Series({ name: "series", position: 2 }),
199
+ categories: [
200
+ new Category({
201
+ term: "policy-filtered",
202
+ scheme: "http://palaceproject.io/terms/visibility-status",
203
+ label: "Policy Filtered",
204
+ }),
205
+ ],
206
+ identifiers: [],
207
+ links: [],
208
+ issued: "issued",
209
+ language: "language",
210
+ rights: "rights",
211
+ publisher: "publisher",
212
+ published: "published",
213
+ summary: new Summary({ content: "content", link: "link" }),
214
+ unparsed: {},
215
+ });
216
+
217
+ const adapted = adapter(entry);
218
+ expect(adapted.visibilityStatus).toBe("policy-filtered");
219
+ });
220
+
221
+ it("doesn't crash when expected data is missing", () => {
222
+ const entry = new OPDSEntry({
223
+ id: "id",
224
+ updated: "updated",
225
+ title: "title",
226
+ subtitle: "subtitle",
227
+ authors: [],
228
+ contributors: [],
229
+ series: new Series({ name: "series", position: 2 }),
230
+ categories: [],
231
+ identifiers: [],
232
+ links: [],
233
+ issued: "issued",
234
+ language: "language",
235
+ rights: "rights",
236
+ publisher: "publisher",
237
+ published: "published",
238
+ summary: new Summary({ content: "content", link: "link" }),
239
+ unparsed: {},
240
+ });
241
+
242
+ const adapted = adapter(entry);
243
+ expect(adapted.title).toBe("title");
244
+ });
245
+ });
@@ -0,0 +1,50 @@
1
+ import * as React from "react";
2
+ import * as ReactDOM from "react-dom";
3
+ import { Router } from "react-router";
4
+
5
+ import SetupPage from "../../src/components/SetupPage";
6
+
7
+ // index.tsx does `export = CirculationAdmin`.
8
+ const CirculationAdmin = require("../../src/index");
9
+
10
+ /** Walk a React element tree for an element of the given type — the structural
11
+ * analog of enzyme's `wrapper.find(Type)`, used where the tree is the app root
12
+ * and there is no rendered DOM to query against. */
13
+ function containsType(node: any, type: React.ElementType): boolean {
14
+ if (!node || typeof node !== "object") return false;
15
+ if (Array.isArray(node)) return node.some((n) => containsType(n, type));
16
+ if (node.type === type) return true;
17
+ return containsType(node.props && node.props.children, type);
18
+ }
19
+
20
+ describe("CirculationAdmin", () => {
21
+ let renderSpy: jest.SpyInstance;
22
+
23
+ beforeEach(() => {
24
+ // Capture what the bootstrap hands to ReactDOM.render without letting it
25
+ // paint into the jsdom document. We assert on the captured element tree
26
+ // (the structural analog of the original enzyme `wrapper.find(...)`); what
27
+ // SetupPage and the routed pages render is covered by their own tests.
28
+ renderSpy = jest
29
+ .spyOn(ReactDOM, "render")
30
+ .mockImplementation(() => undefined);
31
+ });
32
+
33
+ afterEach(() => {
34
+ jest.restoreAllMocks();
35
+ });
36
+
37
+ it("renders the setup page when setting up", () => {
38
+ new CirculationAdmin({ settingUp: true, csrfToken: "token" });
39
+
40
+ expect(renderSpy).toHaveBeenCalledTimes(1);
41
+ expect(containsType(renderSpy.mock.calls[0][0], SetupPage)).toBe(true);
42
+ });
43
+
44
+ it("renders the router otherwise", () => {
45
+ new CirculationAdmin({ csrfToken: "token" });
46
+
47
+ expect(renderSpy).toHaveBeenCalledTimes(1);
48
+ expect(containsType(renderSpy.mock.calls[0][0], Router)).toBe(true);
49
+ });
50
+ });
@@ -0,0 +1,13 @@
1
+ import { configure } from "enzyme";
2
+ import * as Adapter from "enzyme-adapter-react-16";
3
+
4
+ // Setup for the legacy enzyme + chai + sinon tests under `src/**/__tests__/`.
5
+ // Replaces what `src/testHelper.ts` did for mocha. Delete alongside the last
6
+ // enzyme test.
7
+
8
+ configure({ adapter: new Adapter() });
9
+
10
+ // Unhandled promise rejections are tolerated for these tests — see the comment in
11
+ // `legacyJsdomEnvironment.js`. It cannot be done from here: jest hands test code a
12
+ // *copy* of `process`, so a listener registered in this file never reaches the real
13
+ // one that Node checks before throwing.
@@ -0,0 +1,49 @@
1
+ "use strict";
2
+
3
+ const JSDOMEnvironment = require("jest-environment-jsdom").default;
4
+
5
+ const warnUnhandledRejection = (reason) => {
6
+ console.warn("Unhandled promise rejection in a legacy test:", reason);
7
+ };
8
+
9
+ /**
10
+ * jsdom environment for the legacy enzyme tests. Delete it with the last one.
11
+ *
12
+ * Two things it restores that `src/testHelper.ts` used to provide under mocha:
13
+ *
14
+ * 1. `global.jsdom`. A handful of tests call `global.jsdom.reconfigure({ url })` to
15
+ * change the page origin mid-test. `history.replaceState` is not a substitute —
16
+ * they navigate across origins (`http://example.com`), which it cannot do.
17
+ *
18
+ * 2. Tolerance for unhandled promise rejections. Several suites stub a `fetch` that
19
+ * rejects, and nothing awaits the result; the rejection surfaces after the test
20
+ * has already passed. Mocha shrugged those off because its runner keeps an
21
+ * `unhandledRejection` listener installed, and Node only throws when there are
22
+ * none. Under jest they kill the worker and take the whole suite with them.
23
+ *
24
+ * Re-arming has to happen on the `teardown` event, not in the constructor.
25
+ * `jest-circus/globalErrorHandlers` reads `process.listeners()` off the sandbox's
26
+ * *copied* `process` (always empty) but calls `removeAllListeners()` on the real
27
+ * one. So any listener registered earlier is dropped at `setup` and "restored"
28
+ * from an empty list, leaving zero listeners during the post-run window where
29
+ * these rejections actually land.
30
+ *
31
+ * Scoping this to the legacy environment keeps the modern `tests/jest/` suite
32
+ * strict: jest clears listeners at each file's `setup`, and nothing re-arms one
33
+ * for a project that does not use this environment.
34
+ */
35
+ module.exports = class LegacyJSDOMEnvironment extends JSDOMEnvironment {
36
+ constructor(config, context) {
37
+ super(config, context);
38
+ this.global.jsdom = this.dom;
39
+ }
40
+
41
+ handleTestEvent(event) {
42
+ if (
43
+ event.name === "teardown" &&
44
+ process.listenerCount("unhandledRejection") === 0
45
+ ) {
46
+ process.on("unhandledRejection", warnUnhandledRejection);
47
+ }
48
+ }
49
+ };
@@ -0,0 +1,206 @@
1
+ import Admin from "../../../src/models/Admin";
2
+
3
+ describe("Admin", () => {
4
+ const systemAdmin = new Admin([{ role: "system" }]);
5
+ const sitewideLibraryManager = new Admin([{ role: "manager-all" }]);
6
+ const sitewideLibrarian = new Admin([{ role: "librarian-all" }]);
7
+ const libraryManagerA = new Admin([{ role: "manager", library: "a" }]);
8
+ const librarianA = new Admin([{ role: "librarian", library: "a" }]);
9
+ const libraryManagerB = new Admin([{ role: "manager", library: "b" }]);
10
+ const librarianB = new Admin([{ role: "librarian", library: "b" }]);
11
+ const libraryManagerASitewideLibrarian = new Admin([
12
+ { role: "librarian-all" },
13
+ { role: "manager", library: "a" },
14
+ ]);
15
+ const libraryManagerALibrarianB = new Admin([
16
+ { role: "manager", library: "a" },
17
+ { role: "librarian", library: "b" },
18
+ ]);
19
+
20
+ it("stores email", () => {
21
+ expect(new Admin([], "email").email).toBe("email");
22
+ expect(new Admin([]).email).toBeFalsy();
23
+ });
24
+
25
+ it("identifies system admins", () => {
26
+ expect(systemAdmin.isSystemAdmin()).toBeTruthy();
27
+ expect(sitewideLibraryManager.isSystemAdmin()).toBeFalsy();
28
+ expect(sitewideLibrarian.isSystemAdmin()).toBeFalsy();
29
+ expect(libraryManagerA.isSystemAdmin()).toBeFalsy();
30
+ expect(librarianA.isSystemAdmin()).toBeFalsy();
31
+ expect(libraryManagerB.isSystemAdmin()).toBeFalsy();
32
+ expect(librarianB.isSystemAdmin()).toBeFalsy();
33
+ expect(libraryManagerASitewideLibrarian.isSystemAdmin()).toBeFalsy();
34
+ expect(libraryManagerALibrarianB.isSystemAdmin()).toBeFalsy();
35
+ });
36
+
37
+ it("identifies sitewide library managers", () => {
38
+ expect(systemAdmin.isSitewideLibraryManager()).toBeTruthy();
39
+ expect(sitewideLibraryManager.isSitewideLibraryManager()).toBeTruthy();
40
+ expect(sitewideLibrarian.isSitewideLibraryManager()).toBeFalsy();
41
+ expect(libraryManagerA.isSitewideLibraryManager()).toBeFalsy();
42
+ expect(librarianA.isSitewideLibraryManager()).toBeFalsy();
43
+ expect(libraryManagerB.isSitewideLibraryManager()).toBeFalsy();
44
+ expect(librarianB.isSitewideLibraryManager()).toBeFalsy();
45
+ expect(
46
+ libraryManagerASitewideLibrarian.isSitewideLibraryManager()
47
+ ).toBeFalsy();
48
+ expect(libraryManagerALibrarianB.isSitewideLibraryManager()).toBeFalsy();
49
+ });
50
+
51
+ it("identifies sitewide librarians", () => {
52
+ expect(systemAdmin.isSitewideLibrarian()).toBeTruthy();
53
+ expect(sitewideLibraryManager.isSitewideLibrarian()).toBeTruthy();
54
+ expect(sitewideLibrarian.isSitewideLibrarian()).toBeTruthy();
55
+ expect(libraryManagerA.isSitewideLibrarian()).toBeFalsy();
56
+ expect(librarianA.isSitewideLibrarian()).toBeFalsy();
57
+ expect(libraryManagerB.isSitewideLibrarian()).toBeFalsy();
58
+ expect(librarianB.isSitewideLibrarian()).toBeFalsy();
59
+ expect(libraryManagerASitewideLibrarian.isSitewideLibrarian()).toBeTruthy();
60
+ expect(libraryManagerALibrarianB.isSitewideLibrarian()).toBeFalsy();
61
+ });
62
+
63
+ it("identifies library managers", () => {
64
+ expect(systemAdmin.isLibraryManager("a")).toBeTruthy();
65
+ expect(sitewideLibraryManager.isLibraryManager("a")).toBeTruthy();
66
+ expect(sitewideLibrarian.isLibraryManager("a")).toBeFalsy();
67
+ expect(libraryManagerA.isLibraryManager("a")).toBeTruthy();
68
+ expect(librarianA.isLibraryManager("a")).toBeFalsy();
69
+ expect(libraryManagerB.isLibraryManager("a")).toBeFalsy();
70
+ expect(librarianB.isLibraryManager("a")).toBeFalsy();
71
+ expect(libraryManagerASitewideLibrarian.isLibraryManager("a")).toBeTruthy();
72
+ expect(libraryManagerALibrarianB.isLibraryManager("a")).toBeTruthy();
73
+
74
+ expect(systemAdmin.isLibraryManager("b")).toBeTruthy();
75
+ expect(sitewideLibraryManager.isLibraryManager("b")).toBeTruthy();
76
+ expect(sitewideLibrarian.isLibraryManager("b")).toBeFalsy();
77
+ expect(libraryManagerA.isLibraryManager("b")).toBeFalsy();
78
+ expect(librarianA.isLibraryManager("b")).toBeFalsy();
79
+ expect(libraryManagerB.isLibraryManager("b")).toBeTruthy();
80
+ expect(librarianB.isLibraryManager("b")).toBeFalsy();
81
+ expect(libraryManagerASitewideLibrarian.isLibraryManager("b")).toBeFalsy();
82
+ expect(libraryManagerALibrarianB.isLibraryManager("b")).toBeFalsy();
83
+ });
84
+
85
+ it("identifies librarians", () => {
86
+ expect(systemAdmin.isLibrarian("a")).toBeTruthy();
87
+ expect(sitewideLibraryManager.isLibrarian("a")).toBeTruthy();
88
+ expect(sitewideLibrarian.isLibrarian("a")).toBeTruthy();
89
+ expect(libraryManagerA.isLibrarian("a")).toBeTruthy();
90
+ expect(librarianA.isLibrarian("a")).toBeTruthy();
91
+ expect(libraryManagerB.isLibrarian("a")).toBeFalsy();
92
+ expect(librarianB.isLibrarian("a")).toBeFalsy();
93
+ expect(libraryManagerASitewideLibrarian.isLibrarian("a")).toBeTruthy();
94
+ expect(libraryManagerALibrarianB.isLibrarian("a")).toBeTruthy();
95
+
96
+ expect(systemAdmin.isLibrarian("b")).toBeTruthy();
97
+ expect(sitewideLibraryManager.isLibrarian("b")).toBeTruthy();
98
+ expect(sitewideLibrarian.isLibrarian("b")).toBeTruthy();
99
+ expect(libraryManagerA.isLibrarian("b")).toBeFalsy();
100
+ expect(librarianA.isLibrarian("b")).toBeFalsy();
101
+ expect(libraryManagerB.isLibrarian("b")).toBeTruthy();
102
+ expect(librarianB.isLibrarian("b")).toBeTruthy();
103
+ expect(libraryManagerASitewideLibrarian.isLibrarian("b")).toBeTruthy();
104
+ expect(libraryManagerALibrarianB.isLibrarian("b")).toBeTruthy();
105
+ });
106
+
107
+ it("identifies all library managers", () => {
108
+ expect(systemAdmin.isLibraryManagerOfSomeLibrary()).toBeTruthy();
109
+ expect(sitewideLibraryManager.isLibraryManagerOfSomeLibrary()).toBeTruthy();
110
+ expect(sitewideLibrarian.isLibraryManagerOfSomeLibrary()).toBeFalsy();
111
+ expect(libraryManagerA.isLibraryManagerOfSomeLibrary()).toBeTruthy();
112
+ expect(librarianA.isLibraryManagerOfSomeLibrary()).toBeFalsy();
113
+ expect(libraryManagerB.isLibraryManagerOfSomeLibrary()).toBeTruthy();
114
+ expect(librarianB.isLibraryManagerOfSomeLibrary()).toBeFalsy();
115
+ expect(
116
+ libraryManagerASitewideLibrarian.isLibraryManagerOfSomeLibrary()
117
+ ).toBeTruthy();
118
+ expect(
119
+ libraryManagerALibrarianB.isLibraryManagerOfSomeLibrary()
120
+ ).toBeTruthy();
121
+ });
122
+
123
+ it("checks for specific roles", () => {
124
+ expect(systemAdmin.hasRole("system")).toBeTruthy();
125
+ expect(systemAdmin.hasRole("manager-all")).toBeTruthy();
126
+ expect(systemAdmin.hasRole("librarian-all")).toBeTruthy();
127
+ expect(systemAdmin.hasRole("manager", "a")).toBeTruthy();
128
+ expect(systemAdmin.hasRole("librarian", "a")).toBeTruthy();
129
+ expect(systemAdmin.hasRole("manager", "b")).toBeTruthy();
130
+ expect(systemAdmin.hasRole("librarian", "b")).toBeTruthy();
131
+
132
+ expect(sitewideLibraryManager.hasRole("system")).toBeFalsy();
133
+ expect(sitewideLibraryManager.hasRole("manager-all")).toBeTruthy();
134
+ expect(sitewideLibraryManager.hasRole("librarian-all")).toBeTruthy();
135
+ expect(sitewideLibraryManager.hasRole("manager", "a")).toBeTruthy();
136
+ expect(sitewideLibraryManager.hasRole("librarian", "a")).toBeTruthy();
137
+ expect(sitewideLibraryManager.hasRole("manager", "b")).toBeTruthy();
138
+ expect(sitewideLibraryManager.hasRole("librarian", "b")).toBeTruthy();
139
+
140
+ expect(sitewideLibrarian.hasRole("system")).toBeFalsy();
141
+ expect(sitewideLibrarian.hasRole("manager-all")).toBeFalsy();
142
+ expect(sitewideLibrarian.hasRole("librarian-all")).toBeTruthy();
143
+ expect(sitewideLibrarian.hasRole("manager", "a")).toBeFalsy();
144
+ expect(sitewideLibrarian.hasRole("librarian", "a")).toBeTruthy();
145
+ expect(sitewideLibrarian.hasRole("manager", "b")).toBeFalsy();
146
+ expect(sitewideLibrarian.hasRole("librarian", "b")).toBeTruthy();
147
+
148
+ expect(libraryManagerA.hasRole("system")).toBeFalsy();
149
+ expect(libraryManagerA.hasRole("manager-all")).toBeFalsy();
150
+ expect(libraryManagerA.hasRole("librarian-all")).toBeFalsy();
151
+ expect(libraryManagerA.hasRole("manager", "a")).toBeTruthy();
152
+ expect(libraryManagerA.hasRole("librarian", "a")).toBeTruthy();
153
+ expect(libraryManagerA.hasRole("manager", "b")).toBeFalsy();
154
+ expect(libraryManagerA.hasRole("librarian", "b")).toBeFalsy();
155
+
156
+ expect(librarianA.hasRole("system")).toBeFalsy();
157
+ expect(librarianA.hasRole("manager-all")).toBeFalsy();
158
+ expect(librarianA.hasRole("librarian-all")).toBeFalsy();
159
+ expect(librarianA.hasRole("manager", "a")).toBeFalsy();
160
+ expect(librarianA.hasRole("librarian", "a")).toBeTruthy();
161
+ expect(librarianA.hasRole("manager", "b")).toBeFalsy();
162
+ expect(librarianA.hasRole("librarian", "b")).toBeFalsy();
163
+
164
+ expect(libraryManagerB.hasRole("system")).toBeFalsy();
165
+ expect(libraryManagerB.hasRole("manager-all")).toBeFalsy();
166
+ expect(libraryManagerB.hasRole("librarian-all")).toBeFalsy();
167
+ expect(libraryManagerB.hasRole("manager", "a")).toBeFalsy();
168
+ expect(libraryManagerB.hasRole("librarian", "a")).toBeFalsy();
169
+ expect(libraryManagerB.hasRole("manager", "b")).toBeTruthy();
170
+ expect(libraryManagerB.hasRole("librarian", "b")).toBeTruthy();
171
+
172
+ expect(librarianB.hasRole("system")).toBeFalsy();
173
+ expect(librarianB.hasRole("manager-all")).toBeFalsy();
174
+ expect(librarianB.hasRole("librarian-all")).toBeFalsy();
175
+ expect(librarianB.hasRole("manager", "a")).toBeFalsy();
176
+ expect(librarianB.hasRole("librarian", "a")).toBeFalsy();
177
+ expect(librarianB.hasRole("manager", "b")).toBeFalsy();
178
+ expect(librarianB.hasRole("librarian", "b")).toBeTruthy();
179
+
180
+ expect(libraryManagerASitewideLibrarian.hasRole("system")).toBeFalsy();
181
+ expect(libraryManagerASitewideLibrarian.hasRole("manager-all")).toBeFalsy();
182
+ expect(
183
+ libraryManagerASitewideLibrarian.hasRole("librarian-all")
184
+ ).toBeTruthy();
185
+ expect(
186
+ libraryManagerASitewideLibrarian.hasRole("manager", "a")
187
+ ).toBeTruthy();
188
+ expect(
189
+ libraryManagerASitewideLibrarian.hasRole("librarian", "a")
190
+ ).toBeTruthy();
191
+ expect(
192
+ libraryManagerASitewideLibrarian.hasRole("manager", "b")
193
+ ).toBeFalsy();
194
+ expect(
195
+ libraryManagerASitewideLibrarian.hasRole("librarian", "b")
196
+ ).toBeTruthy();
197
+
198
+ expect(libraryManagerALibrarianB.hasRole("system")).toBeFalsy();
199
+ expect(libraryManagerALibrarianB.hasRole("manager-all")).toBeFalsy();
200
+ expect(libraryManagerALibrarianB.hasRole("librarian-all")).toBeFalsy();
201
+ expect(libraryManagerALibrarianB.hasRole("manager", "a")).toBeTruthy();
202
+ expect(libraryManagerALibrarianB.hasRole("librarian", "a")).toBeTruthy();
203
+ expect(libraryManagerALibrarianB.hasRole("manager", "b")).toBeFalsy();
204
+ expect(libraryManagerALibrarianB.hasRole("librarian", "b")).toBeTruthy();
205
+ });
206
+ });
@@ -0,0 +1,81 @@
1
+ import reducer, {
2
+ BookCoverPreviewState,
3
+ } from "../../../src/reducers/bookCoverPreview";
4
+ import ActionCreator from "../../../src/actions";
5
+
6
+ describe("book cover preview reducer", () => {
7
+ const preview = "image data";
8
+
9
+ const initState: BookCoverPreviewState = {
10
+ data: null,
11
+ isFetching: false,
12
+ fetchError: null,
13
+ };
14
+
15
+ const errorState: BookCoverPreviewState = {
16
+ data: null,
17
+ isFetching: false,
18
+ fetchError: { status: 401, response: "test error", url: "test url" },
19
+ };
20
+
21
+ const loadedState = Object.assign({}, initState, {
22
+ data: preview,
23
+ });
24
+
25
+ it("returns initial state for unrecognized action", () => {
26
+ expect(reducer(undefined, {})).toStrictEqual(initState);
27
+ });
28
+
29
+ it("handles PREVIEW_BOOK_COVER_REQUEST", () => {
30
+ const action = {
31
+ type: `${ActionCreator.PREVIEW_BOOK_COVER}_${ActionCreator.REQUEST}`,
32
+ url: "test url",
33
+ };
34
+
35
+ // start with empty state
36
+ let newState = Object.assign({}, initState, {
37
+ isFetching: true,
38
+ });
39
+ expect(reducer(initState, action)).toStrictEqual(newState);
40
+
41
+ // start with error state
42
+ newState = Object.assign({}, errorState, {
43
+ isFetching: true,
44
+ fetchError: null,
45
+ });
46
+ expect(reducer(errorState, action)).toStrictEqual(newState);
47
+ });
48
+
49
+ it("handles PREVIEW_BOOK_COVER_FAILURE", () => {
50
+ const action = {
51
+ type: `${ActionCreator.PREVIEW_BOOK_COVER}_${ActionCreator.FAILURE}`,
52
+ error: "test error",
53
+ };
54
+ const oldState = Object.assign({}, initState, { isFetching: true });
55
+ const newState = Object.assign({}, oldState, {
56
+ fetchError: "test error",
57
+ isFetching: false,
58
+ });
59
+ expect(reducer(oldState, action)).toStrictEqual(newState);
60
+ });
61
+
62
+ it("handles PREVIEW_BOOK_COVER_LOAD", () => {
63
+ const action = {
64
+ type: `${ActionCreator.PREVIEW_BOOK_COVER}_${ActionCreator.LOAD}`,
65
+ data: preview,
66
+ };
67
+ const newState = Object.assign({}, initState, {
68
+ data: preview,
69
+ isFetching: false,
70
+ });
71
+ expect(reducer(initState, action)).toStrictEqual(newState);
72
+ });
73
+
74
+ it("handles PREVIEW_BOOK_COVER_CLEAR", () => {
75
+ const action = {
76
+ type: `${ActionCreator.PREVIEW_BOOK_COVER}_${ActionCreator.CLEAR}`,
77
+ };
78
+ expect(reducer(loadedState, action)).toStrictEqual(initState);
79
+ expect(reducer(errorState, action)).toStrictEqual(initState);
80
+ });
81
+ });