@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,220 @@
1
+ import * as React from "react";
2
+ import { render, screen, within, waitFor } from "@testing-library/react";
3
+ import userEvent from "@testing-library/user-event";
4
+ import PairedMenus from "../../../src/components/PairedMenus";
5
+ import { SettingData, LibraryData } from "../../../src/interfaces";
6
+
7
+ describe("PairedMenus", () => {
8
+ const inputListSetting: SettingData = {
9
+ key: "input",
10
+ label: "Input List",
11
+ default: ["a", "b", "c"],
12
+ options: [
13
+ { key: "a", label: "A" },
14
+ { key: "b", label: "B" },
15
+ { key: "c", label: "C" },
16
+ { key: "d", label: "D" },
17
+ { key: "e", label: "E" },
18
+ ],
19
+ paired: "dropdown",
20
+ };
21
+ const dropdownSetting: SettingData = {
22
+ key: "dropdown",
23
+ label: "Dropdown",
24
+ default: "b",
25
+ options: [
26
+ { key: "a", label: "A" },
27
+ { key: "b", label: "B" },
28
+ { key: "c", label: "C" },
29
+ { key: "d", label: "D" },
30
+ { key: "e", label: "E" },
31
+ ],
32
+ type: "select",
33
+ };
34
+
35
+ const renderPairedMenus = (props = {}) =>
36
+ render(
37
+ <PairedMenus
38
+ inputListSetting={inputListSetting}
39
+ dropdownSetting={dropdownSetting}
40
+ disabled={false}
41
+ {...props}
42
+ />
43
+ );
44
+
45
+ // The current InputList items are rendered as read-only text inputs inside the
46
+ // `.input-list-ul` list; return their displayed values.
47
+ const listItemValues = (container: HTMLElement): string[] =>
48
+ within(container.querySelector<HTMLElement>(".input-list-ul")!)
49
+ .getAllByRole("textbox")
50
+ .map((input) => (input as HTMLInputElement).value);
51
+
52
+ const dropdown = (container: HTMLElement): HTMLSelectElement =>
53
+ container.querySelector<HTMLSelectElement>('select[name="dropdown"]')!;
54
+
55
+ it("renders a fieldset with an InputList and a dropdown", () => {
56
+ const { container } = renderPairedMenus();
57
+ expect(
58
+ container.querySelector("fieldset.paired-menus")
59
+ ).toBeInTheDocument();
60
+ expect(container.querySelector(".input-list")).toBeInTheDocument();
61
+ expect(dropdown(container)).toBeInTheDocument();
62
+ });
63
+
64
+ it("renders the InputList items as read-only fields", () => {
65
+ const { container } = renderPairedMenus();
66
+ const listItems = within(
67
+ container.querySelector<HTMLElement>(".input-list-ul")!
68
+ ).getAllByRole("textbox");
69
+ expect(listItems).toHaveLength(3);
70
+ listItems.forEach((input) =>
71
+ expect((input as HTMLInputElement).readOnly).toBe(true)
72
+ );
73
+ expect(listItemValues(container)).toEqual(["A", "B", "C"]);
74
+ });
75
+
76
+ it("renders the dropdown with its default value and available options", () => {
77
+ const { container } = renderPairedMenus();
78
+ const select = dropdown(container);
79
+ expect(select).toHaveValue("b");
80
+ const options = within(select).getAllByRole("option");
81
+ expect(options.map((o) => o.textContent)).toEqual(["A", "B", "C"]);
82
+ expect(options.map((o) => (o as HTMLOptionElement).value)).toEqual([
83
+ "a",
84
+ "b",
85
+ "c",
86
+ ]);
87
+ });
88
+
89
+ it("optionally disables the InputList's button", () => {
90
+ const { rerender } = renderPairedMenus();
91
+ expect(screen.getByRole("button", { name: "Add" })).toBeEnabled();
92
+
93
+ rerender(
94
+ <PairedMenus
95
+ inputListSetting={inputListSetting}
96
+ dropdownSetting={dropdownSetting}
97
+ disabled={false}
98
+ readOnly={true}
99
+ />
100
+ );
101
+ expect(screen.getByRole("button", { name: "Add" })).toBeDisabled();
102
+ });
103
+
104
+ it("reflects the default InputList values and dropdown value", () => {
105
+ const { container } = renderPairedMenus();
106
+ expect(listItemValues(container)).toEqual(["A", "B", "C"]);
107
+ expect(dropdown(container)).toHaveValue("b");
108
+ });
109
+
110
+ it("takes an optional item prop", () => {
111
+ const item: LibraryData = {
112
+ name: "Library",
113
+ short_name: "lib",
114
+ uuid: "123",
115
+ settings: {
116
+ input: ["c", "d", "e"],
117
+ dropdown: "d",
118
+ },
119
+ };
120
+ const { container } = renderPairedMenus({ item });
121
+
122
+ const listItems = within(
123
+ container.querySelector<HTMLElement>(".input-list-ul")!
124
+ ).getAllByRole("textbox");
125
+ expect(listItems.map((i) => (i as HTMLInputElement).value)).toEqual([
126
+ "C",
127
+ "D",
128
+ "E",
129
+ ]);
130
+ expect(listItems.map((i) => (i as HTMLInputElement).name)).toEqual([
131
+ "input_c",
132
+ "input_d",
133
+ "input_e",
134
+ ]);
135
+
136
+ const select = dropdown(container);
137
+ expect(select).toHaveValue("d");
138
+ expect(
139
+ within(select)
140
+ .getAllByRole("option")
141
+ .map((o) => o.textContent)
142
+ ).toEqual(["C", "D", "E"]);
143
+ });
144
+
145
+ it("updates the InputList when an item is removed", async () => {
146
+ const user = userEvent.setup();
147
+ const { container } = renderPairedMenus();
148
+ expect(listItemValues(container)).toEqual(["A", "B", "C"]);
149
+
150
+ // Remove the first list item ("A").
151
+ await user.click(screen.getAllByRole("button", { name: /Delete/ })[0]);
152
+
153
+ await waitFor(() =>
154
+ expect(
155
+ within(
156
+ container.querySelector<HTMLElement>(".input-list-ul")!
157
+ ).getAllByRole("textbox")
158
+ ).toHaveLength(2)
159
+ );
160
+ expect(listItemValues(container)).toEqual(["B", "C"]);
161
+ });
162
+
163
+ it("recalculates the dropdown options based on the InputList values", async () => {
164
+ const user = userEvent.setup();
165
+ const { container } = renderPairedMenus();
166
+ expect(
167
+ within(dropdown(container))
168
+ .getAllByRole("option")
169
+ .map((o) => o.textContent)
170
+ ).toEqual(["A", "B", "C"]);
171
+
172
+ // Removing "A" leaves the InputList holding [b, c], so the dropdown may only
173
+ // offer B and C.
174
+ await user.click(screen.getAllByRole("button", { name: /Delete/ })[0]);
175
+
176
+ await waitFor(() =>
177
+ expect(
178
+ within(dropdown(container))
179
+ .getAllByRole("option")
180
+ .map((o) => o.textContent)
181
+ ).toEqual(["B", "C"])
182
+ );
183
+ });
184
+
185
+ it("updates the dropdown selection", async () => {
186
+ const user = userEvent.setup();
187
+ const { container } = renderPairedMenus();
188
+ const select = dropdown(container);
189
+ expect(select).toHaveValue("b");
190
+
191
+ await user.selectOptions(select, "c");
192
+ expect(select).toHaveValue("c");
193
+ });
194
+
195
+ it("displays a message when the dropdown has no available values", async () => {
196
+ const user = userEvent.setup();
197
+ const { container } = renderPairedMenus();
198
+
199
+ // Remove every item from the InputList.
200
+ for (let remaining = 3; remaining > 0; remaining--) {
201
+ const removeButtons = screen.getAllByRole("button", { name: /Delete/ });
202
+ expect(removeButtons).toHaveLength(remaining);
203
+ await user.click(removeButtons[0]);
204
+ await waitFor(() =>
205
+ expect(
206
+ screen.queryAllByRole("button", { name: /Delete/ })
207
+ ).toHaveLength(remaining - 1)
208
+ );
209
+ }
210
+
211
+ const message =
212
+ "In order to set this value, you must add at least one option from the menu above.";
213
+ expect(screen.getByText(message)).toBeInTheDocument();
214
+ expect(container.querySelector(".bg-warning")).toHaveTextContent(message);
215
+
216
+ // The dropdown select is replaced by the warning; only the add menu remains.
217
+ expect(dropdown(container)).not.toBeInTheDocument();
218
+ expect(container.querySelectorAll("select")).toHaveLength(1);
219
+ });
220
+ });
@@ -0,0 +1,114 @@
1
+ import * as React from "react";
2
+ import { screen, waitFor } from "@testing-library/react";
3
+
4
+ import { renderWithProviders } from "../testUtils/withProviders";
5
+ import buildStore from "../../../src/store";
6
+ // Render the CONNECTED default export so that mapStateToProps / mapDispatchToProps
7
+ // are exercised: the panel fetches its data on mount, the fetch populates the
8
+ // Redux store, and mapStateToProps feeds it back in as props.
9
+ import PatronAuthServices from "../../../src/components/PatronAuthServices";
10
+
11
+ const listData = {
12
+ patron_auth_services: [
13
+ {
14
+ id: 2,
15
+ protocol: "test protocol",
16
+ settings: { test_setting: "test setting" },
17
+ libraries: [
18
+ { short_name: "nypl", test_library_setting: "test library setting" },
19
+ ],
20
+ name: "nypl protocol",
21
+ },
22
+ ],
23
+ protocols: [
24
+ { name: "test protocol", label: "test protocol label", settings: [] },
25
+ ],
26
+ allLibraries: [{ short_name: "nypl" }],
27
+ };
28
+
29
+ /**
30
+ * Stub `fetch` so the panel's on-mount fetch resolves with `body`. Returning a
31
+ * fresh Response per call avoids "body already used" when several fetches run.
32
+ */
33
+ const stubFetch = (body: unknown) =>
34
+ jest.spyOn(globalThis, "fetch").mockImplementation(
35
+ async () =>
36
+ new Response(JSON.stringify(body), {
37
+ status: 200,
38
+ headers: { "Content-Type": "application/json" },
39
+ })
40
+ );
41
+
42
+ // A fresh store per test keeps the fetched data isolated between tests.
43
+ const renderConnected = (element: React.ReactElement) =>
44
+ renderWithProviders(element, {
45
+ reduxProviderProps: { store: buildStore() },
46
+ });
47
+
48
+ describe("PatronAuthServices", () => {
49
+ afterEach(() => {
50
+ jest.restoreAllMocks();
51
+ });
52
+
53
+ it("shows the patron auth service list", async () => {
54
+ stubFetch(listData);
55
+ const { container } = renderConnected(
56
+ <PatronAuthServices csrfToken="token" />
57
+ );
58
+
59
+ // The list appears once the connected component's on-mount fetch resolves.
60
+ // Its heading is the protocol-aware label built by PatronAuthServices.label().
61
+ expect(
62
+ await screen.findByRole("heading", {
63
+ level: 3,
64
+ name: /nypl protocol: test protocol label/,
65
+ })
66
+ ).toBeInTheDocument();
67
+
68
+ const editLink = container.querySelector("a.edit-item");
69
+ expect(editLink).not.toBeNull();
70
+ expect(editLink.getAttribute("href")).toBe(
71
+ "/admin/web/config/patronAuth/edit/2"
72
+ );
73
+
74
+ // No neighborhood analytics panel is shown in list mode.
75
+ expect(
76
+ screen.queryByText("Patron Neighborhood Analytics: Disabled")
77
+ ).not.toBeInTheDocument();
78
+ });
79
+
80
+ it("shows the neighborhood analytics panel when creating a service whose protocol has a neighborhood_mode setting", async () => {
81
+ const neighborhoodSetting = {
82
+ key: "neighborhood_mode",
83
+ label: "Neighborhood Mode",
84
+ description: "",
85
+ options: [],
86
+ };
87
+ const createData = {
88
+ patron_auth_services: [],
89
+ protocols: [
90
+ {
91
+ name: "test protocol",
92
+ label: "test protocol label",
93
+ settings: [neighborhoodSetting],
94
+ },
95
+ ],
96
+ allLibraries: [{ short_name: "nypl" }],
97
+ };
98
+ stubFetch(createData);
99
+
100
+ const { container } = renderConnected(
101
+ <PatronAuthServices csrfToken="token" editOrCreate="create" />
102
+ );
103
+
104
+ // Once the protocol data loads, the create form's extra section (the
105
+ // NeighborhoodAnalyticsForm) renders because the selected protocol carries a
106
+ // "neighborhood_mode" setting.
107
+ await waitFor(() =>
108
+ expect(container.querySelector("#neighborhood-panel")).toBeInTheDocument()
109
+ );
110
+ expect(
111
+ screen.getByText("Patron Neighborhood Analytics: Disabled")
112
+ ).toBeInTheDocument();
113
+ });
114
+ });
@@ -612,7 +612,9 @@ describe("PatronBlockingRulesEditor", () => {
612
612
  });
613
613
 
614
614
  it("disables all editing inputs and buttons when disabled prop is true", () => {
615
- renderEditor(<PatronBlockingRulesEditor value={existingRules} disabled={true} />);
615
+ renderEditor(
616
+ <PatronBlockingRulesEditor value={existingRules} disabled={true} />
617
+ );
616
618
 
617
619
  // The Help button stays enabled even in disabled mode (read-only affordance).
618
620
  const editingButtons = screen
@@ -0,0 +1,54 @@
1
+ import * as React from "react";
2
+ import { render } from "@testing-library/react";
3
+
4
+ import PatronInfo from "../../../src/components/PatronInfo";
5
+ import { PatronData } from "../../../src/interfaces";
6
+
7
+ const patrons: PatronData[] = [
8
+ {
9
+ authorization_expires: "",
10
+ username: "User Name",
11
+ personal_name: "Personal Name",
12
+ email_address: "user@email.com",
13
+ authorization_identifier: "1234",
14
+ authorization_identifiers: [""],
15
+ block_reason: "",
16
+ external_type: "",
17
+ fines: "",
18
+ permanent_id: "",
19
+ },
20
+ {
21
+ authorization_expires: "",
22
+ personal_name: "Personal Name2",
23
+ authorization_identifier: "5678",
24
+ authorization_identifiers: [""],
25
+ block_reason: "",
26
+ external_type: "",
27
+ fines: "",
28
+ permanent_id: "",
29
+ },
30
+ ];
31
+
32
+ describe("PatronInfo", () => {
33
+ it("should display the patron's information", () => {
34
+ const { container } = render(<PatronInfo patron={patrons[0]} />);
35
+
36
+ const items = container.querySelectorAll("ul.patron-data-list > li");
37
+ expect(items).toHaveLength(4);
38
+ expect(items[0]).toHaveTextContent("Username:User Name");
39
+ expect(items[1]).toHaveTextContent("Personal Name:Personal Name");
40
+ expect(items[2]).toHaveTextContent("Email Address:user@email.com");
41
+ expect(items[3]).toHaveTextContent("Identifier:1234");
42
+ });
43
+
44
+ it("should update the patron's information if a new patron was searched and found", () => {
45
+ const { container, rerender } = render(<PatronInfo patron={patrons[0]} />);
46
+
47
+ rerender(<PatronInfo patron={patrons[1]} />);
48
+
49
+ const items = container.querySelectorAll("ul.patron-data-list > li");
50
+ expect(items).toHaveLength(2);
51
+ expect(items[0]).toHaveTextContent("Personal Name:Personal Name2");
52
+ expect(items[1]).toHaveTextContent("Identifier:5678");
53
+ });
54
+ });