@thepalaceproject/circulation-admin 1.35.0-post.3 → 1.35.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.
package/package.json CHANGED
@@ -152,5 +152,5 @@
152
152
  "*.{js,jsx,ts,tsx,css,md}": "prettier --write",
153
153
  "*.{js,css,md}": "prettier --write"
154
154
  },
155
- "version": "1.35.0-post.3"
155
+ "version": "1.35.0-post.4"
156
156
  }
@@ -0,0 +1,207 @@
1
+ import * as React from "react";
2
+ import { render, screen, waitFor } from "@testing-library/react";
3
+ import userEvent from "@testing-library/user-event";
4
+ import CollectionImportButton, {
5
+ CollectionImportButtonProps,
6
+ } from "../../../src/components/CollectionImportButton";
7
+ import { CollectionData, ProtocolData } from "../../../src/interfaces";
8
+
9
+ const protocolWithImport: ProtocolData = {
10
+ name: "Boundless",
11
+ label: "Boundless",
12
+ supports_import: true,
13
+ settings: [],
14
+ };
15
+
16
+ const protocolWithoutImport: ProtocolData = {
17
+ name: "Overdrive",
18
+ label: "Overdrive",
19
+ supports_import: false,
20
+ settings: [],
21
+ };
22
+
23
+ const savedCollection: CollectionData = {
24
+ id: 42,
25
+ protocol: "Boundless",
26
+ name: "Test Collection",
27
+ };
28
+
29
+ const unsavedCollection: CollectionData = {
30
+ protocol: "Boundless",
31
+ name: "Unsaved Collection",
32
+ };
33
+
34
+ function renderButton(overrides: Partial<CollectionImportButtonProps> = {}) {
35
+ const defaultProps: CollectionImportButtonProps = {
36
+ collection: savedCollection,
37
+ protocols: [protocolWithImport, protocolWithoutImport],
38
+ importCollection: jest.fn().mockResolvedValue(undefined),
39
+ disabled: false,
40
+ ...overrides,
41
+ };
42
+ return {
43
+ ...render(<CollectionImportButton {...defaultProps} />),
44
+ importCollection: defaultProps.importCollection as jest.Mock,
45
+ };
46
+ }
47
+
48
+ /** Expand the collapsed Import panel by clicking its header. */
49
+ async function expandPanel(user: ReturnType<typeof userEvent.setup>) {
50
+ await user.click(screen.getByText("Import"));
51
+ }
52
+
53
+ describe("CollectionImportButton", () => {
54
+ it("does not render when protocol lacks supports_import", () => {
55
+ const collection: CollectionData = {
56
+ id: 1,
57
+ protocol: "Overdrive",
58
+ name: "OD Collection",
59
+ };
60
+ const { container } = renderButton({ collection });
61
+ expect(container.innerHTML).toBe("");
62
+ });
63
+
64
+ it("does not render for unsaved collection (no id)", () => {
65
+ const { container } = renderButton({ collection: unsavedCollection });
66
+ expect(container.innerHTML).toBe("");
67
+ });
68
+
69
+ it("renders panel header when supported", () => {
70
+ renderButton();
71
+ expect(screen.getByText("Import")).toBeInTheDocument();
72
+ });
73
+
74
+ it("renders button and checkbox when panel is expanded", async () => {
75
+ const user = userEvent.setup();
76
+ renderButton();
77
+ await expandPanel(user);
78
+ expect(
79
+ screen.getByRole("button", { name: "Queue Import" })
80
+ ).toBeInTheDocument();
81
+ expect(screen.getByRole("checkbox")).toBeInTheDocument();
82
+ expect(screen.getByText("Force full re-import")).toBeInTheDocument();
83
+ });
84
+
85
+ it("checkbox toggles force state", async () => {
86
+ const user = userEvent.setup();
87
+ renderButton();
88
+ await expandPanel(user);
89
+ const checkbox = screen.getByRole("checkbox");
90
+ expect(checkbox).not.toBeChecked();
91
+ await user.click(checkbox);
92
+ expect(checkbox).toBeChecked();
93
+ await user.click(checkbox);
94
+ expect(checkbox).not.toBeChecked();
95
+ });
96
+
97
+ it("button triggers import with correct args (force=false)", async () => {
98
+ const user = userEvent.setup();
99
+ const { importCollection } = renderButton();
100
+ await expandPanel(user);
101
+ const button = screen.getByRole("button", { name: "Queue Import" });
102
+ await user.click(button);
103
+ expect(importCollection).toHaveBeenCalledWith(42, false);
104
+ });
105
+
106
+ it("button triggers import with force=true when checked", async () => {
107
+ const user = userEvent.setup();
108
+ const { importCollection } = renderButton();
109
+ await expandPanel(user);
110
+ const checkbox = screen.getByRole("checkbox");
111
+ await user.click(checkbox);
112
+ const button = screen.getByRole("button", { name: "Queue Import" });
113
+ await user.click(button);
114
+ expect(importCollection).toHaveBeenCalledWith(42, true);
115
+ });
116
+
117
+ it("shows success feedback with alert-success styling after import", async () => {
118
+ const user = userEvent.setup();
119
+ renderButton();
120
+ await expandPanel(user);
121
+ await user.click(screen.getByRole("button", { name: "Queue Import" }));
122
+ await waitFor(() => {
123
+ const feedback = screen.getByText("Import task queued.");
124
+ expect(feedback).toBeInTheDocument();
125
+ expect(feedback).toHaveClass("alert", "alert-success");
126
+ });
127
+ });
128
+
129
+ it("shows error feedback with alert-danger styling on failure", async () => {
130
+ const user = userEvent.setup();
131
+ const mockImport = jest
132
+ .fn()
133
+ .mockRejectedValue({ response: "Something went wrong" });
134
+ renderButton({ importCollection: mockImport });
135
+ await expandPanel(user);
136
+ await user.click(screen.getByRole("button", { name: "Queue Import" }));
137
+ await waitFor(() => {
138
+ const feedback = screen.getByText("Something went wrong");
139
+ expect(feedback).toBeInTheDocument();
140
+ expect(feedback).toHaveClass("alert", "alert-danger");
141
+ });
142
+ });
143
+
144
+ it("resets force checkbox and feedback when switching collections", async () => {
145
+ const user = userEvent.setup();
146
+ const { rerender, importCollection } = renderButton();
147
+ await expandPanel(user);
148
+
149
+ const checkbox = screen.getByRole("checkbox");
150
+ await user.click(checkbox);
151
+ expect(checkbox).toBeChecked();
152
+
153
+ await user.click(screen.getByRole("button", { name: "Queue Import" }));
154
+ await waitFor(() => {
155
+ expect(screen.getByText("Import task queued.")).toBeInTheDocument();
156
+ });
157
+
158
+ const nextCollection: CollectionData = {
159
+ id: 99,
160
+ protocol: "Boundless",
161
+ name: "Another Collection",
162
+ };
163
+ rerender(
164
+ <CollectionImportButton
165
+ collection={nextCollection}
166
+ protocols={[protocolWithImport, protocolWithoutImport]}
167
+ importCollection={importCollection}
168
+ disabled={false}
169
+ />
170
+ );
171
+
172
+ await waitFor(() => {
173
+ expect(screen.getByRole("checkbox")).not.toBeChecked();
174
+ expect(screen.queryByText("Import task queued.")).not.toBeInTheDocument();
175
+ });
176
+ });
177
+
178
+ it("disables button and checkbox when disabled prop is true", async () => {
179
+ const user = userEvent.setup();
180
+ renderButton({ disabled: true });
181
+ await expandPanel(user);
182
+ expect(screen.getByRole("button", { name: "Queue Import" })).toBeDisabled();
183
+ expect(screen.getByRole("checkbox")).toBeDisabled();
184
+ });
185
+
186
+ it("shows 'Queuing...' text while importing", async () => {
187
+ const user = userEvent.setup();
188
+ let resolveImport: () => void;
189
+ const pendingImport = new Promise<void>((resolve) => {
190
+ resolveImport = resolve;
191
+ });
192
+ const mockImport = jest.fn().mockReturnValue(pendingImport);
193
+ renderButton({ importCollection: mockImport });
194
+ await expandPanel(user);
195
+
196
+ await user.click(screen.getByRole("button", { name: "Queue Import" }));
197
+
198
+ expect(screen.getByRole("button", { name: "Queuing..." })).toBeDisabled();
199
+
200
+ resolveImport();
201
+ await waitFor(() => {
202
+ expect(
203
+ screen.getByRole("button", { name: "Queue Import" })
204
+ ).toBeEnabled();
205
+ });
206
+ });
207
+ });