@thepalaceproject/circulation-admin 1.43.0 → 1.44.0-post.4
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 +28 -7
- package/README.md +11 -5
- package/dist/circulation-admin.js +1 -1
- package/eslint.config.mjs +9 -1
- package/jest.config.js +22 -8
- package/package.json +6 -20
- 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/JsonField.test.tsx +20 -0
- 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 +479 -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/testReporter.js +0 -29
- package/tests/jest/README.md +0 -6
|
@@ -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
|
+
});
|
|
@@ -3,7 +3,6 @@ import reducer, {
|
|
|
3
3
|
getBookData,
|
|
4
4
|
submitBookData,
|
|
5
5
|
} from "../../../src/features/book/bookEditorSlice";
|
|
6
|
-
import { expect } from "chai";
|
|
7
6
|
import * as fetchMock from "fetch-mock-jest";
|
|
8
7
|
import { BookData } from "@thepalaceproject/web-opds-client/lib/interfaces";
|
|
9
8
|
import { RequestError } from "@thepalaceproject/web-opds-client/lib/DataFetcher";
|
|
@@ -57,12 +56,12 @@ describe("Redux bookEditorSlice...", () => {
|
|
|
57
56
|
|
|
58
57
|
describe("reducers...", () => {
|
|
59
58
|
it("should return the initial state from undefined, if no action is passed", () => {
|
|
60
|
-
expect(reducer(undefined, { type: "unknown" })).
|
|
59
|
+
expect(reducer(undefined, { type: "unknown" })).toStrictEqual(
|
|
61
60
|
initialState
|
|
62
61
|
);
|
|
63
62
|
});
|
|
64
63
|
it("should return the initial state from initialState, if no action is passed", () => {
|
|
65
|
-
expect(reducer(initialState, { type: "unknown" })).
|
|
64
|
+
expect(reducer(initialState, { type: "unknown" })).toStrictEqual(
|
|
66
65
|
initialState
|
|
67
66
|
);
|
|
68
67
|
});
|
|
@@ -70,7 +69,7 @@ describe("Redux bookEditorSlice...", () => {
|
|
|
70
69
|
// This is dispatched by `web-opds`client`, but we need to handle it, too.
|
|
71
70
|
const action = { type: ActionCreator.BOOK_CLEAR };
|
|
72
71
|
|
|
73
|
-
expect(reducer(fetchedState, action)).
|
|
72
|
+
expect(reducer(fetchedState, action)).toStrictEqual(initialState);
|
|
74
73
|
});
|
|
75
74
|
|
|
76
75
|
it("should handle getBookData.pending", () => {
|
|
@@ -81,11 +80,11 @@ describe("Redux bookEditorSlice...", () => {
|
|
|
81
80
|
const previousState = { ...initialState, url: null, isFetching: false };
|
|
82
81
|
const state = reducer(previousState, action);
|
|
83
82
|
|
|
84
|
-
expect(state.url).
|
|
85
|
-
expect(state.data).
|
|
86
|
-
expect(state.isFetching).
|
|
87
|
-
expect(state.fetchError).
|
|
88
|
-
expect(state.editError).
|
|
83
|
+
expect(state.url).toBe("https://example.com/book");
|
|
84
|
+
expect(state.data).toBeNull();
|
|
85
|
+
expect(state.isFetching).toBe(true);
|
|
86
|
+
expect(state.fetchError).toBeNull();
|
|
87
|
+
expect(state.editError).toBeNull();
|
|
89
88
|
});
|
|
90
89
|
it("should handle getBookData.fulfilled", () => {
|
|
91
90
|
const action = {
|
|
@@ -101,11 +100,11 @@ describe("Redux bookEditorSlice...", () => {
|
|
|
101
100
|
};
|
|
102
101
|
const state = reducer(previousState, action);
|
|
103
102
|
|
|
104
|
-
expect(state.url).
|
|
105
|
-
expect(state.data).
|
|
106
|
-
expect(state.isFetching).
|
|
107
|
-
expect(state.fetchError).
|
|
108
|
-
expect(state.editError).
|
|
103
|
+
expect(state.url).toBe("https://example.com/book");
|
|
104
|
+
expect(state.data).toStrictEqual(bookData);
|
|
105
|
+
expect(state.isFetching).toBe(false);
|
|
106
|
+
expect(state.fetchError).toBeNull();
|
|
107
|
+
expect(state.editError).toBeNull();
|
|
109
108
|
});
|
|
110
109
|
it("should handle getBookData.rejected", () => {
|
|
111
110
|
const errorObject = { error: "some error object" };
|
|
@@ -122,11 +121,11 @@ describe("Redux bookEditorSlice...", () => {
|
|
|
122
121
|
};
|
|
123
122
|
const state = reducer(previousState, action);
|
|
124
123
|
|
|
125
|
-
expect(state.url).
|
|
126
|
-
expect(state.data).
|
|
127
|
-
expect(state.isFetching).
|
|
128
|
-
expect(state.fetchError).
|
|
129
|
-
expect(state.editError).
|
|
124
|
+
expect(state.url).toBe("https://example.com/book");
|
|
125
|
+
expect(state.data).toBeNull();
|
|
126
|
+
expect(state.isFetching).toBe(false);
|
|
127
|
+
expect(state.fetchError).toStrictEqual(errorObject);
|
|
128
|
+
expect(state.editError).toBeNull();
|
|
130
129
|
});
|
|
131
130
|
|
|
132
131
|
it("should handle submitBookData.pending", () => {
|
|
@@ -134,7 +133,7 @@ describe("Redux bookEditorSlice...", () => {
|
|
|
134
133
|
const previousState = { ...fetchedState, isFetching: false };
|
|
135
134
|
const state = reducer(previousState, action);
|
|
136
135
|
|
|
137
|
-
expect(state).
|
|
136
|
+
expect(state).toStrictEqual({ ...fetchedState, isFetching: true });
|
|
138
137
|
});
|
|
139
138
|
it("should handle submitBookData.fulfilled", () => {
|
|
140
139
|
const action = {
|
|
@@ -144,7 +143,7 @@ describe("Redux bookEditorSlice...", () => {
|
|
|
144
143
|
const previousState = { ...fetchedState, isFetching: true };
|
|
145
144
|
const state = reducer(previousState, action);
|
|
146
145
|
|
|
147
|
-
expect(state).
|
|
146
|
+
expect(state).toStrictEqual({
|
|
148
147
|
...fetchedState,
|
|
149
148
|
isFetching: false,
|
|
150
149
|
editError: null,
|
|
@@ -158,7 +157,7 @@ describe("Redux bookEditorSlice...", () => {
|
|
|
158
157
|
const previousState = { ...fetchedState, isFetching: true };
|
|
159
158
|
const state = reducer(previousState, action);
|
|
160
159
|
|
|
161
|
-
expect(state).
|
|
160
|
+
expect(state).toStrictEqual({
|
|
162
161
|
...fetchedState,
|
|
163
162
|
isFetching: false,
|
|
164
163
|
editError: "some value",
|
|
@@ -200,22 +199,22 @@ describe("Redux bookEditorSlice...", () => {
|
|
|
200
199
|
const dispatchCalls = dispatch.mock.calls;
|
|
201
200
|
|
|
202
201
|
const payload = result.payload as BookData;
|
|
203
|
-
expect(payload.id).
|
|
202
|
+
expect(payload.id).toBe(
|
|
204
203
|
"urn:uuid:1cca9468-c447-4303-bc5a-c57470b85cb1"
|
|
205
204
|
);
|
|
206
|
-
expect(payload.title).
|
|
205
|
+
expect(payload.title).toBe(
|
|
207
206
|
"Tea-Cup Reading and Fortune-Telling by Tea Leaves"
|
|
208
207
|
);
|
|
209
208
|
|
|
210
|
-
expect(dispatchCalls.length).
|
|
211
|
-
expect(dispatchCalls[0][0].type).
|
|
212
|
-
expect(dispatchCalls[0][0].payload).
|
|
213
|
-
expect(dispatchCalls[0][0].meta.arg).
|
|
209
|
+
expect(dispatchCalls.length).toBe(2);
|
|
210
|
+
expect(dispatchCalls[0][0].type).toBe(getBookData.pending.type);
|
|
211
|
+
expect(dispatchCalls[0][0].payload).toBe(undefined);
|
|
212
|
+
expect(dispatchCalls[0][0].meta.arg).toStrictEqual({
|
|
214
213
|
url: goodBookUrl,
|
|
215
214
|
});
|
|
216
|
-
expect(dispatchCalls[1][0].type).
|
|
217
|
-
expect(dispatchCalls[1][0].payload).
|
|
218
|
-
expect(dispatchCalls[1][0].meta.arg).
|
|
215
|
+
expect(dispatchCalls[1][0].type).toBe(getBookData.fulfilled.type);
|
|
216
|
+
expect(dispatchCalls[1][0].payload).toStrictEqual(payload);
|
|
217
|
+
expect(dispatchCalls[1][0].meta.arg).toStrictEqual({
|
|
219
218
|
url: goodBookUrl,
|
|
220
219
|
});
|
|
221
220
|
});
|
|
@@ -226,18 +225,18 @@ describe("Redux bookEditorSlice...", () => {
|
|
|
226
225
|
const dispatchCalls = dispatch.mock.calls;
|
|
227
226
|
|
|
228
227
|
const payload = result.payload as RequestError;
|
|
229
|
-
expect(payload.response).
|
|
230
|
-
expect(payload.url).
|
|
228
|
+
expect(payload.response).toBe(FETCH_OPDS_PARSE_ERROR_MESSAGE);
|
|
229
|
+
expect(payload.url).toBe(brokenBookUrl);
|
|
231
230
|
|
|
232
|
-
expect(dispatchCalls.length).
|
|
233
|
-
expect(dispatchCalls[0][0].type).
|
|
234
|
-
expect(dispatchCalls[0][0].payload).
|
|
235
|
-
expect(dispatchCalls[0][0].meta.arg).
|
|
231
|
+
expect(dispatchCalls.length).toBe(2);
|
|
232
|
+
expect(dispatchCalls[0][0].type).toBe(getBookData.pending.type);
|
|
233
|
+
expect(dispatchCalls[0][0].payload).toBe(undefined);
|
|
234
|
+
expect(dispatchCalls[0][0].meta.arg).toStrictEqual({
|
|
236
235
|
url: brokenBookUrl,
|
|
237
236
|
});
|
|
238
|
-
expect(dispatchCalls[1][0].type).
|
|
239
|
-
expect(dispatchCalls[1][0].payload).
|
|
240
|
-
expect(dispatchCalls[1][0].meta.arg).
|
|
237
|
+
expect(dispatchCalls[1][0].type).toBe(getBookData.rejected.type);
|
|
238
|
+
expect(dispatchCalls[1][0].payload).toStrictEqual(payload);
|
|
239
|
+
expect(dispatchCalls[1][0].meta.arg).toStrictEqual({
|
|
241
240
|
url: brokenBookUrl,
|
|
242
241
|
});
|
|
243
242
|
});
|
|
@@ -249,20 +248,20 @@ describe("Redux bookEditorSlice...", () => {
|
|
|
249
248
|
|
|
250
249
|
const payload = result.payload as RequestError;
|
|
251
250
|
|
|
252
|
-
expect(result.type).
|
|
253
|
-
expect(result.meta.arg).
|
|
254
|
-
expect(payload.response).
|
|
255
|
-
expect(payload.url).
|
|
251
|
+
expect(result.type).toBe(getBookData.rejected.type);
|
|
252
|
+
expect(result.meta.arg).toStrictEqual({ url: errorBookUrl });
|
|
253
|
+
expect(payload.response).toBe("Internal server error");
|
|
254
|
+
expect(payload.url).toBe(errorBookUrl);
|
|
256
255
|
|
|
257
|
-
expect(dispatchCalls.length).
|
|
258
|
-
expect(dispatchCalls[0][0].type).
|
|
259
|
-
expect(dispatchCalls[0][0].payload).
|
|
260
|
-
expect(dispatchCalls[0][0].meta.arg).
|
|
256
|
+
expect(dispatchCalls.length).toBe(2);
|
|
257
|
+
expect(dispatchCalls[0][0].type).toBe(getBookData.pending.type);
|
|
258
|
+
expect(dispatchCalls[0][0].payload).toBe(undefined);
|
|
259
|
+
expect(dispatchCalls[0][0].meta.arg).toStrictEqual({
|
|
261
260
|
url: errorBookUrl,
|
|
262
261
|
});
|
|
263
|
-
expect(dispatchCalls[1][0].type).
|
|
264
|
-
expect(dispatchCalls[1][0].payload).
|
|
265
|
-
expect(dispatchCalls[1][0].meta.arg).
|
|
262
|
+
expect(dispatchCalls[1][0].type).toBe(getBookData.rejected.type);
|
|
263
|
+
expect(dispatchCalls[1][0].payload).toStrictEqual(payload);
|
|
264
|
+
expect(dispatchCalls[1][0].meta.arg).toStrictEqual({
|
|
266
265
|
url: errorBookUrl,
|
|
267
266
|
});
|
|
268
267
|
});
|
|
@@ -327,27 +326,23 @@ describe("Redux bookEditorSlice...", () => {
|
|
|
327
326
|
const dispatchCalls = dispatch.mock.calls;
|
|
328
327
|
const fetchCalls = fetchMock.calls();
|
|
329
328
|
|
|
330
|
-
expect(fetchCalls.length).
|
|
331
|
-
expect(fetchCalls[0].identifier).
|
|
332
|
-
expect(
|
|
333
|
-
|
|
334
|
-
)
|
|
329
|
+
expect(fetchCalls.length).toBe(1);
|
|
330
|
+
expect(fetchCalls[0].identifier).toBe("valid-csrf-token-post");
|
|
331
|
+
expect((fetchCalls[0][1].headers as Headers).get(csrfTokenHeader)).toBe(
|
|
332
|
+
validCsrfToken
|
|
333
|
+
);
|
|
335
334
|
|
|
336
|
-
expect(fetchCalls[0][0]).
|
|
337
|
-
expect(fetchCalls[0][1].method).
|
|
338
|
-
expect(fetchCalls[0][1].body).
|
|
335
|
+
expect(fetchCalls[0][0]).toBe(editBookUrl);
|
|
336
|
+
expect(fetchCalls[0][1].method).toBe("POST");
|
|
337
|
+
expect(fetchCalls[0][1].body).toBe(formData);
|
|
339
338
|
|
|
340
|
-
expect(dispatchCalls.length).
|
|
341
|
-
expect(dispatchCalls[0][0].type).
|
|
342
|
-
expect(dispatchCalls[0][0].payload).
|
|
339
|
+
expect(dispatchCalls.length).toBe(3);
|
|
340
|
+
expect(dispatchCalls[0][0].type).toBe(submitBookData.pending.type);
|
|
341
|
+
expect(dispatchCalls[0][0].payload).toBe(undefined);
|
|
343
342
|
// On a successful update, the second dispatch is to re-fetch the updated book data.
|
|
344
343
|
// The third dispatch is for the fulfilled action.
|
|
345
|
-
expect(dispatchCalls[2][0].type).
|
|
346
|
-
|
|
347
|
-
);
|
|
348
|
-
expect(dispatchCalls[2][0].payload.body.toString()).to.equal(
|
|
349
|
-
"Success!"
|
|
350
|
-
);
|
|
344
|
+
expect(dispatchCalls[2][0].type).toBe(submitBookData.fulfilled.type);
|
|
345
|
+
expect(dispatchCalls[2][0].payload.body.toString()).toBe("Success!");
|
|
351
346
|
});
|
|
352
347
|
it("should fail, if the user is unauthorized", async () => {
|
|
353
348
|
const csrfToken = "invalid-token";
|
|
@@ -365,24 +360,24 @@ describe("Redux bookEditorSlice...", () => {
|
|
|
365
360
|
const dispatchCalls = dispatch.mock.calls;
|
|
366
361
|
const fetchCalls = fetchMock.calls();
|
|
367
362
|
|
|
368
|
-
expect(fetchCalls.length).
|
|
369
|
-
expect(fetchCalls[0].identifier).
|
|
363
|
+
expect(fetchCalls.length).toBe(1);
|
|
364
|
+
expect(fetchCalls[0].identifier).toBe("invalid-csrf-token-post");
|
|
370
365
|
expect(
|
|
371
366
|
(fetchCalls[0][1].headers as Headers).get(csrfTokenHeader)
|
|
372
|
-
).not.
|
|
367
|
+
).not.toBe(validCsrfToken);
|
|
373
368
|
|
|
374
|
-
expect(fetchCalls[0][0]).
|
|
375
|
-
expect(fetchCalls[0][1].method).
|
|
376
|
-
expect(fetchCalls[0][1].body).
|
|
369
|
+
expect(fetchCalls[0][0]).toBe(editBookUrl);
|
|
370
|
+
expect(fetchCalls[0][1].method).toBe("POST");
|
|
371
|
+
expect(fetchCalls[0][1].body).toBe(formData);
|
|
377
372
|
|
|
378
|
-
expect(dispatchCalls.length).
|
|
379
|
-
expect(dispatchCalls[0][0].type).
|
|
380
|
-
expect(dispatchCalls[0][0].payload).
|
|
373
|
+
expect(dispatchCalls.length).toBe(2);
|
|
374
|
+
expect(dispatchCalls[0][0].type).toBe(submitBookData.pending.type);
|
|
375
|
+
expect(dispatchCalls[0][0].payload).toBe(undefined);
|
|
381
376
|
// There is no re-fetch on a failed request, ...
|
|
382
377
|
// ...so the second dispatch is for the rejected action.
|
|
383
|
-
expect(dispatchCalls[1][0].type).
|
|
384
|
-
expect(dispatchCalls[1][0].payload.status).
|
|
385
|
-
expect(dispatchCalls[1][0].payload.response).
|
|
378
|
+
expect(dispatchCalls[1][0].type).toBe(submitBookData.rejected.type);
|
|
379
|
+
expect(dispatchCalls[1][0].payload.status).toBe(400);
|
|
380
|
+
expect(dispatchCalls[1][0].payload.response).toBe(
|
|
386
381
|
"There was an error saving your changes."
|
|
387
382
|
);
|
|
388
383
|
});
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
// prettier-ignore
|
|
2
|
+
export default [
|
|
3
|
+
{
|
|
4
|
+
"name": "Fiction",
|
|
5
|
+
"source": "Library staff",
|
|
6
|
+
"type": "http://librarysimplified.org/terms/fiction/",
|
|
7
|
+
"weight": 10000000
|
|
8
|
+
},
|
|
9
|
+
{
|
|
10
|
+
"name": "Horror",
|
|
11
|
+
"source": "Library staff",
|
|
12
|
+
"type": "http://librarysimplified.org/terms/genres/Simplified/",
|
|
13
|
+
"weight": 100000
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
"name": "Adult",
|
|
17
|
+
"source": "Library staff",
|
|
18
|
+
"type": "schema:audience",
|
|
19
|
+
"weight": 100000
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
"name": "FICTION / Ghost",
|
|
23
|
+
"source": "Axis 360",
|
|
24
|
+
"type": "BISAC",
|
|
25
|
+
"weight": 1
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
"name": "FICTION / Horror",
|
|
29
|
+
"source": "Axis 360",
|
|
30
|
+
"type": "BISAC",
|
|
31
|
+
"weight": 1
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
"name": "General Adult",
|
|
35
|
+
"source": "Axis 360",
|
|
36
|
+
"type": "Axis 360 Audience",
|
|
37
|
+
"weight": 1
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
"name": "FICTION / Anthologies (multiple authors)",
|
|
41
|
+
"source": "Axis 360",
|
|
42
|
+
"type": "BISAC",
|
|
43
|
+
"weight": 1
|
|
44
|
+
},
|
|
45
|
+
];
|