@thepalaceproject/circulation-admin 1.38.0 → 1.39.0-post.2
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/README.md +42 -0
- package/dist/circulation-admin.css +1 -1
- package/dist/circulation-admin.js +1 -1
- package/jest.config.js +1 -0
- package/package.json +5 -3
- package/scripts/syncPatronBlockingDocs.js +43 -0
- package/tests/jest/api/patronBlockingRules.test.ts +28 -6
- package/tests/jest/components/CollectionImportButton.test.tsx +145 -7
- package/tests/jest/components/Collections.test.tsx +220 -0
- package/tests/jest/components/DiscoveryServices.test.tsx +545 -0
- package/tests/jest/components/EditableConfigList.test.tsx +399 -0
- package/tests/jest/components/IndividualAdmins.test.tsx +390 -0
- package/tests/jest/components/PatronAuthServiceEditForm.test.tsx +39 -16
- package/tests/jest/components/PatronBlockingRulesEditor.test.tsx +234 -46
- package/tests/jest/components/PatronBlockingRulesHelpModal.test.tsx +148 -0
- package/webpack.common.js +4 -0
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { render, screen, waitFor } from "@testing-library/react";
|
|
3
|
+
import userEvent from "@testing-library/user-event";
|
|
4
|
+
import { PatronBlockingRulesHelpModal } from "../../../src/components/PatronBlockingRulesHelpModal";
|
|
5
|
+
|
|
6
|
+
const SAMPLE_FIELDS = {
|
|
7
|
+
fines: "2.50",
|
|
8
|
+
patron_identifier: "12345",
|
|
9
|
+
patron_name: "John Doe",
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
const baseProps = {
|
|
13
|
+
show: true,
|
|
14
|
+
onHide: jest.fn(),
|
|
15
|
+
availableFields: null,
|
|
16
|
+
fieldsLoading: false,
|
|
17
|
+
fieldsError: null,
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
describe("PatronBlockingRulesHelpModal", () => {
|
|
21
|
+
beforeEach(() => {
|
|
22
|
+
baseProps.onHide = jest.fn();
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
it("renders the modal title when show is true", () => {
|
|
26
|
+
render(<PatronBlockingRulesHelpModal {...baseProps} />);
|
|
27
|
+
expect(screen.getByText(/Patron Blocking Rules — Help/i)).toBeTruthy();
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
it("does not render when show is false", () => {
|
|
31
|
+
render(<PatronBlockingRulesHelpModal {...baseProps} show={false} />);
|
|
32
|
+
expect(
|
|
33
|
+
screen.queryByText(/Patron Blocking Rules — Help/i)
|
|
34
|
+
).toBeNull();
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it("shows a loading indicator when fieldsLoading is true", () => {
|
|
38
|
+
render(
|
|
39
|
+
<PatronBlockingRulesHelpModal
|
|
40
|
+
{...baseProps}
|
|
41
|
+
fieldsLoading={true}
|
|
42
|
+
availableFields={null}
|
|
43
|
+
fieldsError={null}
|
|
44
|
+
/>
|
|
45
|
+
);
|
|
46
|
+
expect(screen.getByText(/Loading available fields/i)).toBeTruthy();
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it("does not show the fields table while loading", () => {
|
|
50
|
+
render(
|
|
51
|
+
<PatronBlockingRulesHelpModal
|
|
52
|
+
{...baseProps}
|
|
53
|
+
fieldsLoading={true}
|
|
54
|
+
availableFields={SAMPLE_FIELDS}
|
|
55
|
+
fieldsError={null}
|
|
56
|
+
/>
|
|
57
|
+
);
|
|
58
|
+
// Patron field names should not be visible while loading.
|
|
59
|
+
expect(screen.queryByText("fines")).toBeNull();
|
|
60
|
+
expect(screen.queryByText("patron_identifier")).toBeNull();
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
it("shows available fields in a table when loaded successfully", () => {
|
|
64
|
+
render(
|
|
65
|
+
<PatronBlockingRulesHelpModal
|
|
66
|
+
{...baseProps}
|
|
67
|
+
availableFields={SAMPLE_FIELDS}
|
|
68
|
+
/>
|
|
69
|
+
);
|
|
70
|
+
// Verify the fields table content is present.
|
|
71
|
+
expect(screen.getByText("fines")).toBeTruthy();
|
|
72
|
+
expect(screen.getByText("2.50")).toBeTruthy();
|
|
73
|
+
expect(screen.getByText("patron_identifier")).toBeTruthy();
|
|
74
|
+
expect(screen.getByText("12345")).toBeTruthy();
|
|
75
|
+
expect(screen.getByText("patron_name")).toBeTruthy();
|
|
76
|
+
expect(screen.getByText("John Doe")).toBeTruthy();
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
it("shows a warning message when fieldsError is set", () => {
|
|
80
|
+
render(
|
|
81
|
+
<PatronBlockingRulesHelpModal
|
|
82
|
+
{...baseProps}
|
|
83
|
+
fieldsError="Save the service before template variables can be fetched."
|
|
84
|
+
/>
|
|
85
|
+
);
|
|
86
|
+
expect(
|
|
87
|
+
screen.getByText(/Save the service before template variables can be fetched/i)
|
|
88
|
+
).toBeTruthy();
|
|
89
|
+
expect(screen.queryByText("fines")).toBeNull();
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
it("shows a muted fallback when there are no fields and no error", () => {
|
|
93
|
+
render(
|
|
94
|
+
<PatronBlockingRulesHelpModal
|
|
95
|
+
{...baseProps}
|
|
96
|
+
availableFields={null}
|
|
97
|
+
fieldsError={null}
|
|
98
|
+
/>
|
|
99
|
+
);
|
|
100
|
+
expect(
|
|
101
|
+
screen.getByText(/No field data available/i)
|
|
102
|
+
).toBeTruthy();
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
it("renders null values as italic 'null'", () => {
|
|
106
|
+
render(
|
|
107
|
+
<PatronBlockingRulesHelpModal
|
|
108
|
+
{...baseProps}
|
|
109
|
+
availableFields={{ some_field: null }}
|
|
110
|
+
/>
|
|
111
|
+
);
|
|
112
|
+
expect(screen.getByText("null")).toBeTruthy();
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
it("renders the Available Functions section with content", () => {
|
|
116
|
+
render(<PatronBlockingRulesHelpModal {...baseProps} />);
|
|
117
|
+
expect(screen.getByText(/Available Functions/i)).toBeTruthy();
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
it("renders the Syntax Reference section", () => {
|
|
121
|
+
render(<PatronBlockingRulesHelpModal {...baseProps} />);
|
|
122
|
+
expect(screen.getByText(/Syntax Reference/i)).toBeTruthy();
|
|
123
|
+
// simpleeval appears in both the functions doc and the syntax section.
|
|
124
|
+
expect(screen.getAllByText(/simpleeval/i).length).toBeGreaterThan(0);
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
it("calls onHide when the header × button is clicked", async () => {
|
|
128
|
+
const user = userEvent.setup();
|
|
129
|
+
const onHide = jest.fn();
|
|
130
|
+
render(<PatronBlockingRulesHelpModal {...baseProps} onHide={onHide} />);
|
|
131
|
+
|
|
132
|
+
// The header × button has class "close"; the footer button has content "Close".
|
|
133
|
+
const closeButtons = screen.getAllByRole("button", { name: /close/i });
|
|
134
|
+
await user.click(closeButtons[0]);
|
|
135
|
+
expect(onHide).toHaveBeenCalledTimes(1);
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
it("calls onHide when the footer Close button is clicked", async () => {
|
|
139
|
+
const user = userEvent.setup();
|
|
140
|
+
const onHide = jest.fn();
|
|
141
|
+
render(<PatronBlockingRulesHelpModal {...baseProps} onHide={onHide} />);
|
|
142
|
+
|
|
143
|
+
// Footer Close is the last close-named button; header × is first.
|
|
144
|
+
const closeButtons = screen.getAllByRole("button", { name: /close/i });
|
|
145
|
+
await user.click(closeButtons[closeButtons.length - 1]);
|
|
146
|
+
expect(onHide).toHaveBeenCalledTimes(1);
|
|
147
|
+
});
|
|
148
|
+
});
|