@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.
- package/.eslintrc +59 -0
- package/.node-version +1 -0
- package/.nvmrc +1 -0
- package/.prettierrc.json +10 -0
- package/.sass-lint.yml +37 -0
- package/CHANGELOG.md +946 -0
- package/README.md +155 -0
- package/dist/060b2710bdbbe3dfe48b58d59bd5f1fb.svg +288 -0
- package/dist/0db1520f47986b6c755a.svg +1 -0
- package/dist/1e59d2330b4c6deb84b3.ttf +0 -0
- package/dist/20fd1704ea223900efa9.woff2 +0 -0
- package/dist/4692b9ec53fd5972caa2.ttf +0 -0
- package/dist/5be1347c682810f199c7.eot +0 -0
- package/dist/6563aa3790be8329e4f2.svg +1 -0
- package/dist/82b1212e45a2bc35dd73.woff +0 -0
- package/dist/8b43027f47b20503057d.eot +0 -0
- package/dist/PalaceCollectionManagerLogo.svg +122 -0
- package/dist/be810be3a3e14c682a25.woff2 +0 -0
- package/dist/c1e38fd9e0e74ba58f7a2b77ef29fdd3.svg +2671 -0
- package/dist/circulation-admin.css +6841 -0
- package/dist/circulation-admin.js +2 -0
- package/dist/circulation-admin.js.LICENSE.txt +153 -0
- package/dist/f691f37e57f04c152e23.woff +0 -0
- package/jest.config.js +15 -0
- package/jest.polyfills.js +12 -0
- package/nightwatch.json +58 -0
- package/package.json +155 -0
- package/pull_request_template.md +22 -0
- package/requirements-ci.txt +1 -0
- package/testReporter.js +31 -0
- package/tests/__data__/statisticsApiResponseData.ts +327 -0
- package/tests/__mocks__/fileMock.js +1 -0
- package/tests/__mocks__/styleMock.js +1 -0
- package/tests/browser/README.md +19 -0
- package/tests/browser/assertions/noError.js +38 -0
- package/tests/browser/commands/goHome.js +13 -0
- package/tests/browser/commands/signIn.js +18 -0
- package/tests/browser/globals.js.sample +5 -0
- package/tests/browser/navigate.js +294 -0
- package/tests/browser/pages/book.js +21 -0
- package/tests/browser/pages/catalog.js +24 -0
- package/tests/browser/pages/login.js +11 -0
- package/tests/browser/redirect.js +104 -0
- package/tests/browser/signInFailure.js +22 -0
- package/tests/jest/README.md +6 -0
- package/tests/jest/api/admin.test.ts +60 -0
- package/tests/jest/businessRules/roleBasedAccess.test.ts +250 -0
- package/tests/jest/components/AdvancedSearchBuilder.test.tsx +38 -0
- package/tests/jest/components/BookEditor.test.tsx +240 -0
- package/tests/jest/components/CirculationEventsDownload.test.tsx +65 -0
- package/tests/jest/components/CustomLists.test.tsx +203 -0
- package/tests/jest/components/EditableInput.test.tsx +64 -0
- package/tests/jest/components/IndividualAdminEditForm.test.tsx +128 -0
- package/tests/jest/components/InventoryReportRequestModal.test.tsx +652 -0
- package/tests/jest/components/Lane.test.tsx +78 -0
- package/tests/jest/components/LaneEditor.test.tsx +148 -0
- package/tests/jest/components/ProtocolFormField.test.tsx +37 -0
- package/tests/jest/components/QuicksightDashboard.test.tsx +67 -0
- package/tests/jest/components/Stats.test.tsx +699 -0
- package/tests/jest/context/AppContext.test.tsx +113 -0
- package/tests/jest/features/book.test.ts +396 -0
- package/tests/jest/jest-setup.ts +1 -0
- package/tests/jest/sample/sample.test.js +3 -0
- package/tests/jest/testUtils/renderWithContext.tsx +38 -0
- package/tests/jest/testUtils/withProviders.tsx +92 -0
- package/tests/jest/utils/NoCacheDataFetcher.test.ts +75 -0
- package/tsconfig.json +25 -0
- package/tslint.json +56 -0
- package/webpack.common.js +72 -0
- package/webpack.dev-server.config.js +215 -0
- package/webpack.dev.config.js +9 -0
- package/webpack.prod.config.js +8 -0
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { screen, waitFor } from "@testing-library/react";
|
|
3
|
+
import userEvent from "@testing-library/user-event";
|
|
4
|
+
import { setupServer } from "msw/node";
|
|
5
|
+
import { http, HttpResponse } from "msw";
|
|
6
|
+
import CustomLists from "../../../src/components/CustomLists";
|
|
7
|
+
import renderWithContext from "../testUtils/renderWithContext";
|
|
8
|
+
import buildStore from "../../../src/store";
|
|
9
|
+
import { ConfigurationSettings } from "../../../src/interfaces";
|
|
10
|
+
|
|
11
|
+
describe("CustomLists", () => {
|
|
12
|
+
// Stub scrollTo, since a component in the render tree will try to call it, and it is not
|
|
13
|
+
// provided by JSDOM.
|
|
14
|
+
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
|
15
|
+
Element.prototype.scrollTo = () => {};
|
|
16
|
+
|
|
17
|
+
const server = setupServer(
|
|
18
|
+
http.get("*/search", () => HttpResponse.xml("<feed />")),
|
|
19
|
+
http.get("*", () => HttpResponse.json({}))
|
|
20
|
+
);
|
|
21
|
+
|
|
22
|
+
beforeAll(() => {
|
|
23
|
+
server.listen();
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
afterAll(() => {
|
|
27
|
+
server.close();
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
it("adds filters when new filter values are entered", async () => {
|
|
31
|
+
const user = userEvent.setup();
|
|
32
|
+
|
|
33
|
+
const appConfigSettings: Partial<ConfigurationSettings> = {
|
|
34
|
+
csrfToken: "",
|
|
35
|
+
featureFlags: {},
|
|
36
|
+
roles: [{ role: "system" }],
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
renderWithContext(
|
|
40
|
+
<CustomLists
|
|
41
|
+
csrfToken=""
|
|
42
|
+
editOrCreate="create"
|
|
43
|
+
library="testlib"
|
|
44
|
+
store={buildStore()}
|
|
45
|
+
/>,
|
|
46
|
+
appConfigSettings
|
|
47
|
+
);
|
|
48
|
+
|
|
49
|
+
await user.click(screen.getByRole("textbox", { name: "filter value" }));
|
|
50
|
+
await user.keyboard("horror{enter}");
|
|
51
|
+
|
|
52
|
+
const items = screen.getAllByRole("treeitem");
|
|
53
|
+
|
|
54
|
+
expect(items).toHaveLength(1);
|
|
55
|
+
expect(items[0]).toHaveTextContent(/Genre = horror/);
|
|
56
|
+
|
|
57
|
+
await user.click(screen.getByRole("textbox", { name: "filter value" }));
|
|
58
|
+
await user.keyboard("science fiction{enter}");
|
|
59
|
+
|
|
60
|
+
const newItems = screen.getAllByRole("treeitem");
|
|
61
|
+
|
|
62
|
+
expect(newItems).toHaveLength(3);
|
|
63
|
+
expect(newItems[0]).toHaveTextContent(
|
|
64
|
+
/all of these filters must be matched/i
|
|
65
|
+
);
|
|
66
|
+
expect(newItems[1]).toHaveTextContent(/genre = horror/i);
|
|
67
|
+
expect(newItems[2]).toHaveTextContent(/genre = science fiction/i);
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
it("replaces the existing filters when adding a new filter when the clear filters checkbox is checked", async () => {
|
|
71
|
+
const user = userEvent.setup();
|
|
72
|
+
|
|
73
|
+
const appConfigSettings: Partial<ConfigurationSettings> = {
|
|
74
|
+
csrfToken: "",
|
|
75
|
+
featureFlags: {},
|
|
76
|
+
roles: [{ role: "system" }],
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
renderWithContext(
|
|
80
|
+
<CustomLists
|
|
81
|
+
csrfToken=""
|
|
82
|
+
editOrCreate="create"
|
|
83
|
+
library="testlib"
|
|
84
|
+
store={buildStore()}
|
|
85
|
+
/>,
|
|
86
|
+
appConfigSettings
|
|
87
|
+
);
|
|
88
|
+
|
|
89
|
+
await user.click(screen.getByRole("textbox", { name: "filter value" }));
|
|
90
|
+
await user.keyboard("horror{enter}");
|
|
91
|
+
|
|
92
|
+
let items = screen.getAllByRole("treeitem");
|
|
93
|
+
|
|
94
|
+
expect(items).toHaveLength(1);
|
|
95
|
+
expect(items[0]).toHaveTextContent(/Genre = horror/);
|
|
96
|
+
|
|
97
|
+
await user.click(screen.getByRole("textbox", { name: "filter value" }));
|
|
98
|
+
await user.keyboard("science fiction{enter}");
|
|
99
|
+
|
|
100
|
+
items = screen.getAllByRole("treeitem");
|
|
101
|
+
expect(items).toHaveLength(3);
|
|
102
|
+
|
|
103
|
+
await user.click(screen.getByRole("checkbox", { name: /clear filters/i }));
|
|
104
|
+
|
|
105
|
+
await user.click(screen.getByRole("textbox", { name: "filter value" }));
|
|
106
|
+
await user.keyboard("fantasy{enter}");
|
|
107
|
+
|
|
108
|
+
items = screen.getAllByRole("treeitem");
|
|
109
|
+
expect(items).toHaveLength(1);
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
it("sends the language=all search parameter", async () => {
|
|
113
|
+
let searchParams = null;
|
|
114
|
+
|
|
115
|
+
server.use(
|
|
116
|
+
http.get("*/search", ({ request }) => {
|
|
117
|
+
const url = new URL(request.url);
|
|
118
|
+
searchParams = url.searchParams;
|
|
119
|
+
return HttpResponse.xml("<feed />");
|
|
120
|
+
})
|
|
121
|
+
);
|
|
122
|
+
|
|
123
|
+
const user = userEvent.setup();
|
|
124
|
+
|
|
125
|
+
const appConfigSettings: Partial<ConfigurationSettings> = {
|
|
126
|
+
csrfToken: "",
|
|
127
|
+
featureFlags: {},
|
|
128
|
+
roles: [{ role: "system" }],
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
renderWithContext(
|
|
132
|
+
<CustomLists
|
|
133
|
+
csrfToken=""
|
|
134
|
+
editOrCreate="create"
|
|
135
|
+
library="testlib"
|
|
136
|
+
store={buildStore()}
|
|
137
|
+
/>,
|
|
138
|
+
appConfigSettings
|
|
139
|
+
);
|
|
140
|
+
|
|
141
|
+
await user.click(screen.getByRole("textbox", { name: "filter value" }));
|
|
142
|
+
await user.keyboard("horror{enter}");
|
|
143
|
+
|
|
144
|
+
await waitFor(() => {
|
|
145
|
+
expect(searchParams.get("language")).toEqual("all");
|
|
146
|
+
|
|
147
|
+
expect(JSON.parse(searchParams.get("q"))).toEqual({
|
|
148
|
+
query: {
|
|
149
|
+
key: "genre",
|
|
150
|
+
value: "horror",
|
|
151
|
+
},
|
|
152
|
+
});
|
|
153
|
+
});
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
it("sends the language=all search parameter when a language filter is added, and places the language filter in the q parameter", async () => {
|
|
157
|
+
let searchParams = null;
|
|
158
|
+
|
|
159
|
+
server.use(
|
|
160
|
+
http.get("*/search", ({ request }) => {
|
|
161
|
+
const url = new URL(request.url);
|
|
162
|
+
searchParams = url.searchParams;
|
|
163
|
+
return HttpResponse.xml("<feed />");
|
|
164
|
+
})
|
|
165
|
+
);
|
|
166
|
+
|
|
167
|
+
const user = userEvent.setup();
|
|
168
|
+
|
|
169
|
+
const appConfigSettings: Partial<ConfigurationSettings> = {
|
|
170
|
+
csrfToken: "",
|
|
171
|
+
featureFlags: {},
|
|
172
|
+
roles: [{ role: "system" }],
|
|
173
|
+
};
|
|
174
|
+
|
|
175
|
+
renderWithContext(
|
|
176
|
+
<CustomLists
|
|
177
|
+
csrfToken=""
|
|
178
|
+
editOrCreate="create"
|
|
179
|
+
library="testlib"
|
|
180
|
+
store={buildStore()}
|
|
181
|
+
/>,
|
|
182
|
+
appConfigSettings
|
|
183
|
+
);
|
|
184
|
+
|
|
185
|
+
await userEvent.selectOptions(
|
|
186
|
+
screen.getByRole("combobox", { name: "filter field key" }),
|
|
187
|
+
screen.getByRole("option", { name: "Language" })
|
|
188
|
+
);
|
|
189
|
+
await user.click(screen.getByRole("textbox", { name: "filter value" }));
|
|
190
|
+
await user.keyboard("french{enter}");
|
|
191
|
+
|
|
192
|
+
await waitFor(() => {
|
|
193
|
+
expect(searchParams.get("language")).toEqual("all");
|
|
194
|
+
|
|
195
|
+
expect(JSON.parse(searchParams.get("q"))).toEqual({
|
|
196
|
+
query: {
|
|
197
|
+
key: "language",
|
|
198
|
+
value: "french",
|
|
199
|
+
},
|
|
200
|
+
});
|
|
201
|
+
});
|
|
202
|
+
});
|
|
203
|
+
});
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { render, screen } from "@testing-library/react";
|
|
3
|
+
import EditableInput from "../../../src/components/EditableInput";
|
|
4
|
+
|
|
5
|
+
describe("EditableInput", () => {
|
|
6
|
+
it("renders an accessible description if a description prop is supplied and a label prop is supplied", () => {
|
|
7
|
+
const label = "input1";
|
|
8
|
+
const description = "this is a field";
|
|
9
|
+
|
|
10
|
+
render(
|
|
11
|
+
<EditableInput
|
|
12
|
+
label={label}
|
|
13
|
+
optionalText={false}
|
|
14
|
+
description={description}
|
|
15
|
+
/>
|
|
16
|
+
);
|
|
17
|
+
|
|
18
|
+
const textbox = screen.getByRole("textbox", { name: label });
|
|
19
|
+
|
|
20
|
+
expect(textbox).toHaveAccessibleDescription(description);
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
it("renders an accessible description if a description prop is supplied and a label prop is not supplied", () => {
|
|
24
|
+
const description = "this is a field";
|
|
25
|
+
|
|
26
|
+
render(<EditableInput optionalText={false} description={description} />);
|
|
27
|
+
|
|
28
|
+
const textbox = screen.getByRole("textbox");
|
|
29
|
+
|
|
30
|
+
expect(textbox).toHaveAccessibleDescription(description);
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it("renders an accessible description if optionalText is true", () => {
|
|
34
|
+
render(<EditableInput optionalText={true} />);
|
|
35
|
+
|
|
36
|
+
const textbox = screen.getByRole("textbox");
|
|
37
|
+
|
|
38
|
+
expect(textbox).toHaveAccessibleDescription(/optional/i);
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it("associates accessible descriptions with the correct inputs when multiple instances are present", () => {
|
|
42
|
+
const descriptions = ["desc 1", "desc 2", "desc 3"];
|
|
43
|
+
|
|
44
|
+
render(
|
|
45
|
+
<div>
|
|
46
|
+
<EditableInput optionalText={false} description={descriptions[0]} />
|
|
47
|
+
<EditableInput optionalText={false} />
|
|
48
|
+
<EditableInput optionalText={false} />
|
|
49
|
+
<EditableInput optionalText={false} description={descriptions[1]} />
|
|
50
|
+
<EditableInput optionalText={false} description={descriptions[2]} />
|
|
51
|
+
<EditableInput optionalText={false} />
|
|
52
|
+
</div>
|
|
53
|
+
);
|
|
54
|
+
|
|
55
|
+
const textboxes = screen.getAllByRole("textbox");
|
|
56
|
+
|
|
57
|
+
expect(textboxes[0]).toHaveAccessibleDescription(descriptions[0]);
|
|
58
|
+
expect(textboxes[1]).toHaveAccessibleDescription("");
|
|
59
|
+
expect(textboxes[2]).toHaveAccessibleDescription("");
|
|
60
|
+
expect(textboxes[3]).toHaveAccessibleDescription(descriptions[1]);
|
|
61
|
+
expect(textboxes[4]).toHaveAccessibleDescription(descriptions[2]);
|
|
62
|
+
expect(textboxes[5]).toHaveAccessibleDescription("");
|
|
63
|
+
});
|
|
64
|
+
});
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { screen } from "@testing-library/react";
|
|
3
|
+
import userEvent from "@testing-library/user-event";
|
|
4
|
+
import IndividualAdminEditForm from "../../../src/components/IndividualAdminEditForm";
|
|
5
|
+
import renderWithContext from "../testUtils/renderWithContext";
|
|
6
|
+
import { ConfigurationSettings } from "../../../src/interfaces";
|
|
7
|
+
|
|
8
|
+
describe("IndividualAdminEditForm", () => {
|
|
9
|
+
it("clears the role checkboxes after save", async () => {
|
|
10
|
+
const user = userEvent.setup();
|
|
11
|
+
|
|
12
|
+
const appConfigSettings: Partial<ConfigurationSettings> = {
|
|
13
|
+
csrfToken: "",
|
|
14
|
+
featureFlags: {},
|
|
15
|
+
roles: [
|
|
16
|
+
{
|
|
17
|
+
role: "system",
|
|
18
|
+
},
|
|
19
|
+
],
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
const props = {
|
|
23
|
+
data: {
|
|
24
|
+
allLibraries: [
|
|
25
|
+
{
|
|
26
|
+
name: "Alpha",
|
|
27
|
+
short_name: "alpha",
|
|
28
|
+
uuid: "a3247ce9-9639-426b-bb09-82e9cb4cf44b",
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
name: "Beta",
|
|
32
|
+
short_name: "beta",
|
|
33
|
+
uuid: "da80cd40-7a87-41db-a789-5f1e87732aeb",
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
name: "Gamma",
|
|
37
|
+
short_name: "gamma",
|
|
38
|
+
uuid: "15f32675-73f5-46f3-91f4-837b933bc7b1",
|
|
39
|
+
},
|
|
40
|
+
],
|
|
41
|
+
},
|
|
42
|
+
disabled: false,
|
|
43
|
+
listDataKey: "",
|
|
44
|
+
urlBase: "",
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
const { rerender } = renderWithContext(
|
|
48
|
+
<IndividualAdminEditForm {...props} />,
|
|
49
|
+
appConfigSettings
|
|
50
|
+
);
|
|
51
|
+
|
|
52
|
+
const systemAdminCheckbox = screen.getByRole("checkbox", {
|
|
53
|
+
name: /^system admin$/i,
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
const allAdminCheckbox = screen.getByRole("checkbox", {
|
|
57
|
+
name: /^administrator$/i,
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
const allUserCheckbox = screen.getByRole("checkbox", {
|
|
61
|
+
name: /^user$/i,
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
const alphaAdminCheckbox = screen.getByRole("checkbox", {
|
|
65
|
+
name: /administrator of alpha/i,
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
const alphaUserCheckbox = screen.getByRole("checkbox", {
|
|
69
|
+
name: /user of alpha/i,
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
const betaAdminCheckbox = screen.getByRole("checkbox", {
|
|
73
|
+
name: /administrator of beta/i,
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
const betaUserCheckbox = screen.getByRole("checkbox", {
|
|
77
|
+
name: /user of beta/i,
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
const gammaAdminCheckbox = screen.getByRole("checkbox", {
|
|
81
|
+
name: /administrator of gamma/i,
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
const gammaUserCheckbox = screen.getByRole("checkbox", {
|
|
85
|
+
name: /user of gamma/i,
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
expect(systemAdminCheckbox).not.toBeChecked();
|
|
89
|
+
|
|
90
|
+
await user.click(systemAdminCheckbox);
|
|
91
|
+
|
|
92
|
+
expect(systemAdminCheckbox).toBeChecked();
|
|
93
|
+
|
|
94
|
+
expect(allAdminCheckbox).toBeChecked();
|
|
95
|
+
expect(allUserCheckbox).toBeChecked();
|
|
96
|
+
|
|
97
|
+
expect(alphaAdminCheckbox).toBeChecked();
|
|
98
|
+
expect(alphaUserCheckbox).toBeChecked();
|
|
99
|
+
|
|
100
|
+
expect(betaAdminCheckbox).toBeChecked();
|
|
101
|
+
expect(betaUserCheckbox).toBeChecked();
|
|
102
|
+
|
|
103
|
+
expect(gammaAdminCheckbox).toBeChecked();
|
|
104
|
+
expect(gammaUserCheckbox).toBeChecked();
|
|
105
|
+
|
|
106
|
+
const nextProps = {
|
|
107
|
+
...props,
|
|
108
|
+
// Existence of the responseBody prop indicates that the form was just saved.
|
|
109
|
+
responseBody: "some response",
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
rerender(<IndividualAdminEditForm {...nextProps} />);
|
|
113
|
+
|
|
114
|
+
expect(systemAdminCheckbox).not.toBeChecked();
|
|
115
|
+
|
|
116
|
+
expect(allAdminCheckbox).not.toBeChecked();
|
|
117
|
+
expect(allUserCheckbox).not.toBeChecked();
|
|
118
|
+
|
|
119
|
+
expect(alphaAdminCheckbox).not.toBeChecked();
|
|
120
|
+
expect(alphaUserCheckbox).not.toBeChecked();
|
|
121
|
+
|
|
122
|
+
expect(betaAdminCheckbox).not.toBeChecked();
|
|
123
|
+
expect(betaUserCheckbox).not.toBeChecked();
|
|
124
|
+
|
|
125
|
+
expect(gammaAdminCheckbox).not.toBeChecked();
|
|
126
|
+
expect(gammaUserCheckbox).not.toBeChecked();
|
|
127
|
+
});
|
|
128
|
+
});
|