@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.
Files changed (133) hide show
  1. package/CLAUDE.md +28 -7
  2. package/README.md +11 -5
  3. package/dist/circulation-admin.js +1 -1
  4. package/eslint.config.mjs +9 -1
  5. package/jest.config.js +22 -8
  6. package/package.json +6 -20
  7. package/tests/jest/actions.test.ts +962 -0
  8. package/tests/jest/components/AccountPage.test.tsx +41 -0
  9. package/tests/jest/components/AdvancedSearchBooleanFilter.test.tsx +231 -0
  10. package/tests/jest/components/AdvancedSearchBuilder.test.tsx +93 -20
  11. package/tests/jest/components/AdvancedSearchFilter.test.tsx +142 -0
  12. package/tests/jest/components/AdvancedSearchFilterInput.test.tsx +212 -0
  13. package/tests/jest/components/AdvancedSearchFilterViewer.test.tsx +52 -0
  14. package/tests/jest/components/AdvancedSearchValueFilter.test.tsx +123 -0
  15. package/tests/jest/components/Announcement.test.tsx +76 -0
  16. package/tests/jest/components/AnnouncementForm.test.tsx +229 -0
  17. package/tests/jest/components/AnnouncementsSection.test.tsx +146 -0
  18. package/tests/jest/components/Autocomplete.test.tsx +67 -0
  19. package/tests/jest/components/BookCoverEditor.test.tsx +393 -0
  20. package/tests/jest/components/BookDetails.test.tsx +133 -0
  21. package/tests/jest/components/BookDetailsContainer.test.tsx +116 -0
  22. package/tests/jest/components/BookDetailsEditor.test.tsx +275 -0
  23. package/tests/jest/components/BookDetailsTabContainer.test.tsx +237 -0
  24. package/tests/jest/components/BookEditForm.test.tsx +352 -0
  25. package/tests/jest/components/BookEditor.test.tsx +20 -21
  26. package/tests/jest/components/CatalogPage.test.tsx +158 -0
  27. package/tests/jest/components/CatalogServices.test.tsx +100 -0
  28. package/tests/jest/components/ChangePasswordForm.test.tsx +163 -0
  29. package/tests/jest/components/CirculationEventsDownloadForm.test.tsx +41 -0
  30. package/tests/jest/components/Classifications.test.tsx +166 -0
  31. package/tests/jest/components/ClassificationsForm.test.tsx +458 -0
  32. package/tests/jest/components/ClassificationsTable.test.tsx +57 -0
  33. package/tests/jest/components/Collections.test.tsx +335 -7
  34. package/tests/jest/components/ColorPicker.test.tsx +58 -0
  35. package/tests/jest/components/ComplaintForm.test.tsx +171 -0
  36. package/tests/jest/components/Complaints.test.tsx +259 -0
  37. package/tests/jest/components/ConfigPage.test.tsx +58 -0
  38. package/tests/jest/components/ConfigTabContainer.test.tsx +196 -0
  39. package/tests/jest/components/ContextProvider.test.tsx +138 -0
  40. package/tests/jest/components/Contributors.test.tsx +157 -0
  41. package/tests/jest/components/CustomListEditor.test.tsx +367 -0
  42. package/tests/jest/components/CustomListEntriesEditor.test.tsx +1240 -0
  43. package/tests/jest/components/CustomListPage.test.tsx +57 -0
  44. package/tests/jest/components/CustomListSearch.test.tsx +275 -0
  45. package/tests/jest/components/CustomLists.test.tsx +621 -4
  46. package/tests/jest/components/CustomListsForBook.test.tsx +353 -0
  47. package/tests/jest/components/CustomListsSidebar.test.tsx +251 -0
  48. package/tests/jest/components/DashboardPage.test.tsx +76 -0
  49. package/tests/jest/components/DebugAuthentication.test.tsx +1 -5
  50. package/tests/jest/components/DiagnosticsServiceTabs.test.tsx +141 -0
  51. package/tests/jest/components/DiagnosticsServiceType.test.tsx +75 -0
  52. package/tests/jest/components/DiagnosticsTabContainer.test.tsx +147 -0
  53. package/tests/jest/components/DiscoveryServices.test.tsx +154 -7
  54. package/tests/jest/components/EditableConfigList.test.tsx +606 -9
  55. package/tests/jest/components/EditableInput.test.tsx +250 -1
  56. package/tests/jest/components/EditorField.test.tsx +95 -0
  57. package/tests/jest/components/EntryPointsContainer.test.tsx +97 -0
  58. package/tests/jest/components/EntryPointsTabs.test.tsx +88 -0
  59. package/tests/jest/components/ErrorMessage.test.tsx +97 -0
  60. package/tests/jest/components/Footer.test.tsx +141 -0
  61. package/tests/jest/components/GenreForm.test.tsx +152 -0
  62. package/tests/jest/components/Header.test.tsx +421 -0
  63. package/tests/jest/components/IndividualAdminEditForm.test.tsx +506 -1
  64. package/tests/jest/components/IndividualAdmins.test.tsx +124 -8
  65. package/tests/jest/components/InputList.test.tsx +493 -0
  66. package/tests/jest/components/InventoryReportRequestModal.test.tsx +16 -42
  67. package/tests/jest/components/JsonField.test.tsx +20 -0
  68. package/tests/jest/components/Lane.test.tsx +132 -10
  69. package/tests/jest/components/LaneCustomListsEditor.test.tsx +450 -0
  70. package/tests/jest/components/LaneEditor.test.tsx +377 -13
  71. package/tests/jest/components/LanePage.test.tsx +68 -0
  72. package/tests/jest/components/Lanes.test.tsx +446 -0
  73. package/tests/jest/components/LanesSidebar.test.tsx +621 -0
  74. package/tests/jest/components/LanguageField.test.tsx +40 -0
  75. package/tests/jest/components/Libraries.test.tsx +48 -0
  76. package/tests/jest/components/LibraryEditForm.test.tsx +383 -0
  77. package/tests/jest/components/LibraryRegistration.test.tsx +354 -0
  78. package/tests/jest/components/LibraryRegistrationForm.test.tsx +203 -0
  79. package/tests/jest/components/LoadButton.test.tsx +49 -0
  80. package/tests/jest/components/ManagePatrons.test.tsx +73 -0
  81. package/tests/jest/components/ManagePatronsForm.test.tsx +180 -0
  82. package/tests/jest/components/ManagePatronsTabContainer.test.tsx +143 -0
  83. package/tests/jest/components/MetadataServices.test.tsx +95 -0
  84. package/tests/jest/components/NeighborhoodAnalyticsForm.test.tsx +167 -0
  85. package/tests/jest/components/PairedMenus.test.tsx +220 -0
  86. package/tests/jest/components/PatronAuthServices.test.tsx +114 -0
  87. package/tests/jest/components/PatronBlockingRulesEditor.test.tsx +3 -1
  88. package/tests/jest/components/PatronInfo.test.tsx +54 -0
  89. package/tests/jest/components/ProtocolFormField.test.tsx +479 -7
  90. package/tests/jest/components/ResetAdobeId.test.tsx +302 -0
  91. package/tests/jest/components/SaveButton.test.tsx +47 -0
  92. package/tests/jest/components/SelfTestResult.test.tsx +164 -0
  93. package/tests/jest/components/SelfTests.test.tsx +601 -0
  94. package/tests/jest/components/SelfTestsCategory.test.tsx +200 -0
  95. package/tests/jest/components/SelfTestsTabContainer.test.tsx +216 -0
  96. package/tests/jest/components/ServiceEditForm.test.tsx +1092 -0
  97. package/tests/jest/components/ServiceWithRegistrationsEditForm.test.tsx +110 -0
  98. package/tests/jest/components/SetupPage.test.tsx +41 -0
  99. package/tests/jest/components/SingleStatListItem.test.tsx +62 -0
  100. package/tests/jest/components/SitewideAnnouncements.test.tsx +134 -0
  101. package/tests/jest/components/Stats.test.tsx +4 -15
  102. package/tests/jest/components/TabContainer.test.tsx +68 -0
  103. package/tests/jest/components/TextWithEditMode.test.tsx +218 -0
  104. package/tests/jest/components/Timestamp.test.tsx +74 -0
  105. package/tests/jest/components/ToolTip.test.tsx +46 -0
  106. package/tests/jest/components/TroubleshootingCategoryPage.test.tsx +69 -0
  107. package/tests/jest/components/TroubleshootingPage.test.tsx +94 -0
  108. package/tests/jest/components/TroubleshootingTabContainer.test.tsx +68 -0
  109. package/tests/jest/components/UpdatingLoader.test.tsx +36 -0
  110. package/tests/jest/components/WelcomePage.test.tsx +43 -0
  111. package/tests/jest/components/WithEditButton.test.tsx +55 -0
  112. package/tests/jest/components/WithRemoveButton.test.tsx +95 -0
  113. package/tests/jest/editorAdapter.test.ts +245 -0
  114. package/tests/jest/features/book.test.ts +75 -80
  115. package/tests/jest/fixtures/classificationsData.ts +45 -0
  116. package/tests/jest/fixtures/genreData.ts +811 -0
  117. package/tests/jest/index.test.tsx +50 -0
  118. package/tests/jest/models/Admin.test.ts +206 -0
  119. package/tests/jest/reducers/bookCoverPreview.test.ts +81 -0
  120. package/tests/jest/reducers/classifications.test.ts +161 -0
  121. package/tests/jest/reducers/collections.test.ts +96 -0
  122. package/tests/jest/reducers/complaints.test.ts +166 -0
  123. package/tests/jest/reducers/createFetchEditReducer.test.ts +220 -0
  124. package/tests/jest/reducers/createRegisterLibraryReducer.test.ts +51 -0
  125. package/tests/jest/reducers/customListDetails.test.ts +120 -0
  126. package/tests/jest/reducers/customListEditor.test.ts +2823 -0
  127. package/tests/jest/reducers/managePatrons.test.ts +71 -0
  128. package/tests/jest/testUtils/formDataShim.test.ts +135 -0
  129. package/tests/jest/testUtils/formDataShim.ts +112 -0
  130. package/tests/jest/utils/sharedFunctions.test.ts +121 -0
  131. package/tsconfig.json +3 -4
  132. package/testReporter.js +0 -29
  133. package/tests/jest/README.md +0 -6
@@ -0,0 +1,302 @@
1
+ import * as React from "react";
2
+ import { screen, waitFor } from "@testing-library/react";
3
+ import userEvent from "@testing-library/user-event";
4
+
5
+ import buildStore from "../../../src/store";
6
+ import { renderWithProviders } from "../testUtils/withProviders";
7
+ import ConnectedResetAdobeId, {
8
+ ResetAdobeId,
9
+ } from "../../../src/components/ResetAdobeId";
10
+
11
+ const patrons = [
12
+ {
13
+ authorization_expires: "",
14
+ username: "User Name",
15
+ personal_name: "Personal Name",
16
+ email_address: "user@email.com",
17
+ authorization_identifier: "1234",
18
+ authorization_identifiers: [""],
19
+ block_reason: "",
20
+ external_type: "",
21
+ fines: "",
22
+ permanent_id: "",
23
+ },
24
+ {
25
+ authorization_expires: "",
26
+ personal_name: "Personal Name2",
27
+ authorization_identifier: "5678",
28
+ authorization_identifiers: [""],
29
+ block_reason: "",
30
+ external_type: "",
31
+ fines: "",
32
+ permanent_id: "",
33
+ },
34
+ ];
35
+
36
+ describe("ResetAdobeId", () => {
37
+ let store: ReturnType<typeof buildStore>;
38
+
39
+ beforeEach(() => {
40
+ store = buildStore();
41
+ });
42
+
43
+ afterEach(() => {
44
+ jest.restoreAllMocks();
45
+ });
46
+
47
+ // Render the unconnected component with explicit props (patron / resetAdobeId /
48
+ // responseBody / fetchError are what `connect` would otherwise source from the
49
+ // store, so tests inject them directly). Wrapped in providers because the
50
+ // ManagePatronsForm it renders is connected.
51
+ const resetElement = (props: Record<string, unknown> = {}) => (
52
+ <ResetAdobeId store={store} csrfToken="token" library="nypl" {...props} />
53
+ );
54
+ const renderReset = (props: Record<string, unknown> = {}) =>
55
+ renderWithProviders(resetElement(props));
56
+
57
+ describe("rendering without patron", () => {
58
+ it("has the patron-actions class", () => {
59
+ const { container } = renderReset({ patron: null });
60
+ expect(container.firstChild).toHaveClass("patron-actions");
61
+ });
62
+
63
+ it("has a header", () => {
64
+ renderReset({ patron: null });
65
+ expect(
66
+ screen.getByRole("heading", { name: "Reset Adobe ID" })
67
+ ).toBeInTheDocument();
68
+ });
69
+
70
+ it("does not render a patron-info section", () => {
71
+ const { container } = renderReset({ patron: null });
72
+ expect(container.querySelector(".patron-info")).not.toBeInTheDocument();
73
+ });
74
+
75
+ it("prompts the user to search for a patron", () => {
76
+ renderReset({ patron: null });
77
+ expect(
78
+ screen.getByText("Search for a patron to begin.")
79
+ ).toBeInTheDocument();
80
+ });
81
+ });
82
+
83
+ describe("rendering with patron", () => {
84
+ it("shows a warning before the submit button", () => {
85
+ const { container } = renderReset({ patron: patrons[0] });
86
+ expect(container.querySelector(".patron-warning")).toHaveTextContent(
87
+ "IMPORTANT: Patron User Name will lose access to any existing loans. Loans will still appear in the patron's book list until they expire, but the patron will be unable to read or return them."
88
+ );
89
+ });
90
+
91
+ it("gives the submit button a danger class", () => {
92
+ renderReset({ patron: patrons[0] });
93
+ expect(
94
+ screen.getByRole("button", { name: "Reset Adobe ID" })
95
+ ).toHaveClass("danger");
96
+ });
97
+ });
98
+
99
+ describe("behavior", () => {
100
+ it("has a disabled submit button initially", () => {
101
+ renderReset({ patron: patrons[0], resetAdobeId: jest.fn() });
102
+ expect(
103
+ screen.getByRole("button", { name: "Reset Adobe ID" })
104
+ ).toBeDisabled();
105
+ });
106
+
107
+ it("enables the submit button when the checkbox is checked", async () => {
108
+ const user = userEvent.setup();
109
+ renderReset({ patron: patrons[0], resetAdobeId: jest.fn() });
110
+ const button = screen.getByRole("button", { name: "Reset Adobe ID" });
111
+ expect(button).toBeDisabled();
112
+
113
+ await user.click(screen.getByRole("checkbox"));
114
+
115
+ expect(button).not.toBeDisabled();
116
+ });
117
+
118
+ it("disables the submit button when the checkbox is unchecked again", async () => {
119
+ const user = userEvent.setup();
120
+ renderReset({ patron: patrons[0], resetAdobeId: jest.fn() });
121
+ const button = screen.getByRole("button", { name: "Reset Adobe ID" });
122
+ const checkbox = screen.getByRole("checkbox");
123
+
124
+ await user.click(checkbox);
125
+ expect(button).not.toBeDisabled();
126
+
127
+ await user.click(checkbox);
128
+ expect(button).toBeDisabled();
129
+ });
130
+
131
+ it("calls the Adobe ID action when the button is clicked", async () => {
132
+ const user = userEvent.setup();
133
+ const resetAdobeId = jest.fn().mockResolvedValue(undefined);
134
+ renderReset({ patron: patrons[0], resetAdobeId });
135
+ expect(resetAdobeId).not.toHaveBeenCalled();
136
+
137
+ await user.click(screen.getByRole("checkbox"));
138
+ const button = screen.getByRole("button", { name: "Reset Adobe ID" });
139
+ expect(button).not.toBeDisabled();
140
+ await user.click(button);
141
+
142
+ await waitFor(() => expect(resetAdobeId).toHaveBeenCalledTimes(1));
143
+ const [data, library] = resetAdobeId.mock.calls[0];
144
+ expect(data.get("identifier")).toBe("1234");
145
+ expect(library).toBe("nypl");
146
+
147
+ // After the reset resolves the component clears the checkbox, re-disabling
148
+ // the button; awaiting that settles the pending promise.
149
+ await waitFor(() => expect(button).toBeDisabled());
150
+ });
151
+
152
+ it("shows a success alert message if the reset is successful", () => {
153
+ const { container, rerender } = renderReset({
154
+ patron: patrons[0],
155
+ resetAdobeId: jest.fn().mockResolvedValue(undefined),
156
+ });
157
+ expect(container.querySelector(".alert-success")).not.toBeInTheDocument();
158
+
159
+ // The response now comes from the server (via the responseBody prop).
160
+ rerender(
161
+ resetElement({
162
+ patron: patrons[0],
163
+ responseBody: "Adobe ID for patron has been reset.",
164
+ })
165
+ );
166
+
167
+ const alert = container.querySelector(".alert-success");
168
+ expect(alert).toBeInTheDocument();
169
+ expect(alert).toHaveTextContent("Adobe ID for patron has been reset.");
170
+ expect(alert).toHaveTextContent(
171
+ "Please instruct the patron to sign back into their account."
172
+ );
173
+ });
174
+
175
+ it("hides the checkbox and the reset button if the reset is successful", () => {
176
+ const { container, rerender } = renderReset({
177
+ patron: patrons[0],
178
+ resetAdobeId: jest.fn().mockResolvedValue(undefined),
179
+ });
180
+ expect(
181
+ container.querySelector(".reset-adobe-id input")
182
+ ).toBeInTheDocument();
183
+ expect(
184
+ container.querySelector(".reset-adobe-id button")
185
+ ).toBeInTheDocument();
186
+
187
+ rerender(
188
+ resetElement({
189
+ patron: patrons[0],
190
+ responseBody: "Adobe ID for patron has been reset.",
191
+ })
192
+ );
193
+
194
+ expect(
195
+ container.querySelector(".reset-adobe-id input")
196
+ ).not.toBeInTheDocument();
197
+ expect(
198
+ container.querySelector(".reset-adobe-id button")
199
+ ).not.toBeInTheDocument();
200
+ });
201
+
202
+ it("shows a failure alert message if the reset fails", () => {
203
+ const fetchError = { status: 400, response: "", url: "" };
204
+ // The legacy test modeled the failing action with a rejecting stub; the
205
+ // failure alert itself is driven by the fetchError prop the reducer sets.
206
+ const { container, rerender } = renderReset({
207
+ patron: patrons[0],
208
+ resetAdobeId: jest.fn().mockRejectedValue(fetchError),
209
+ });
210
+ expect(container.querySelector(".alert-danger")).not.toBeInTheDocument();
211
+
212
+ rerender(
213
+ resetElement({
214
+ patron: patrons[0],
215
+ resetAdobeId: jest.fn().mockRejectedValue(fetchError),
216
+ fetchError,
217
+ })
218
+ );
219
+
220
+ const alert = container.querySelector(".alert-danger");
221
+ expect(alert).toBeInTheDocument();
222
+ expect(alert).toHaveTextContent(
223
+ "Error: failed to reset Adobe ID for patron 1234"
224
+ );
225
+ });
226
+
227
+ it("updates the warning message if a new patron is searched and found", () => {
228
+ const { container, rerender } = renderReset({
229
+ patron: patrons[0],
230
+ resetAdobeId: jest.fn(),
231
+ });
232
+ expect(container.querySelector(".patron-warning")).toHaveTextContent(
233
+ "IMPORTANT: Patron User Name will lose access"
234
+ );
235
+
236
+ rerender(resetElement({ patron: patrons[1], resetAdobeId: jest.fn() }));
237
+
238
+ expect(container.querySelector(".patron-warning")).toHaveTextContent(
239
+ "IMPORTANT: Patron Personal Name2 will lose access"
240
+ );
241
+ });
242
+
243
+ it("restores the checkbox and the reset button if a new patron is searched and found", () => {
244
+ const { container, rerender } = renderReset({
245
+ patron: patrons[0],
246
+ responseBody: "Adobe ID for patron has been reset.",
247
+ resetAdobeId: jest.fn(),
248
+ });
249
+ expect(
250
+ container.querySelector(".reset-adobe-id input")
251
+ ).not.toBeInTheDocument();
252
+ expect(
253
+ container.querySelector(".reset-adobe-id button")
254
+ ).not.toBeInTheDocument();
255
+
256
+ rerender(
257
+ resetElement({
258
+ patron: patrons[0],
259
+ responseBody: "",
260
+ resetAdobeId: jest.fn(),
261
+ })
262
+ );
263
+
264
+ expect(
265
+ container.querySelector(".reset-adobe-id input")
266
+ ).toBeInTheDocument();
267
+ expect(
268
+ container.querySelector(".reset-adobe-id button")
269
+ ).toBeInTheDocument();
270
+ });
271
+ });
272
+
273
+ describe("connected (default export)", () => {
274
+ it("wires state and dispatch through connect", async () => {
275
+ // Defensive: nothing fetches on this mount, but stub fetch so any stray
276
+ // request settles rather than crashing the strict worker.
277
+ jest.spyOn(globalThis, "fetch").mockResolvedValue(
278
+ new Response(JSON.stringify({}), {
279
+ status: 200,
280
+ headers: { "Content-Type": "application/json" },
281
+ })
282
+ );
283
+
284
+ renderWithProviders(
285
+ <ConnectedResetAdobeId
286
+ store={buildStore()}
287
+ csrfToken="token"
288
+ library="nypl"
289
+ />
290
+ );
291
+
292
+ // With no patron in the store, mapStateToProps yields patron=undefined and
293
+ // the connected component prompts the user to search.
294
+ expect(
295
+ await screen.findByRole("heading", { name: "Reset Adobe ID" })
296
+ ).toBeInTheDocument();
297
+ expect(
298
+ screen.getByText("Search for a patron to begin.")
299
+ ).toBeInTheDocument();
300
+ });
301
+ });
302
+ });
@@ -0,0 +1,47 @@
1
+ import * as React from "react";
2
+ import { render, screen } from "@testing-library/react";
3
+ import userEvent from "@testing-library/user-event";
4
+
5
+ import SaveButton from "../../../src/components/SaveButton";
6
+
7
+ describe("SaveButton", () => {
8
+ it("says 'Save' when given no text", () => {
9
+ render(<SaveButton disabled={false} submit={jest.fn()} />);
10
+
11
+ expect(screen.getByRole("button", { name: "Save" })).toBeInTheDocument();
12
+ });
13
+
14
+ it("displays the text it is given", () => {
15
+ render(
16
+ <SaveButton
17
+ disabled={false}
18
+ submit={jest.fn()}
19
+ text="some other string"
20
+ />
21
+ );
22
+
23
+ expect(
24
+ screen.getByRole("button", { name: "some other string" })
25
+ ).toBeInTheDocument();
26
+ });
27
+
28
+ it("submits when clicked", async () => {
29
+ const user = userEvent.setup();
30
+ const submit = jest.fn();
31
+ render(<SaveButton disabled={false} submit={submit} />);
32
+
33
+ await user.click(screen.getByRole("button"));
34
+
35
+ expect(submit).toHaveBeenCalledTimes(1);
36
+ });
37
+
38
+ it("does nothing when disabled", async () => {
39
+ const user = userEvent.setup();
40
+ const submit = jest.fn();
41
+ render(<SaveButton disabled={true} submit={submit} />);
42
+
43
+ await user.click(screen.getByRole("button"));
44
+
45
+ expect(submit).not.toHaveBeenCalled();
46
+ });
47
+ });
@@ -0,0 +1,164 @@
1
+ import * as React from "react";
2
+ import { render, screen } from "@testing-library/react";
3
+
4
+ import SelfTestResult from "../../../src/components/SelfTestResult";
5
+ import { SelfTestsResult } from "../../../src/interfaces";
6
+
7
+ const stringResult: SelfTestsResult = {
8
+ duration: 0,
9
+ end: "",
10
+ name: "Test Result",
11
+ result: "abc",
12
+ start: "",
13
+ success: true,
14
+ };
15
+
16
+ describe("SelfTestResult", () => {
17
+ it("displays a result with a string", () => {
18
+ const { container } = render(
19
+ <SelfTestResult result={stringResult} isFetching={false} />
20
+ );
21
+
22
+ expect(container.querySelector("li")).toHaveClass("success");
23
+
24
+ expect(screen.getByRole("heading", { level: 4 })).toHaveTextContent(
25
+ "Test Result"
26
+ );
27
+
28
+ const testResult = container.querySelector("pre");
29
+ expect(testResult).toHaveClass("result-description");
30
+ expect(testResult).toHaveTextContent("abc");
31
+
32
+ expect(screen.getByText("success: true")).toHaveClass(
33
+ "success-description"
34
+ );
35
+
36
+ // A single-line string result is not shown in a collapsible panel.
37
+ expect(container.querySelector(".panel")).not.toBeInTheDocument();
38
+ });
39
+
40
+ it("displays a result with a multi-line string", () => {
41
+ const longStringResult: SelfTestsResult = {
42
+ ...stringResult,
43
+ result: "abc \n def",
44
+ };
45
+ const { container } = render(
46
+ <SelfTestResult result={longStringResult} isFetching={false} />
47
+ );
48
+
49
+ const panel = container.querySelector(".panel");
50
+ expect(panel).toBeInTheDocument();
51
+ expect(panel).toHaveClass("panel-success");
52
+ expect(screen.getByText("Results")).toBeInTheDocument();
53
+
54
+ expect(container.querySelector("pre")?.textContent).toBe("abc \n def");
55
+ });
56
+
57
+ it("displays a result with an array", () => {
58
+ const arrayResult: SelfTestsResult = {
59
+ ...stringResult,
60
+ result: ["Thing 1", "Thing 2"],
61
+ };
62
+ const { container } = render(
63
+ <SelfTestResult result={arrayResult} isFetching={false} />
64
+ );
65
+
66
+ const panel = container.querySelector(".panel");
67
+ expect(panel).toBeInTheDocument();
68
+ expect(panel).toHaveClass("panel-success");
69
+ expect(screen.getByText("Results (2)")).toBeInTheDocument();
70
+
71
+ const items = container.querySelectorAll("ol li");
72
+ expect(items).toHaveLength(2);
73
+ expect(items[0]).toHaveTextContent("Thing 1");
74
+ expect(items[1]).toHaveTextContent("Thing 2");
75
+ });
76
+
77
+ it("displays a result with an empty array", () => {
78
+ const emptyArrayResult: SelfTestsResult = { ...stringResult, result: [] };
79
+ const { container } = render(
80
+ <SelfTestResult result={emptyArrayResult} isFetching={false} />
81
+ );
82
+
83
+ expect(container.querySelector(".panel")).not.toBeInTheDocument();
84
+ expect(
85
+ screen.getByText("The test ran successfully, but no results were found.")
86
+ ).toHaveClass("warning");
87
+ });
88
+
89
+ it("displays a result with an exception", () => {
90
+ const exceptionResult: SelfTestsResult = {
91
+ ...stringResult,
92
+ success: false,
93
+ exception: { class: "IntegrationException", message: "Problem!" },
94
+ };
95
+ const { container } = render(
96
+ <SelfTestResult result={exceptionResult} isFetching={false} />
97
+ );
98
+
99
+ expect(container.querySelector("li")).toHaveClass("failure");
100
+ expect(screen.getByText("success: false")).toHaveClass(
101
+ "success-description"
102
+ );
103
+ expect(screen.getByText("exception: Problem!")).toHaveClass(
104
+ "exception-description"
105
+ );
106
+ });
107
+
108
+ it("displays debug_message when present", () => {
109
+ const exceptionResult: SelfTestsResult = {
110
+ ...stringResult,
111
+ success: false,
112
+ exception: {
113
+ class: "IntegrationException",
114
+ debug_message: "debug message...",
115
+ message: "Problem!",
116
+ },
117
+ };
118
+ render(<SelfTestResult result={exceptionResult} isFetching={false} />);
119
+
120
+ expect(screen.getByText("debug: debug message...")).toHaveClass(
121
+ "debug-description"
122
+ );
123
+ });
124
+
125
+ it("does not display debug_message when it is empty", () => {
126
+ const exceptionResult: SelfTestsResult = {
127
+ ...stringResult,
128
+ success: false,
129
+ exception: {
130
+ class: "IntegrationException",
131
+ debug_message: "",
132
+ message: "Problem!",
133
+ },
134
+ };
135
+ const { container } = render(
136
+ <SelfTestResult result={exceptionResult} isFetching={false} />
137
+ );
138
+
139
+ expect(
140
+ container.querySelector(".exception-description")
141
+ ).toBeInTheDocument();
142
+ expect(
143
+ container.querySelector(".debug-description")
144
+ ).not.toBeInTheDocument();
145
+ });
146
+
147
+ it("does not display debug_message when it is absent", () => {
148
+ const exceptionResult: SelfTestsResult = {
149
+ ...stringResult,
150
+ success: false,
151
+ exception: { class: "IntegrationException", message: "Problem!" },
152
+ };
153
+ const { container } = render(
154
+ <SelfTestResult result={exceptionResult} isFetching={false} />
155
+ );
156
+
157
+ expect(
158
+ container.querySelector(".exception-description")
159
+ ).toBeInTheDocument();
160
+ expect(
161
+ container.querySelector(".debug-description")
162
+ ).not.toBeInTheDocument();
163
+ });
164
+ });