@thepalaceproject/circulation-admin 1.43.0-post.7 → 1.43.0-post.9

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 (48) hide show
  1. package/package.json +1 -1
  2. package/tests/jest/components/AccountPage.test.tsx +41 -0
  3. package/tests/jest/components/AdvancedSearchBooleanFilter.test.tsx +231 -0
  4. package/tests/jest/components/AdvancedSearchFilter.test.tsx +142 -0
  5. package/tests/jest/components/AdvancedSearchFilterInput.test.tsx +212 -0
  6. package/tests/jest/components/AdvancedSearchFilterViewer.test.tsx +52 -0
  7. package/tests/jest/components/AdvancedSearchValueFilter.test.tsx +123 -0
  8. package/tests/jest/components/BookDetails.test.tsx +133 -0
  9. package/tests/jest/components/BookDetailsContainer.test.tsx +116 -0
  10. package/tests/jest/components/CatalogServices.test.tsx +63 -0
  11. package/tests/jest/components/CirculationEventsDownloadForm.test.tsx +41 -0
  12. package/tests/jest/components/ConfigPage.test.tsx +58 -0
  13. package/tests/jest/components/CustomListPage.test.tsx +57 -0
  14. package/tests/jest/components/CustomListSearch.test.tsx +275 -0
  15. package/tests/jest/components/CustomListsSidebar.test.tsx +251 -0
  16. package/tests/jest/components/DashboardPage.test.tsx +76 -0
  17. package/tests/jest/components/DebugAuthentication.test.tsx +1 -5
  18. package/tests/jest/components/DiagnosticsTabContainer.test.tsx +147 -0
  19. package/tests/jest/components/EditableConfigList.test.tsx +8 -2
  20. package/tests/jest/components/EntryPointsContainer.test.tsx +97 -0
  21. package/tests/jest/components/EntryPointsTabs.test.tsx +88 -0
  22. package/tests/jest/components/ErrorMessage.test.tsx +97 -0
  23. package/tests/jest/components/Footer.test.tsx +141 -0
  24. package/tests/jest/components/InventoryReportRequestModal.test.tsx +16 -42
  25. package/tests/jest/components/LaneEditor.test.tsx +2 -6
  26. package/tests/jest/components/LanePage.test.tsx +68 -0
  27. package/tests/jest/components/LanguageField.test.tsx +40 -0
  28. package/tests/jest/components/LoadButton.test.tsx +49 -0
  29. package/tests/jest/components/ManagePatrons.test.tsx +73 -0
  30. package/tests/jest/components/ManagePatronsForm.test.tsx +180 -0
  31. package/tests/jest/components/ManagePatronsTabContainer.test.tsx +143 -0
  32. package/tests/jest/components/MetadataServices.test.tsx +59 -0
  33. package/tests/jest/components/PatronBlockingRulesEditor.test.tsx +3 -1
  34. package/tests/jest/components/PatronInfo.test.tsx +54 -0
  35. package/tests/jest/components/SelfTestResult.test.tsx +164 -0
  36. package/tests/jest/components/SelfTestsCategory.test.tsx +200 -0
  37. package/tests/jest/components/ServiceWithRegistrationsEditForm.test.tsx +110 -0
  38. package/tests/jest/components/SetupPage.test.tsx +41 -0
  39. package/tests/jest/components/SingleStatListItem.test.tsx +62 -0
  40. package/tests/jest/components/Stats.test.tsx +4 -8
  41. package/tests/jest/components/TabContainer.test.tsx +68 -0
  42. package/tests/jest/components/Timestamp.test.tsx +74 -0
  43. package/tests/jest/components/TroubleshootingTabContainer.test.tsx +68 -0
  44. package/tests/jest/components/WelcomePage.test.tsx +43 -0
  45. package/tests/jest/components/WithEditButton.test.tsx +55 -0
  46. package/tests/jest/components/WithRemoveButton.test.tsx +95 -0
  47. package/tests/jest/index.test.tsx +50 -0
  48. package/tests/jest/components/AdvancedSearchBuilder.test.tsx +0 -38
@@ -0,0 +1,59 @@
1
+ import * as React from "react";
2
+ import { screen, within } from "@testing-library/react";
3
+ import { MetadataServices } from "../../../src/components/MetadataServices";
4
+ import renderWithContext from "../testUtils/renderWithContext";
5
+ import { defaultFeatureFlags } from "../../../src/utils/featureFlags";
6
+
7
+ describe("MetadataServices", () => {
8
+ const data = {
9
+ metadata_services: [
10
+ {
11
+ id: 2,
12
+ protocol: "test protocol",
13
+ name: "sample name",
14
+ settings: {
15
+ test_setting: "test setting",
16
+ },
17
+ libraries: [{ short_name: "nypl" }],
18
+ },
19
+ ],
20
+ protocols: [
21
+ {
22
+ name: "test protocol",
23
+ label: "test protocol label",
24
+ sitewide: false,
25
+ settings: [],
26
+ },
27
+ ],
28
+ allLibraries: [
29
+ {
30
+ short_name: "nypl",
31
+ },
32
+ ],
33
+ };
34
+
35
+ const config = { csrfToken: "token", featureFlags: defaultFeatureFlags };
36
+
37
+ it("shows metadata service list", () => {
38
+ renderWithContext(
39
+ <MetadataServices
40
+ data={data}
41
+ fetchData={jest.fn()}
42
+ editItem={jest.fn().mockResolvedValue(undefined)}
43
+ csrfToken="token"
44
+ isFetching={false}
45
+ />,
46
+ config
47
+ );
48
+
49
+ const items = screen.getAllByRole("listitem");
50
+ expect(items).toHaveLength(1);
51
+ expect(items[0]).toHaveTextContent("sample name: test protocol");
52
+
53
+ const editLink = within(items[0]).getByRole("link");
54
+ expect(editLink).toHaveAttribute(
55
+ "href",
56
+ "/admin/web/config/metadata/edit/2"
57
+ );
58
+ });
59
+ });
@@ -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
+ });
@@ -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
+ });
@@ -0,0 +1,200 @@
1
+ import * as React from "react";
2
+ import { screen } from "@testing-library/react";
3
+
4
+ import { renderWithProviders } from "../testUtils/withProviders";
5
+ import buildStore from "../../../src/store";
6
+
7
+ // `SelfTests` is a connected component that fetches self-test results on mount.
8
+ // It is incidental to what `SelfTestsCategory` itself does (build one panel per
9
+ // item, color-code them, decide `openByDefault`, and compute `sortByCollection`),
10
+ // so mock it to a marker that exposes the props the category passes down.
11
+ jest.mock("../../../src/components/SelfTests", () => ({
12
+ __esModule: true,
13
+ default: (props: any) => (
14
+ <div
15
+ data-testid="self-tests"
16
+ data-type={props.type}
17
+ data-item-id={props.item?.id}
18
+ data-item-name={props.item?.name}
19
+ data-sort-by-collection={String(props.sortByCollection)}
20
+ />
21
+ ),
22
+ }));
23
+
24
+ import SelfTestsCategory from "../../../src/components/SelfTestsCategory";
25
+
26
+ const initialSetupResult = {
27
+ duration: 0.000119,
28
+ end: "2018-08-07T19:34:54Z",
29
+ exception: null,
30
+ name: "Initial setup.",
31
+ result: null,
32
+ start: "2018-08-07T19:34:54Z",
33
+ success: true,
34
+ };
35
+
36
+ const collections: any[] = [
37
+ {
38
+ id: 1,
39
+ name: "collection with success",
40
+ protocol: "protocol",
41
+ self_test_results: {
42
+ duration: 1.75,
43
+ start: "2018-08-07T19:34:54Z",
44
+ end: "2018-08-07T19:34:55Z",
45
+ results: [initialSetupResult],
46
+ },
47
+ },
48
+ {
49
+ id: 2,
50
+ name: "collection with failure",
51
+ protocol: "protocol",
52
+ self_test_results: {
53
+ duration: 1.75,
54
+ start: "2018-08-07T19:34:54Z",
55
+ end: "2018-08-07T19:34:55Z",
56
+ results: [
57
+ initialSetupResult,
58
+ {
59
+ duration: 0,
60
+ end: "2018-08-07T19:34:55Z",
61
+ exception: {
62
+ class: "IntegrationException",
63
+ debug_message:
64
+ "Add the collection to a library that has a patron authentication service.",
65
+ message: "Collection is not associated with any libraries.",
66
+ },
67
+ name: "Acquiring test patron credentials.",
68
+ result: null,
69
+ start: "2018-08-07T19:34:55Z",
70
+ success: false,
71
+ },
72
+ ],
73
+ },
74
+ },
75
+ {
76
+ id: 3,
77
+ name: "collection with no results",
78
+ protocol: "protocol",
79
+ },
80
+ ];
81
+
82
+ const renderCategory = (items: any[] = collections) =>
83
+ renderWithProviders(
84
+ <SelfTestsCategory
85
+ type="collection"
86
+ linkName="collections"
87
+ csrfToken="token"
88
+ items={items}
89
+ store={buildStore()}
90
+ />
91
+ );
92
+
93
+ describe("SelfTestsCategory", () => {
94
+ it("renders a list of items", () => {
95
+ const { container } = renderCategory();
96
+
97
+ const root = container.querySelector(".self-tests-category");
98
+ expect(root).toBeInTheDocument();
99
+ expect(root).toHaveClass("has-additional-content");
100
+
101
+ const titles = Array.from(
102
+ container.querySelectorAll("ul .panel-title")
103
+ ).map((el) => el.textContent);
104
+ expect(titles).toStrictEqual([
105
+ "collection with success",
106
+ "collection with failure",
107
+ "collection with no results",
108
+ ]);
109
+
110
+ // One SelfTests per item, each carrying the category's type and its item.
111
+ const selfTests = screen.getAllByTestId("self-tests");
112
+ expect(selfTests).toHaveLength(3);
113
+ selfTests.forEach((el, idx) => {
114
+ expect(el).toHaveAttribute("data-type", "collection");
115
+ expect(el).toHaveAttribute("data-item-id", String(collections[idx].id));
116
+ });
117
+ });
118
+
119
+ it("color-codes", () => {
120
+ const { container } = renderCategory();
121
+ const panels = container.querySelectorAll(".panel");
122
+ expect(panels[0]).toHaveClass("panel-success");
123
+ expect(panels[1]).toHaveClass("panel-danger");
124
+ expect(panels[2]).toHaveClass("panel-default");
125
+ });
126
+
127
+ it("opens the panel by default if there's only one", () => {
128
+ // With multiple items, none are open by default.
129
+ const multi = renderCategory();
130
+ expect(
131
+ Array.from(multi.container.querySelectorAll(".panel-heading")).some(
132
+ (h) => h.getAttribute("aria-expanded") === "true"
133
+ )
134
+ ).toBe(false);
135
+ multi.unmount();
136
+
137
+ // A fresh render with a single item opens that panel by default.
138
+ // (`openByDefault` seeds the panel's initial state, so a new mount is needed.)
139
+ const single = renderCategory([collections[0]]);
140
+ const singleHeaders = single.container.querySelectorAll(".panel-heading");
141
+ expect(singleHeaders).toHaveLength(1);
142
+ expect(singleHeaders[0]).toHaveAttribute("aria-expanded", "true");
143
+ });
144
+
145
+ it("passes the sortByCollection prop to SelfTests", () => {
146
+ const { rerender } = renderCategory();
147
+ expect(screen.getAllByTestId("self-tests")[0]).toHaveAttribute(
148
+ "data-sort-by-collection",
149
+ "false"
150
+ );
151
+
152
+ const withCollection = {
153
+ id: 99,
154
+ name: "collection with sorting",
155
+ protocol: "protocol",
156
+ self_test_results: {
157
+ duration: 1.75,
158
+ start: "2018-08-07T19:34:54Z",
159
+ end: "2018-08-07T19:34:55Z",
160
+ results: [{ ...initialSetupResult, collection: "sort!" }],
161
+ },
162
+ };
163
+
164
+ rerender(
165
+ <SelfTestsCategory
166
+ type="collection"
167
+ linkName="collections"
168
+ csrfToken="token"
169
+ items={[withCollection, ...collections]}
170
+ store={buildStore()}
171
+ />
172
+ );
173
+
174
+ expect(screen.getAllByTestId("self-tests")[0]).toHaveAttribute(
175
+ "data-sort-by-collection",
176
+ "true"
177
+ );
178
+ });
179
+
180
+ it("handles unnamed items", () => {
181
+ const nameless: any = {
182
+ id: 1,
183
+ name: null,
184
+ protocol: "protocol",
185
+ self_test_results: {
186
+ duration: 1.75,
187
+ start: "2018-08-07T19:34:54Z",
188
+ end: "2018-08-07T19:34:55Z",
189
+ results: [initialSetupResult],
190
+ },
191
+ };
192
+ const { container } = renderCategory([nameless]);
193
+
194
+ expect(screen.getByText("Unnamed collection")).toHaveClass("panel-title");
195
+ // Panel id becomes `${name-without-spaces}-${id}`; its body id appends "-panel".
196
+ expect(
197
+ container.querySelector("#Unnamedcollection-1-panel")
198
+ ).toBeInTheDocument();
199
+ });
200
+ });
@@ -0,0 +1,110 @@
1
+ import * as React from "react";
2
+ import { render, screen } from "@testing-library/react";
3
+
4
+ import { DiscoveryServicesData } from "../../../src/interfaces";
5
+ import ServiceWithRegistrationsEditForm from "../../../src/components/ServiceWithRegistrationsEditForm";
6
+
7
+ // `ServiceWithRegistrationsEditForm` is generic/abstract over its data type;
8
+ // the legacy test subclassed it to bind the discovery-services data type.
9
+ class DiscoveryServiceEditForm extends ServiceWithRegistrationsEditForm<DiscoveryServicesData> {}
10
+
11
+ const serviceData = {
12
+ id: 1,
13
+ protocol: "protocol 1",
14
+ };
15
+ const protocolsData = [
16
+ {
17
+ name: "protocol 1",
18
+ label: "protocol 1 label",
19
+ supports_registration: true,
20
+ settings: [],
21
+ library_settings: [],
22
+ },
23
+ ];
24
+ const allLibraries = [
25
+ { short_name: "nypl", name: "New York Public Library" },
26
+ { short_name: "bpl", name: "Brooklyn Public Library" },
27
+ { short_name: "qpl", name: "Queens Public Library" },
28
+ ];
29
+ const libraryRegistrationsData = [
30
+ {
31
+ id: 1,
32
+ libraries: [
33
+ { short_name: "nypl", status: "success", stage: "production" },
34
+ { short_name: "bpl", status: "failure", stage: "testing" },
35
+ ],
36
+ },
37
+ ];
38
+ const servicesData: any = {
39
+ discovery_services: [serviceData],
40
+ protocols: protocolsData,
41
+ allLibraries: allLibraries,
42
+ libraryRegistrations: libraryRegistrationsData,
43
+ };
44
+
45
+ const renderForm = (save = jest.fn()) =>
46
+ render(
47
+ <DiscoveryServiceEditForm
48
+ disabled={false}
49
+ data={servicesData}
50
+ item={serviceData as any}
51
+ save={save}
52
+ urlBase="url base"
53
+ listDataKey="discovery_services"
54
+ />
55
+ );
56
+
57
+ describe("ServiceWithRegistrationsEditForm", () => {
58
+ it("renders the underlying service edit form fields", () => {
59
+ renderForm();
60
+
61
+ // The base ServiceEditForm (via `super.render()`) contributes the Name text
62
+ // field and the Required Fields section.
63
+ expect(screen.getByRole("textbox")).toBeInTheDocument();
64
+ expect(
65
+ screen.getByRole("heading", { name: "Required Fields" })
66
+ ).toBeInTheDocument();
67
+ });
68
+
69
+ it("renders a registration section reflecting each library's status", () => {
70
+ renderForm();
71
+
72
+ // The registration UI only renders when the protocol supports registration
73
+ // and the data carries libraries — i.e. those props flowed to LibraryRegistration.
74
+ expect(
75
+ screen.getByRole("heading", { name: "Register libraries" })
76
+ ).toBeInTheDocument();
77
+
78
+ expect(
79
+ screen.getByRole("link", { name: "New York Public Library" })
80
+ ).toBeInTheDocument();
81
+ expect(
82
+ screen.getByRole("link", { name: "Brooklyn Public Library" })
83
+ ).toBeInTheDocument();
84
+ expect(
85
+ screen.getByRole("link", { name: "Queens Public Library" })
86
+ ).toBeInTheDocument();
87
+
88
+ // Statuses come from `data.libraryRegistrations`.
89
+ expect(screen.getByText("Registered")).toHaveClass("bg-success");
90
+ expect(screen.getByText("Registration failed")).toHaveClass("bg-danger");
91
+ expect(screen.getByText("Not registered")).toHaveClass("bg-warning");
92
+ });
93
+
94
+ it("enables the per-library registration actions when not disabled", () => {
95
+ renderForm();
96
+
97
+ // `disabled={false}` flows through to each registration button.
98
+ const updateButton = screen.getByRole("button", {
99
+ name: "Update registration",
100
+ });
101
+ const retryButton = screen.getByRole("button", {
102
+ name: "Retry registration",
103
+ });
104
+ const registerButton = screen.getByRole("button", { name: "Register" });
105
+
106
+ expect(updateButton).not.toBeDisabled();
107
+ expect(retryButton).not.toBeDisabled();
108
+ expect(registerButton).not.toBeDisabled();
109
+ });
110
+ });
@@ -0,0 +1,41 @@
1
+ import * as React from "react";
2
+ import { screen } from "@testing-library/react";
3
+
4
+ import { renderWithProviders } from "../testUtils/withProviders";
5
+ import SetupPage from "../../../src/components/SetupPage";
6
+
7
+ describe("SetupPage", () => {
8
+ beforeEach(() => {
9
+ // SetupPage renders the connected IndividualAdmins list, which fetches the
10
+ // admin list on mount. Stub fetch so mounting does not hit the network.
11
+ jest.spyOn(globalThis, "fetch").mockResolvedValue(
12
+ new Response(JSON.stringify({ individualAdmins: [] }), {
13
+ status: 200,
14
+ headers: { "Content-Type": "application/json" },
15
+ })
16
+ );
17
+ });
18
+
19
+ afterEach(() => {
20
+ jest.restoreAllMocks();
21
+ });
22
+
23
+ it("shows the individual-admins setup (create) form", async () => {
24
+ renderWithProviders(<SetupPage />);
25
+
26
+ // SetupPage wires IndividualAdmins with settingUp + editOrCreate="create".
27
+ // That combination renders the "Welcome!" heading and the
28
+ // "Set up your system admin account" create-form heading; their presence in
29
+ // the real DOM proves the child received those props (the original shallow
30
+ // test asserted the props directly). No identifier is passed, so there is no
31
+ // per-item "Edit ..." heading — only the create form shows.
32
+ expect(
33
+ await screen.findByRole("heading", { name: "Welcome!" })
34
+ ).toBeInTheDocument();
35
+ expect(
36
+ screen.getByRole("heading", {
37
+ name: "Set up your system admin account",
38
+ })
39
+ ).toBeInTheDocument();
40
+ });
41
+ });