@thepalaceproject/circulation-admin 1.43.0 → 1.44.0

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,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,216 @@
1
+ import * as React from "react";
2
+ import * as PropTypes from "prop-types";
3
+ import { render, screen, waitFor } from "@testing-library/react";
4
+ import userEvent from "@testing-library/user-event";
5
+
6
+ import buildStore from "../../../src/store";
7
+
8
+ // Each active tab renders the connected SelfTestsCategory, which fetches on mount
9
+ // and recurses into the self-tests machinery. This container's behavior is which
10
+ // slice of the store each category gets and under what type/link name, so mock it
11
+ // to a marker echoing those. `data-items` names the slice's contents so passing
12
+ // the wrong category's items — or the whole items object — is visible.
13
+ jest.mock("../../../src/components/SelfTestsCategory", () => ({
14
+ __esModule: true,
15
+ default: (props: any) => (
16
+ <div
17
+ data-testid="self-tests-category"
18
+ data-type={props.type}
19
+ data-link-name={props.linkName}
20
+ data-items={
21
+ Array.isArray(props.items)
22
+ ? props.items.map((item: any) => item.name).join(",")
23
+ : `not-an-array:${typeof props.items}`
24
+ }
25
+ />
26
+ ),
27
+ }));
28
+
29
+ import { SelfTestsTabContainer } from "../../../src/components/SelfTestsTabContainer";
30
+
31
+ // SelfTestsTabContainer reads `router` and `pathFor` from legacy react-router
32
+ // context via static contextTypes (handleSelect calls router.push). Supply them
33
+ // through a tiny legacy-context provider.
34
+ class LegacyContextProvider extends React.Component<{
35
+ router: { push: jest.Mock };
36
+ pathFor: jest.Mock;
37
+ children: React.ReactNode;
38
+ }> {
39
+ static childContextTypes = {
40
+ router: PropTypes.object,
41
+ pathFor: PropTypes.func,
42
+ };
43
+ getChildContext() {
44
+ return { router: this.props.router, pathFor: this.props.pathFor };
45
+ }
46
+ render() {
47
+ return <>{this.props.children}</>;
48
+ }
49
+ }
50
+
51
+ describe("SelfTestsTabContainer", () => {
52
+ let goToTab: jest.Mock;
53
+ let fetchItems: jest.Mock;
54
+ let router: { push: jest.Mock };
55
+ let pathFor: jest.Mock;
56
+ let store;
57
+
58
+ const collections = [
59
+ {
60
+ id: 1,
61
+ name: "collection 1",
62
+ protocol: "protocol",
63
+ self_test_results: {
64
+ duration: 1.75,
65
+ start: "2018-08-07T19:34:54Z",
66
+ end: "2018-08-07T19:34:55Z",
67
+ results: [
68
+ {
69
+ duration: 0.000119,
70
+ end: "2018-08-07T19:34:54Z",
71
+ exception: null,
72
+ name: "Initial setup.",
73
+ result: null,
74
+ start: "2018-08-07T19:34:54Z",
75
+ success: true,
76
+ },
77
+ ],
78
+ },
79
+ },
80
+ ];
81
+
82
+ beforeEach(() => {
83
+ store = buildStore();
84
+ goToTab = jest.fn();
85
+ fetchItems = jest.fn();
86
+ router = { push: jest.fn() };
87
+ pathFor = jest.fn();
88
+ });
89
+
90
+ const element = (overrides = {}) => {
91
+ const props = {
92
+ store,
93
+ goToTab,
94
+ fetchItems,
95
+ tab: "collections",
96
+ items: { protocols: [], collections },
97
+ ...overrides,
98
+ };
99
+ return (
100
+ <LegacyContextProvider router={router} pathFor={pathFor}>
101
+ <SelfTestsTabContainer {...props} />
102
+ </LegacyContextProvider>
103
+ );
104
+ };
105
+
106
+ const renderContainer = (overrides = {}) => render(element(overrides));
107
+
108
+ it("renders a tab container", () => {
109
+ const { container } = renderContainer();
110
+ expect(container.firstChild).toHaveClass("tab-container");
111
+ });
112
+
113
+ it("renders tabs and defaults to showing the Collections tab", () => {
114
+ const { container } = renderContainer();
115
+ const tabs = container.querySelectorAll("ul.nav-tabs li");
116
+ expect(tabs).toHaveLength(3);
117
+
118
+ expect(tabs[0]).toHaveTextContent("Collections");
119
+ expect(tabs[0]).toHaveClass("active");
120
+
121
+ expect(tabs[1]).toHaveTextContent("Patron Authentication");
122
+ expect(tabs[1]).not.toHaveClass("active");
123
+
124
+ expect(tabs[2]).toHaveTextContent("Metadata Services");
125
+ expect(tabs[2]).not.toHaveClass("active");
126
+ });
127
+
128
+ it("renders tab content", () => {
129
+ const { container } = renderContainer();
130
+
131
+ // Only the collections data is present, so its category renders; the other
132
+ // two tabs fall back to a loading indicator.
133
+ const markers = screen.getAllByTestId("self-tests-category");
134
+ expect(markers).toHaveLength(1);
135
+ expect(markers[0]).toHaveAttribute("data-type", "collection");
136
+ expect(markers[0]).toHaveAttribute("data-link-name", "collections");
137
+ expect(markers[0]).toHaveAttribute("data-items", "collection 1");
138
+
139
+ expect(container.querySelectorAll(".loading")).toHaveLength(2);
140
+ });
141
+
142
+ it("calls fetchItems on mount", () => {
143
+ renderContainer();
144
+ expect(fetchItems).toHaveBeenCalled();
145
+ });
146
+
147
+ it("shows an error message", () => {
148
+ const { container, rerender } = renderContainer();
149
+ expect(container.querySelectorAll(".alert-danger")).toHaveLength(0);
150
+
151
+ rerender(
152
+ element({
153
+ fetchError: { status: 401, response: "error fetching diagnostics" },
154
+ })
155
+ );
156
+
157
+ expect(container.querySelectorAll(".alert-danger")).toHaveLength(3);
158
+ });
159
+
160
+ it("calls goToTab and pushes to the router when a tab is clicked", async () => {
161
+ renderContainer();
162
+
163
+ await userEvent.click(
164
+ screen.getByRole("link", { name: "Patron Authentication" })
165
+ );
166
+
167
+ expect(goToTab).toHaveBeenCalledTimes(1);
168
+ expect(goToTab).toHaveBeenCalledWith("patronAuthServices");
169
+ await waitFor(() =>
170
+ expect(router.push).toHaveBeenCalledWith(
171
+ "/admin/web/troubleshooting/self-tests/patronAuthServices"
172
+ )
173
+ );
174
+ });
175
+
176
+ it("switches tabs when the tab prop changes", () => {
177
+ const { container, rerender } = renderContainer();
178
+ let tabs = container.querySelectorAll("ul.nav-tabs li");
179
+ expect(tabs[0]).toHaveClass("active");
180
+ expect(tabs[1]).not.toHaveClass("active");
181
+
182
+ rerender(element({ tab: "patronAuthServices" }));
183
+
184
+ tabs = container.querySelectorAll("ul.nav-tabs li");
185
+ expect(tabs[0]).not.toHaveClass("active");
186
+ expect(tabs[1]).toHaveClass("active");
187
+ });
188
+
189
+ it("derives the store key, type, and link name for each category", () => {
190
+ // A category only renders once its store key holds items, so populate all
191
+ // three with distinctly named items: the key each category is looked up
192
+ // under is then observable in the items its marker receives.
193
+ renderContainer({
194
+ items: {
195
+ collections: [{ id: 1, name: "a collection" }],
196
+ patron_auth_services: [{ id: 2, name: "a patron auth service" }],
197
+ metadata_services: [{ id: 3, name: "a metadata service" }],
198
+ },
199
+ });
200
+
201
+ const markers = screen.getAllByTestId("self-tests-category");
202
+ expect(markers).toHaveLength(3);
203
+
204
+ expect(markers[0]).toHaveAttribute("data-type", "collection");
205
+ expect(markers[0]).toHaveAttribute("data-link-name", "collections");
206
+ expect(markers[0]).toHaveAttribute("data-items", "a collection");
207
+
208
+ expect(markers[1]).toHaveAttribute("data-type", "patron_auth_service");
209
+ expect(markers[1]).toHaveAttribute("data-link-name", "patronAuth");
210
+ expect(markers[1]).toHaveAttribute("data-items", "a patron auth service");
211
+
212
+ expect(markers[2]).toHaveAttribute("data-type", "metadata_service");
213
+ expect(markers[2]).toHaveAttribute("data-link-name", "metadata");
214
+ expect(markers[2]).toHaveAttribute("data-items", "a metadata service");
215
+ });
216
+ });