@thepalaceproject/circulation-admin 0.0.0-post.1

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 (72) hide show
  1. package/.eslintrc +59 -0
  2. package/.node-version +1 -0
  3. package/.nvmrc +1 -0
  4. package/.prettierrc.json +10 -0
  5. package/.sass-lint.yml +37 -0
  6. package/CHANGELOG.md +946 -0
  7. package/README.md +155 -0
  8. package/dist/060b2710bdbbe3dfe48b58d59bd5f1fb.svg +288 -0
  9. package/dist/0db1520f47986b6c755a.svg +1 -0
  10. package/dist/1e59d2330b4c6deb84b3.ttf +0 -0
  11. package/dist/20fd1704ea223900efa9.woff2 +0 -0
  12. package/dist/4692b9ec53fd5972caa2.ttf +0 -0
  13. package/dist/5be1347c682810f199c7.eot +0 -0
  14. package/dist/6563aa3790be8329e4f2.svg +1 -0
  15. package/dist/82b1212e45a2bc35dd73.woff +0 -0
  16. package/dist/8b43027f47b20503057d.eot +0 -0
  17. package/dist/PalaceCollectionManagerLogo.svg +122 -0
  18. package/dist/be810be3a3e14c682a25.woff2 +0 -0
  19. package/dist/c1e38fd9e0e74ba58f7a2b77ef29fdd3.svg +2671 -0
  20. package/dist/circulation-admin.css +6841 -0
  21. package/dist/circulation-admin.js +2 -0
  22. package/dist/circulation-admin.js.LICENSE.txt +153 -0
  23. package/dist/f691f37e57f04c152e23.woff +0 -0
  24. package/jest.config.js +15 -0
  25. package/jest.polyfills.js +12 -0
  26. package/nightwatch.json +58 -0
  27. package/package.json +155 -0
  28. package/pull_request_template.md +22 -0
  29. package/requirements-ci.txt +1 -0
  30. package/testReporter.js +31 -0
  31. package/tests/__data__/statisticsApiResponseData.ts +327 -0
  32. package/tests/__mocks__/fileMock.js +1 -0
  33. package/tests/__mocks__/styleMock.js +1 -0
  34. package/tests/browser/README.md +19 -0
  35. package/tests/browser/assertions/noError.js +38 -0
  36. package/tests/browser/commands/goHome.js +13 -0
  37. package/tests/browser/commands/signIn.js +18 -0
  38. package/tests/browser/globals.js.sample +5 -0
  39. package/tests/browser/navigate.js +294 -0
  40. package/tests/browser/pages/book.js +21 -0
  41. package/tests/browser/pages/catalog.js +24 -0
  42. package/tests/browser/pages/login.js +11 -0
  43. package/tests/browser/redirect.js +104 -0
  44. package/tests/browser/signInFailure.js +22 -0
  45. package/tests/jest/README.md +6 -0
  46. package/tests/jest/api/admin.test.ts +60 -0
  47. package/tests/jest/businessRules/roleBasedAccess.test.ts +250 -0
  48. package/tests/jest/components/AdvancedSearchBuilder.test.tsx +38 -0
  49. package/tests/jest/components/BookEditor.test.tsx +240 -0
  50. package/tests/jest/components/CirculationEventsDownload.test.tsx +65 -0
  51. package/tests/jest/components/CustomLists.test.tsx +203 -0
  52. package/tests/jest/components/EditableInput.test.tsx +64 -0
  53. package/tests/jest/components/IndividualAdminEditForm.test.tsx +128 -0
  54. package/tests/jest/components/InventoryReportRequestModal.test.tsx +652 -0
  55. package/tests/jest/components/Lane.test.tsx +78 -0
  56. package/tests/jest/components/LaneEditor.test.tsx +148 -0
  57. package/tests/jest/components/ProtocolFormField.test.tsx +37 -0
  58. package/tests/jest/components/QuicksightDashboard.test.tsx +67 -0
  59. package/tests/jest/components/Stats.test.tsx +699 -0
  60. package/tests/jest/context/AppContext.test.tsx +113 -0
  61. package/tests/jest/features/book.test.ts +396 -0
  62. package/tests/jest/jest-setup.ts +1 -0
  63. package/tests/jest/sample/sample.test.js +3 -0
  64. package/tests/jest/testUtils/renderWithContext.tsx +38 -0
  65. package/tests/jest/testUtils/withProviders.tsx +92 -0
  66. package/tests/jest/utils/NoCacheDataFetcher.test.ts +75 -0
  67. package/tsconfig.json +25 -0
  68. package/tslint.json +56 -0
  69. package/webpack.common.js +72 -0
  70. package/webpack.dev-server.config.js +215 -0
  71. package/webpack.dev.config.js +9 -0
  72. package/webpack.prod.config.js +8 -0
@@ -0,0 +1,250 @@
1
+ import { renderHook } from "@testing-library/react-hooks";
2
+ import { componentWithProviders } from "../testUtils/withProviders";
3
+ import { ConfigurationSettings, FeatureFlags } from "../../../src/interfaces";
4
+ import {
5
+ useMayRequestInventoryReports,
6
+ useMaySeeQuickSightLink,
7
+ useMayViewCollectionBarChart,
8
+ } from "../../../src/businessRules/roleBasedAccess";
9
+
10
+ const setupWrapper = ({
11
+ roles,
12
+ featureFlags,
13
+ }: Partial<ConfigurationSettings>) => {
14
+ const appConfigSettings: Partial<ConfigurationSettings> = {
15
+ featureFlags,
16
+ roles,
17
+ email: "email",
18
+ csrfToken: "token",
19
+ };
20
+ return componentWithProviders({ appConfigSettings });
21
+ };
22
+
23
+ describe("Business rules for role-based access", () => {
24
+ const libraryMatch = "match";
25
+ const libraryMismatch = "mismatch";
26
+
27
+ describe("controls access to inventory reports", () => {
28
+ const testAccess = (
29
+ expectedResult: boolean,
30
+ config: Partial<ConfigurationSettings>
31
+ ) => {
32
+ const wrapper = setupWrapper(config);
33
+ const { result } = renderHook(
34
+ () => useMayRequestInventoryReports({ library: libraryMatch }),
35
+ { wrapper }
36
+ );
37
+ expect(result.current).toBe(expectedResult);
38
+ };
39
+
40
+ it("restricts access to only sysadmins, if the restriction feature flag is true", () => {
41
+ const featureFlags: FeatureFlags = { reportsOnlyForSysadmins: true };
42
+
43
+ testAccess(true, { roles: [{ role: "system" }], featureFlags });
44
+
45
+ testAccess(false, { roles: [{ role: "manager-all" }], featureFlags });
46
+ testAccess(false, { roles: [{ role: "librarian-all" }], featureFlags });
47
+
48
+ testAccess(false, {
49
+ roles: [{ role: "manager", library: libraryMatch }],
50
+ featureFlags,
51
+ });
52
+ testAccess(false, {
53
+ roles: [{ role: "manager", library: libraryMismatch }],
54
+ featureFlags,
55
+ });
56
+ testAccess(false, {
57
+ roles: [{ role: "librarian", library: libraryMatch }],
58
+ featureFlags,
59
+ });
60
+ testAccess(false, {
61
+ roles: [{ role: "librarian", library: libraryMismatch }],
62
+ featureFlags,
63
+ });
64
+ });
65
+
66
+ it("allows all users, if the restriction feature flag is is false", () => {
67
+ const featureFlags: FeatureFlags = { reportsOnlyForSysadmins: false };
68
+
69
+ testAccess(true, { roles: [{ role: "system" }], featureFlags });
70
+
71
+ testAccess(true, { roles: [{ role: "manager-all" }], featureFlags });
72
+ testAccess(true, { roles: [{ role: "librarian-all" }], featureFlags });
73
+
74
+ testAccess(true, {
75
+ roles: [{ role: "manager", library: libraryMatch }],
76
+ featureFlags,
77
+ });
78
+ testAccess(true, {
79
+ roles: [{ role: "manager", library: libraryMismatch }],
80
+ featureFlags,
81
+ });
82
+ testAccess(true, {
83
+ roles: [{ role: "librarian", library: libraryMatch }],
84
+ featureFlags,
85
+ });
86
+ testAccess(true, {
87
+ roles: [{ role: "librarian", library: libraryMismatch }],
88
+ featureFlags,
89
+ });
90
+ });
91
+
92
+ it("allows all users, if the restriction feature flag is not set", () => {
93
+ const featureFlags: FeatureFlags = {};
94
+
95
+ testAccess(true, { roles: [{ role: "system" }], featureFlags });
96
+
97
+ testAccess(true, { roles: [{ role: "manager-all" }], featureFlags });
98
+ testAccess(true, { roles: [{ role: "librarian-all" }], featureFlags });
99
+
100
+ testAccess(true, {
101
+ roles: [{ role: "manager", library: libraryMatch }],
102
+ featureFlags,
103
+ });
104
+ testAccess(true, {
105
+ roles: [{ role: "manager", library: libraryMismatch }],
106
+ featureFlags,
107
+ });
108
+ testAccess(true, {
109
+ roles: [{ role: "librarian", library: libraryMatch }],
110
+ featureFlags,
111
+ });
112
+ testAccess(true, {
113
+ roles: [{ role: "librarian", library: libraryMismatch }],
114
+ featureFlags,
115
+ });
116
+ });
117
+ });
118
+
119
+ describe("controls access to the quicksight link", () => {
120
+ const testAccess = (
121
+ expectedResult: boolean,
122
+ config: Partial<ConfigurationSettings>
123
+ ) => {
124
+ const wrapper = setupWrapper(config);
125
+ const { result } = renderHook(
126
+ () => useMaySeeQuickSightLink({ library: libraryMatch }),
127
+ { wrapper }
128
+ );
129
+ expect(result.current).toBe(expectedResult);
130
+ };
131
+
132
+ it("restricts access to only sysadmins, if the restriction feature flag is true", () => {
133
+ const featureFlags: FeatureFlags = { quicksightOnlyForSysadmins: true };
134
+
135
+ testAccess(true, { roles: [{ role: "system" }], featureFlags });
136
+
137
+ testAccess(false, { roles: [{ role: "manager-all" }], featureFlags });
138
+ testAccess(false, { roles: [{ role: "librarian-all" }], featureFlags });
139
+
140
+ testAccess(false, {
141
+ roles: [{ role: "manager", library: libraryMatch }],
142
+ featureFlags,
143
+ });
144
+ testAccess(false, {
145
+ roles: [{ role: "manager", library: libraryMismatch }],
146
+ featureFlags,
147
+ });
148
+ testAccess(false, {
149
+ roles: [{ role: "librarian", library: libraryMatch }],
150
+ featureFlags,
151
+ });
152
+ testAccess(false, {
153
+ roles: [{ role: "librarian", library: libraryMismatch }],
154
+ featureFlags,
155
+ });
156
+ });
157
+
158
+ it("allows all users, if the restriction feature flag is is false", () => {
159
+ const featureFlags: FeatureFlags = { quicksightOnlyForSysadmins: false };
160
+
161
+ testAccess(true, { roles: [{ role: "system" }], featureFlags });
162
+
163
+ testAccess(true, { roles: [{ role: "manager-all" }], featureFlags });
164
+ testAccess(true, { roles: [{ role: "librarian-all" }], featureFlags });
165
+
166
+ testAccess(true, {
167
+ roles: [{ role: "manager", library: libraryMatch }],
168
+ featureFlags,
169
+ });
170
+ testAccess(true, {
171
+ roles: [{ role: "manager", library: libraryMismatch }],
172
+ featureFlags,
173
+ });
174
+ testAccess(true, {
175
+ roles: [{ role: "librarian", library: libraryMatch }],
176
+ featureFlags,
177
+ });
178
+ testAccess(true, {
179
+ roles: [{ role: "librarian", library: libraryMismatch }],
180
+ featureFlags,
181
+ });
182
+ });
183
+
184
+ it("allows all users, if the restriction feature flag is not set", () => {
185
+ const featureFlags: FeatureFlags = {};
186
+
187
+ testAccess(true, { roles: [{ role: "system" }], featureFlags });
188
+
189
+ testAccess(true, { roles: [{ role: "manager-all" }], featureFlags });
190
+ testAccess(true, { roles: [{ role: "librarian-all" }], featureFlags });
191
+
192
+ testAccess(true, {
193
+ roles: [{ role: "manager", library: libraryMatch }],
194
+ featureFlags,
195
+ });
196
+ testAccess(true, {
197
+ roles: [{ role: "manager", library: libraryMismatch }],
198
+ featureFlags,
199
+ });
200
+ testAccess(true, {
201
+ roles: [{ role: "librarian", library: libraryMatch }],
202
+ featureFlags,
203
+ });
204
+ testAccess(true, {
205
+ roles: [{ role: "librarian", library: libraryMismatch }],
206
+ featureFlags,
207
+ });
208
+ });
209
+ });
210
+
211
+ describe("controls access to the collection statistics barchart", () => {
212
+ const testAccess = (
213
+ expectedResult: boolean,
214
+ config: Partial<ConfigurationSettings>
215
+ ) => {
216
+ const wrapper = setupWrapper(config);
217
+ const { result } = renderHook(
218
+ () => useMayViewCollectionBarChart({ library: libraryMatch }),
219
+ { wrapper }
220
+ );
221
+ expect(result.current).toBe(expectedResult);
222
+ };
223
+
224
+ it("restricts access to sysadmins", () => {
225
+ const featureFlags: FeatureFlags = {};
226
+
227
+ testAccess(true, { roles: [{ role: "system" }], featureFlags });
228
+
229
+ testAccess(false, { roles: [{ role: "manager-all" }], featureFlags });
230
+ testAccess(false, { roles: [{ role: "librarian-all" }], featureFlags });
231
+
232
+ testAccess(false, {
233
+ roles: [{ role: "manager", library: libraryMatch }],
234
+ featureFlags,
235
+ });
236
+ testAccess(false, {
237
+ roles: [{ role: "manager", library: libraryMismatch }],
238
+ featureFlags,
239
+ });
240
+ testAccess(false, {
241
+ roles: [{ role: "librarian", library: libraryMatch }],
242
+ featureFlags,
243
+ });
244
+ testAccess(false, {
245
+ roles: [{ role: "librarian", library: libraryMismatch }],
246
+ featureFlags,
247
+ });
248
+ });
249
+ });
250
+ });
@@ -0,0 +1,38 @@
1
+ import * as React from "react";
2
+ import { render, screen } from "@testing-library/react";
3
+ import userEvent from "@testing-library/user-event";
4
+ import AdvancedSearchBuilder from "../../../src/components/AdvancedSearchBuilder";
5
+
6
+ describe("AdvancedSearchBuilder", () => {
7
+ it("renders a placeholder and help text when the publication date filter is selected", async () => {
8
+ const user = userEvent.setup();
9
+
10
+ const query = {
11
+ id: "0",
12
+ key: "genre",
13
+ value: "Horror",
14
+ };
15
+
16
+ render(
17
+ <AdvancedSearchBuilder
18
+ isOwner={true}
19
+ name="search"
20
+ query={query}
21
+ selectedQueryId="0"
22
+ />
23
+ );
24
+
25
+ await userEvent.selectOptions(
26
+ screen.getByRole("combobox", { name: "filter field key" }),
27
+ screen.getByRole("option", { name: "Publication Date" })
28
+ );
29
+
30
+ const filterValueField = screen.getByRole("textbox", {
31
+ name: "filter value",
32
+ });
33
+
34
+ expect(filterValueField).toHaveAttribute("placeholder", "YYYY-MM-DD");
35
+ expect(filterValueField).toHaveAccessibleDescription(/publication date/i);
36
+ expect(filterValueField).toHaveAccessibleDescription(/YYYY-MM-DD/i);
37
+ });
38
+ });
@@ -0,0 +1,240 @@
1
+ import * as React from "react";
2
+ import { renderWithProviders } from "../testUtils/withProviders";
3
+ import userEvent from "@testing-library/user-event";
4
+ import {
5
+ bookEditorApiEndpoints,
6
+ PER_LIBRARY_SUPPRESS_REL,
7
+ PER_LIBRARY_UNSUPPRESS_REL,
8
+ } from "../../../src/features/book/bookEditorSlice";
9
+ import { BookDetailsEditor } from "../../../src/components/BookDetailsEditor";
10
+ import { expect } from "chai";
11
+ import { store } from "../../../src/store";
12
+ import * as fetchMock from "fetch-mock-jest";
13
+
14
+ describe("BookDetails", () => {
15
+ const suppressPerLibraryLink = {
16
+ href: "/suppress/href",
17
+ rel: PER_LIBRARY_SUPPRESS_REL,
18
+ };
19
+ const unsuppressPerLibraryLink = {
20
+ href: "/unsuppress/href",
21
+ rel: PER_LIBRARY_UNSUPPRESS_REL,
22
+ };
23
+
24
+ let fetchBookData;
25
+ let fetchRoles;
26
+ let fetchMedia;
27
+ let fetchLanguages;
28
+ let postBookData;
29
+ let dispatchProps;
30
+ const suppressBook = jest.fn().mockImplementation((url: string) =>
31
+ store.dispatch(
32
+ bookEditorApiEndpoints.endpoints.suppressBook.initiate({
33
+ url,
34
+ csrfToken: "token",
35
+ })
36
+ )
37
+ );
38
+ const unsuppressBook = jest.fn().mockImplementation((url: string) =>
39
+ store.dispatch(
40
+ bookEditorApiEndpoints.endpoints.unsuppressBook.initiate({
41
+ url,
42
+ csrfToken: "token",
43
+ })
44
+ )
45
+ );
46
+
47
+ beforeAll(() => {
48
+ fetchMock
49
+ .post("/suppress/href", {
50
+ status: 200,
51
+ body: { message: "Successfully suppressed book availability." },
52
+ })
53
+ .delete("/unsuppress/href", {
54
+ status: 200,
55
+ body: { message: "Successfully unsuppressed book availability." },
56
+ });
57
+ });
58
+ beforeEach(() => {
59
+ fetchBookData = jest.fn();
60
+ fetchRoles = jest.fn();
61
+ fetchMedia = jest.fn();
62
+ fetchLanguages = jest.fn();
63
+ postBookData = jest.fn();
64
+ dispatchProps = {
65
+ fetchBookData,
66
+ fetchRoles,
67
+ fetchMedia,
68
+ fetchLanguages,
69
+ postBookData,
70
+ suppressBook,
71
+ unsuppressBook,
72
+ };
73
+ });
74
+ afterEach(() => {
75
+ jest.clearAllMocks();
76
+ fetchMock.resetHistory();
77
+ });
78
+ afterAll(() => {
79
+ jest.restoreAllMocks();
80
+ fetchMock.restore();
81
+ });
82
+
83
+ it("don't show hide button if not a library's admin", () => {
84
+ const { queryByRole } = renderWithProviders(
85
+ <BookDetailsEditor
86
+ bookData={{ id: "id", title: "title", suppressPerLibraryLink }}
87
+ bookUrl="url"
88
+ csrfToken="token"
89
+ canSuppress={false}
90
+ {...dispatchProps}
91
+ />
92
+ );
93
+ const hideButton = queryByRole("button", { name: "Hide" });
94
+ const restoreButton = queryByRole("button", { name: "Restore" });
95
+
96
+ expect(hideButton).to.be.null;
97
+ expect(restoreButton).to.be.null;
98
+ });
99
+
100
+ it("don't show restore button if not a library's admin", () => {
101
+ const { queryByRole } = renderWithProviders(
102
+ <BookDetailsEditor
103
+ bookData={{ id: "id", title: "title", unsuppressPerLibraryLink }}
104
+ bookUrl="url"
105
+ csrfToken="token"
106
+ canSuppress={false}
107
+ {...dispatchProps}
108
+ />
109
+ );
110
+ const hideButton = queryByRole("button", { name: "Hide" });
111
+ const restoreButton = queryByRole("button", { name: "Restore" });
112
+
113
+ expect(hideButton).to.be.null;
114
+ expect(restoreButton).to.be.null;
115
+ });
116
+
117
+ it("uses modal for suppress book confirmation", async () => {
118
+ // Configure standard constructors so that RTK Query works in tests with FetchMockJest
119
+ Object.assign(fetchMock.config, {
120
+ fetch,
121
+ Headers,
122
+ Request,
123
+ Response,
124
+ });
125
+
126
+ const user = userEvent.setup();
127
+
128
+ const { getByRole, getByText, queryByRole } = renderWithProviders(
129
+ <BookDetailsEditor
130
+ bookData={{ id: "id", title: "title", suppressPerLibraryLink }}
131
+ bookUrl="url"
132
+ csrfToken="token"
133
+ canSuppress={true}
134
+ {...dispatchProps}
135
+ />
136
+ );
137
+
138
+ // The `Hide` button should be present.
139
+ const hideButton = getByRole("button", { name: "Hide" });
140
+
141
+ // Clicking `Hide` should show the book suppression modal.
142
+ await user.click(hideButton);
143
+ getByRole("heading", { level: 4, name: "Suppressing Availability" });
144
+ getByText(/to hide this title from your library's catalog/);
145
+ let confirmButton = getByRole("button", { name: "Suppress Availability" });
146
+ let cancelButton = getByRole("button", { name: "Cancel" });
147
+
148
+ // Clicking `Cancel` should close the modal.
149
+ await user.click(cancelButton);
150
+ confirmButton = queryByRole("button", { name: "Suppress Availability" });
151
+ cancelButton = queryByRole("button", { name: "Cancel" });
152
+ expect(confirmButton).to.be.null;
153
+ expect(cancelButton).to.be.null;
154
+
155
+ // Clicking `Hide` again should show the modal again.
156
+ await user.click(hideButton);
157
+ confirmButton = getByRole("button", { name: "Suppress Availability" });
158
+
159
+ // Clicking the confirmation button should invoke the API and show a confirmation.
160
+ await user.click(confirmButton);
161
+ getByRole("heading", { level: 4, name: "Result" });
162
+ getByText(/Successfully suppressed book availability/);
163
+ getByRole("button", { name: "Dismiss" });
164
+
165
+ // Check that the API was invoked.
166
+ expect(suppressBook.mock.calls.length).to.equal(1);
167
+ expect(suppressBook.mock.calls[0][0]).to.equal("/suppress/href");
168
+ const fetchCalls = fetchMock.calls();
169
+ expect(fetchCalls.length).to.equal(1);
170
+ const fetchCall = fetchCalls[0];
171
+ const fetchOptions = fetchCalls[0][1];
172
+ expect(fetchCall[0]).to.equal("/suppress/href");
173
+ expect(fetchOptions["headers"]["X-CSRF-Token"]).to.contain("token");
174
+ expect(fetchOptions["method"]).to.equal("POST");
175
+ });
176
+ it("uses modal for unsuppress book confirmation", async () => {
177
+ // Configure standard constructors so that RTK Query works in tests with FetchMockJest
178
+ Object.assign(fetchMock.config, {
179
+ fetch,
180
+ Headers,
181
+ Request,
182
+ Response,
183
+ });
184
+
185
+ const user = userEvent.setup();
186
+
187
+ const { getByRole, getByText, queryByRole } = renderWithProviders(
188
+ <BookDetailsEditor
189
+ bookData={{
190
+ id: "id",
191
+ title: "title",
192
+ unsuppressPerLibraryLink,
193
+ visibilityStatus: "manually-suppressed",
194
+ }}
195
+ bookUrl="url"
196
+ csrfToken="token"
197
+ canSuppress={true}
198
+ {...dispatchProps}
199
+ />
200
+ );
201
+
202
+ // The `Restore` button should be present.
203
+ const restoreButton = getByRole("button", { name: "Restore" });
204
+
205
+ // Clicking `Restore` should show the book un/suppression modal.
206
+ await user.click(restoreButton);
207
+ getByRole("heading", { level: 4, name: "Restoring Availability" });
208
+ getByText(/to make this title visible in your library's catalog/);
209
+ let confirmButton = getByRole("button", { name: "Restore Availability" });
210
+ let cancelButton = getByRole("button", { name: "Cancel" });
211
+
212
+ // Clicking `Cancel` should close the modal.
213
+ await user.click(cancelButton);
214
+ confirmButton = queryByRole("button", { name: "Restore Availability" });
215
+ cancelButton = queryByRole("button", { name: "Cancel" });
216
+ expect(confirmButton).to.be.null;
217
+ expect(cancelButton).to.be.null;
218
+
219
+ // Clicking `Restore` again should show the modal again.
220
+ await user.click(restoreButton);
221
+ confirmButton = getByRole("button", { name: "Restore Availability" });
222
+
223
+ // Clicking the confirmation button should invoke the API and show a confirmation.
224
+ await user.click(confirmButton);
225
+ getByRole("heading", { level: 4, name: "Result" });
226
+ getByText(/Successfully unsuppressed book availability/);
227
+ getByRole("button", { name: "Dismiss" });
228
+
229
+ // Check that the API was invoked.
230
+ expect(unsuppressBook.mock.calls.length).to.equal(1);
231
+ expect(unsuppressBook.mock.calls[0][0]).to.equal("/unsuppress/href");
232
+ const fetchCalls = fetchMock.calls();
233
+ expect(fetchCalls.length).to.equal(1);
234
+ const fetchCall = fetchCalls[0];
235
+ const fetchOptions = fetchCalls[0][1];
236
+ expect(fetchCall[0]).to.equal("/unsuppress/href");
237
+ expect(fetchOptions["headers"]["X-CSRF-Token"]).to.contain("token");
238
+ expect(fetchOptions["method"]).to.equal("DELETE");
239
+ });
240
+ });
@@ -0,0 +1,65 @@
1
+ import * as React from "react";
2
+ import { screen, fireEvent } from "@testing-library/react";
3
+ import "@testing-library/jest-dom";
4
+
5
+ import CirculationEventsDownload from "../../../src/components/CirculationEventsDownload";
6
+ import { ConfigurationSettings, FeatureFlags } from "../../../src/interfaces";
7
+ import { defaultFeatureFlags } from "../../../src/utils/featureFlags";
8
+ import { renderWithProviders } from "../testUtils/withProviders";
9
+
10
+ describe("CirculationEventsDownload", () => {
11
+ it("renders nothing if showCircEventsDownload feature flag is false", () => {
12
+ const featureFlags: FeatureFlags = {
13
+ ...defaultFeatureFlags,
14
+ showCircEventsDownload: false,
15
+ };
16
+ const appConfigSettings: Partial<ConfigurationSettings> = {
17
+ featureFlags,
18
+ };
19
+ const { container } = renderWithProviders(
20
+ <CirculationEventsDownload library="testlib" />,
21
+ {
22
+ appConfigSettings,
23
+ }
24
+ );
25
+ expect(container).toBeEmptyDOMElement();
26
+ });
27
+
28
+ describe("when showCircEventsDownload feature flag is true", () => {
29
+ const featureFlags: FeatureFlags = {
30
+ ...defaultFeatureFlags,
31
+ showCircEventsDownload: true,
32
+ };
33
+ const libraryProp = "testlib";
34
+ const appConfigSettings: Partial<ConfigurationSettings> = {
35
+ featureFlags,
36
+ };
37
+
38
+ beforeEach(() => {
39
+ renderWithProviders(<CirculationEventsDownload library={libraryProp} />, {
40
+ appConfigSettings,
41
+ });
42
+ });
43
+
44
+ it("renders a download button", () => {
45
+ expect(
46
+ screen.getByRole("button", { name: "Download CSV" })
47
+ ).toBeInTheDocument();
48
+ });
49
+
50
+ it("shows the form when the download button is clicked", () => {
51
+ const downloadButton = screen.getByRole("button", {
52
+ name: "Download CSV",
53
+ });
54
+ fireEvent.click(downloadButton);
55
+ expect(
56
+ screen.getByRole("heading", { name: "Download CSV" })
57
+ ).toBeInTheDocument();
58
+ expect(
59
+ screen.getByRole("button", { name: "Download" })
60
+ ).toBeInTheDocument();
61
+ expect(screen.getByText("Start Date")).toBeInTheDocument();
62
+ expect(screen.getByText("End Date")).toBeInTheDocument();
63
+ });
64
+ });
65
+ });