@truedat/dd 8.12.1 → 8.12.2
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 +3 -3
- package/src/components/GrantRequestTemplateForm.js +76 -0
- package/src/components/StructureGrantCartCheckout.js +122 -59
- package/src/components/StructureGrantCartInformation.js +38 -9
- package/src/components/__tests__/StructureGrantCartCheckout.grouping.spec.js +95 -0
- package/src/components/__tests__/StructureGrantCartCheckout.selection.spec.js +416 -0
- package/src/components/__tests__/StructureGrantCartCheckout.spec.js +177 -3
- package/src/components/__tests__/__snapshots__/StructureGrantCartCheckout.spec.js.snap +217 -1
- package/src/components/__tests__/__snapshots__/StructureGrantCartInformation.spec.js.snap +54 -0
- package/src/reducers/__tests__/grantRequestsCart.spec.js +69 -0
- package/src/reducers/grantRequestsCart.js +17 -2
|
@@ -0,0 +1,416 @@
|
|
|
1
|
+
import { waitFor, within } from "@testing-library/react";
|
|
2
|
+
import PropTypes from "prop-types";
|
|
3
|
+
import userEvent from "@testing-library/user-event";
|
|
4
|
+
import { useSelector, useDispatch } from "react-redux";
|
|
5
|
+
import { render } from "@truedat/test/render";
|
|
6
|
+
import { TEMPLATES_QUERY, DOMAINS_QUERY } from "@truedat/core/api/queries";
|
|
7
|
+
import { useTemplateRelations } from "@truedat/df/hooks/useTemplateRelations";
|
|
8
|
+
import {
|
|
9
|
+
addGrantRequestToCart,
|
|
10
|
+
removeGrantRequestFromCart,
|
|
11
|
+
} from "../../routines";
|
|
12
|
+
import {
|
|
13
|
+
grantRequestsCart,
|
|
14
|
+
initialState,
|
|
15
|
+
} from "../../reducers/grantRequestsCart";
|
|
16
|
+
import StructureGrantCartCheckout from "../StructureGrantCartCheckout";
|
|
17
|
+
import { useTemplatesByIds } from "../../hooks/useTemplatesByIds";
|
|
18
|
+
import { useCheckoutGrantRequest } from "../../hooks/useCheckoutGrantRequest";
|
|
19
|
+
|
|
20
|
+
jest.mock("@truedat/core/webContext", () => ({
|
|
21
|
+
useWebContext: () => ({ scopesWithRelations: ["gr"] }),
|
|
22
|
+
}));
|
|
23
|
+
jest.mock("@truedat/df/hooks/useTemplateRelations", () => ({
|
|
24
|
+
useTemplateRelations: jest.fn(),
|
|
25
|
+
}));
|
|
26
|
+
jest.mock("../../hooks/useTemplatesByIds", () => ({
|
|
27
|
+
useTemplatesByIds: jest.fn(),
|
|
28
|
+
}));
|
|
29
|
+
jest.mock("../../hooks/useCheckoutGrantRequest", () => ({
|
|
30
|
+
useCheckoutGrantRequest: jest.fn(),
|
|
31
|
+
}));
|
|
32
|
+
jest.mock("../StructureGrantCart", () => () => <div>Structures</div>);
|
|
33
|
+
jest.mock("../StructureGrantCartUserSelector", () => () => (
|
|
34
|
+
<div data-testid="user-selector" />
|
|
35
|
+
));
|
|
36
|
+
|
|
37
|
+
const makeTemplate = (id, name) => ({
|
|
38
|
+
id,
|
|
39
|
+
name,
|
|
40
|
+
label: name,
|
|
41
|
+
scope: "gr",
|
|
42
|
+
content: [
|
|
43
|
+
{
|
|
44
|
+
name: "details",
|
|
45
|
+
fields: [
|
|
46
|
+
{
|
|
47
|
+
name: "reason",
|
|
48
|
+
label: "Reason",
|
|
49
|
+
type: "string",
|
|
50
|
+
placeholder: "Reason",
|
|
51
|
+
},
|
|
52
|
+
],
|
|
53
|
+
},
|
|
54
|
+
],
|
|
55
|
+
});
|
|
56
|
+
const assigned = makeTemplate("1", "Assigned");
|
|
57
|
+
const fallback = makeTemplate("2", "Default");
|
|
58
|
+
const alternative = makeTemplate("3", "Alternative");
|
|
59
|
+
const templates = [assigned, fallback, alternative];
|
|
60
|
+
const structure = (id, systemId) => ({
|
|
61
|
+
id,
|
|
62
|
+
system_id: systemId,
|
|
63
|
+
system: { id: systemId, name: `System ${systemId}` },
|
|
64
|
+
domain_ids: [1],
|
|
65
|
+
});
|
|
66
|
+
const trigger = jest.fn().mockResolvedValue({});
|
|
67
|
+
|
|
68
|
+
function CheckoutView({ visible = true }) {
|
|
69
|
+
const cart = useSelector((state) => state.grantRequestsCart);
|
|
70
|
+
const dispatch = useDispatch();
|
|
71
|
+
return (
|
|
72
|
+
<>
|
|
73
|
+
<button
|
|
74
|
+
onClick={() => dispatch(addGrantRequestToCart(structure(201, 20)))}
|
|
75
|
+
>
|
|
76
|
+
Add system
|
|
77
|
+
</button>
|
|
78
|
+
<button onClick={() => dispatch(removeGrantRequestFromCart({ id: 201 }))}>
|
|
79
|
+
Remove system
|
|
80
|
+
</button>
|
|
81
|
+
<output data-testid="cart">{JSON.stringify(cart)}</output>
|
|
82
|
+
{visible && <StructureGrantCartCheckout />}
|
|
83
|
+
</>
|
|
84
|
+
);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
CheckoutView.propTypes = { visible: PropTypes.bool };
|
|
88
|
+
|
|
89
|
+
const openCheckout = (structures, extra = {}) =>
|
|
90
|
+
render(<CheckoutView />, {
|
|
91
|
+
reducer: { grantRequestsCart },
|
|
92
|
+
state: { grantRequestsCart: { ...initialState, structures, ...extra } },
|
|
93
|
+
mocks: [
|
|
94
|
+
{
|
|
95
|
+
request: { query: TEMPLATES_QUERY, variables: { scope: "gr" } },
|
|
96
|
+
result: { data: { templates } },
|
|
97
|
+
maxUsageCount: 20,
|
|
98
|
+
},
|
|
99
|
+
{
|
|
100
|
+
request: {
|
|
101
|
+
query: DOMAINS_QUERY,
|
|
102
|
+
variables: { action: "createForeignGrantRequest" },
|
|
103
|
+
},
|
|
104
|
+
result: {
|
|
105
|
+
data: {
|
|
106
|
+
domains: [
|
|
107
|
+
{
|
|
108
|
+
id: "1",
|
|
109
|
+
name: "Domain",
|
|
110
|
+
externalId: "domain",
|
|
111
|
+
parentId: null,
|
|
112
|
+
actions: [],
|
|
113
|
+
},
|
|
114
|
+
],
|
|
115
|
+
},
|
|
116
|
+
},
|
|
117
|
+
maxUsageCount: 20,
|
|
118
|
+
},
|
|
119
|
+
],
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
beforeEach(() => {
|
|
123
|
+
jest.clearAllMocks();
|
|
124
|
+
useTemplateRelations.mockReturnValue({
|
|
125
|
+
resource: [],
|
|
126
|
+
default: { template_id: 2 },
|
|
127
|
+
loading: false,
|
|
128
|
+
});
|
|
129
|
+
useTemplatesByIds.mockReturnValue({ 1: assigned, 2: fallback });
|
|
130
|
+
useCheckoutGrantRequest.mockReturnValue({ trigger, isMutating: false });
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
it("restores a selected template and edited content after leaving and reentering single checkout", async () => {
|
|
134
|
+
const view = openCheckout([structure(101, 10)]);
|
|
135
|
+
const user = userEvent.setup({ delay: null });
|
|
136
|
+
await view.findByPlaceholderText("Reason");
|
|
137
|
+
const userSelector = await view.findByTestId("user-selector");
|
|
138
|
+
expect(userSelector.closest("form")).toContainElement(
|
|
139
|
+
view.getByPlaceholderText("Reason"),
|
|
140
|
+
);
|
|
141
|
+
expect(
|
|
142
|
+
view.container.querySelectorAll(".grant-cart-group-form"),
|
|
143
|
+
).toHaveLength(0);
|
|
144
|
+
await user.click(await view.findByRole("option", { name: "Alternative" }));
|
|
145
|
+
await user.type(view.getByPlaceholderText("Reason"), "My draft");
|
|
146
|
+
view.rerender(<CheckoutView visible={false} />);
|
|
147
|
+
view.rerender(<CheckoutView />);
|
|
148
|
+
await waitFor(() =>
|
|
149
|
+
expect(view.getByPlaceholderText("Reason")).toHaveValue("My draft"),
|
|
150
|
+
);
|
|
151
|
+
expect(
|
|
152
|
+
JSON.parse(view.getByTestId("cart").textContent).groupTemplates
|
|
153
|
+
.nodomain_others_notype.template,
|
|
154
|
+
).toBe("Alternative");
|
|
155
|
+
await user.click(view.getByRole("button", { name: "actions.save" }));
|
|
156
|
+
expect(trigger).toHaveBeenCalledWith(
|
|
157
|
+
expect.objectContaining({
|
|
158
|
+
grant_request_group: expect.objectContaining({
|
|
159
|
+
type: "Alternative",
|
|
160
|
+
requests: [
|
|
161
|
+
expect.objectContaining({
|
|
162
|
+
metadata: { reason: { value: "My draft", origin: "user" } },
|
|
163
|
+
}),
|
|
164
|
+
],
|
|
165
|
+
}),
|
|
166
|
+
}),
|
|
167
|
+
);
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
it("keeps assigned and selectable system drafts separate across a remount", async () => {
|
|
171
|
+
useTemplateRelations.mockReturnValue({
|
|
172
|
+
resource: [{ resource_id: 10, template_id: 1 }],
|
|
173
|
+
default: { template_id: 2 },
|
|
174
|
+
loading: false,
|
|
175
|
+
});
|
|
176
|
+
const view = openCheckout([structure(101, 10), structure(201, 20)]);
|
|
177
|
+
const user = userEvent.setup({ delay: null });
|
|
178
|
+
await waitFor(() =>
|
|
179
|
+
expect(view.getAllByPlaceholderText("Reason")).toHaveLength(2),
|
|
180
|
+
);
|
|
181
|
+
const groups = view.container.querySelectorAll(".grant-cart-group-form");
|
|
182
|
+
expect(within(groups[0]).queryByRole("listbox")).not.toBeInTheDocument();
|
|
183
|
+
await user.click(
|
|
184
|
+
await within(groups[1]).findByRole("option", { name: "Alternative" }),
|
|
185
|
+
);
|
|
186
|
+
await user.type(
|
|
187
|
+
within(groups[1]).getByPlaceholderText("Reason"),
|
|
188
|
+
"Group draft",
|
|
189
|
+
);
|
|
190
|
+
view.rerender(<CheckoutView visible={false} />);
|
|
191
|
+
view.rerender(<CheckoutView />);
|
|
192
|
+
await waitFor(() =>
|
|
193
|
+
expect(view.getAllByPlaceholderText("Reason")[1]).toHaveValue(
|
|
194
|
+
"Group draft",
|
|
195
|
+
),
|
|
196
|
+
);
|
|
197
|
+
await user.click(view.getByRole("button", { name: "actions.save" }));
|
|
198
|
+
expect(trigger).toHaveBeenCalledWith({
|
|
199
|
+
grant_request_groups: [
|
|
200
|
+
expect.objectContaining({ type: "Assigned" }),
|
|
201
|
+
expect.objectContaining({
|
|
202
|
+
type: "Alternative",
|
|
203
|
+
requests: [
|
|
204
|
+
expect.objectContaining({
|
|
205
|
+
data_structure_id: 201,
|
|
206
|
+
metadata: { reason: { value: "Group draft", origin: "user" } },
|
|
207
|
+
}),
|
|
208
|
+
],
|
|
209
|
+
}),
|
|
210
|
+
],
|
|
211
|
+
});
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
it("waits for relations and restores a saved selection when definitions arrive later", async () => {
|
|
215
|
+
useTemplateRelations.mockReturnValue({ resource: [], loading: true });
|
|
216
|
+
useTemplatesByIds.mockReturnValue({});
|
|
217
|
+
const view = openCheckout([structure(101, 10)], {
|
|
218
|
+
groupTemplates: {
|
|
219
|
+
nodomain_others_notype: {
|
|
220
|
+
template: "Alternative",
|
|
221
|
+
templateContent: { reason: { value: "Saved", origin: "user" } },
|
|
222
|
+
},
|
|
223
|
+
},
|
|
224
|
+
});
|
|
225
|
+
expect(view.queryByPlaceholderText("Reason")).not.toBeInTheDocument();
|
|
226
|
+
expect(view.getByRole("button", { name: "actions.save" })).toBeDisabled();
|
|
227
|
+
useTemplateRelations.mockReturnValue({
|
|
228
|
+
resource: [],
|
|
229
|
+
default: { template_id: 2 },
|
|
230
|
+
loading: false,
|
|
231
|
+
});
|
|
232
|
+
view.rerender(<CheckoutView />);
|
|
233
|
+
await waitFor(() =>
|
|
234
|
+
expect(view.getByPlaceholderText("Reason")).toHaveValue("Saved"),
|
|
235
|
+
);
|
|
236
|
+
useTemplatesByIds.mockReturnValue({ 2: { ...fallback } });
|
|
237
|
+
view.rerender(<CheckoutView />);
|
|
238
|
+
expect(view.getByPlaceholderText("Reason")).toHaveValue("Saved");
|
|
239
|
+
expect(
|
|
240
|
+
JSON.parse(view.getByTestId("cart").textContent).groupTemplates
|
|
241
|
+
.nodomain_others_notype.template,
|
|
242
|
+
).toBe("Alternative");
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
it("shares all grant templates across systems without a matching relation", async () => {
|
|
246
|
+
useTemplateRelations.mockReturnValue({
|
|
247
|
+
resource: [{ resource_id: 30, template_id: 1 }],
|
|
248
|
+
loading: false,
|
|
249
|
+
});
|
|
250
|
+
const view = openCheckout([structure(101, 10), structure(201, 20)]);
|
|
251
|
+
await waitFor(() =>
|
|
252
|
+
expect(view.getAllByRole("option", { name: "Assigned" })).toHaveLength(1),
|
|
253
|
+
);
|
|
254
|
+
expect(
|
|
255
|
+
view.queryByText("grantRequest.cart.group.others"),
|
|
256
|
+
).not.toBeInTheDocument();
|
|
257
|
+
expect(view.getByText("structure.grant.cart.template")).toBeInTheDocument();
|
|
258
|
+
expect(view.queryByPlaceholderText("Reason")).not.toBeInTheDocument();
|
|
259
|
+
expect(view.getByRole("button", { name: "actions.save" })).toBeDisabled();
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
it("preserves the draft when adding and removing another system", async () => {
|
|
263
|
+
useTemplateRelations.mockReturnValue({
|
|
264
|
+
resource: [{ resource_id: 20, template_id: 1 }],
|
|
265
|
+
default: { template_id: 2 },
|
|
266
|
+
loading: false,
|
|
267
|
+
});
|
|
268
|
+
const view = openCheckout([structure(101, 10)]);
|
|
269
|
+
const user = userEvent.setup({ delay: null });
|
|
270
|
+
await view.findByPlaceholderText("Reason");
|
|
271
|
+
await user.click(await view.findByRole("option", { name: "Alternative" }));
|
|
272
|
+
await user.type(view.getByPlaceholderText("Reason"), "First system");
|
|
273
|
+
expect(view.getByText("structure.grant.cart.template")).toBeInTheDocument();
|
|
274
|
+
expect(
|
|
275
|
+
view.container.querySelectorAll(".grant-cart-group-form"),
|
|
276
|
+
).toHaveLength(0);
|
|
277
|
+
await user.click(view.getByText("Add system"));
|
|
278
|
+
await waitFor(() =>
|
|
279
|
+
expect(view.getAllByPlaceholderText("Reason")).toHaveLength(2),
|
|
280
|
+
);
|
|
281
|
+
expect(view.getAllByPlaceholderText("Reason")[0]).toHaveValue("First system");
|
|
282
|
+
expect(
|
|
283
|
+
view.container.querySelectorAll(".grant-cart-group-form"),
|
|
284
|
+
).toHaveLength(2);
|
|
285
|
+
expect(
|
|
286
|
+
view.queryByText("structure.grant.cart.template"),
|
|
287
|
+
).not.toBeInTheDocument();
|
|
288
|
+
await user.click(view.getByText("Remove system"));
|
|
289
|
+
expect(view.getByPlaceholderText("Reason")).toHaveValue("First system");
|
|
290
|
+
expect(view.getByText("structure.grant.cart.template")).toBeInTheDocument();
|
|
291
|
+
expect(
|
|
292
|
+
view.container.querySelectorAll(".grant-cart-group-form"),
|
|
293
|
+
).toHaveLength(0);
|
|
294
|
+
await user.click(view.getByRole("button", { name: "actions.save" }));
|
|
295
|
+
expect(trigger).toHaveBeenCalledWith(
|
|
296
|
+
expect.objectContaining({
|
|
297
|
+
grant_request_group: expect.objectContaining({ type: "Alternative" }),
|
|
298
|
+
}),
|
|
299
|
+
);
|
|
300
|
+
});
|
|
301
|
+
|
|
302
|
+
it("restores a grant modification draft without reapplying its default", async () => {
|
|
303
|
+
const view = openCheckout([structure(101, 10)], {
|
|
304
|
+
modificationGrant: { id: 7, detail: {} },
|
|
305
|
+
});
|
|
306
|
+
const user = userEvent.setup({ delay: null });
|
|
307
|
+
await view.findByPlaceholderText("Reason");
|
|
308
|
+
await user.click(await view.findByRole("option", { name: "Alternative" }));
|
|
309
|
+
await user.type(view.getByPlaceholderText("Reason"), "Updated draft");
|
|
310
|
+
view.rerender(<CheckoutView visible={false} />);
|
|
311
|
+
view.rerender(<CheckoutView />);
|
|
312
|
+
await waitFor(() =>
|
|
313
|
+
expect(view.getByPlaceholderText("Reason")).toHaveValue("Updated draft"),
|
|
314
|
+
);
|
|
315
|
+
await user.click(view.getByRole("button", { name: "actions.save" }));
|
|
316
|
+
expect(trigger).toHaveBeenCalledWith(
|
|
317
|
+
expect.objectContaining({
|
|
318
|
+
grant_request_group: expect.objectContaining({
|
|
319
|
+
type: "Alternative",
|
|
320
|
+
modification_grant_id: 7,
|
|
321
|
+
}),
|
|
322
|
+
}),
|
|
323
|
+
);
|
|
324
|
+
});
|
|
325
|
+
|
|
326
|
+
it("retains the default assignment for an explicit system relation with no template", async () => {
|
|
327
|
+
useTemplateRelations.mockReturnValue({
|
|
328
|
+
resource: [{ resource_id: 10, template_id: null }],
|
|
329
|
+
default: { template_id: 2 },
|
|
330
|
+
loading: false,
|
|
331
|
+
});
|
|
332
|
+
const view = openCheckout([structure(101, 10)]);
|
|
333
|
+
await view.findByPlaceholderText("Reason");
|
|
334
|
+
expect(view.queryByRole("combobox")).not.toBeInTheDocument();
|
|
335
|
+
expect(
|
|
336
|
+
JSON.parse(view.getByTestId("cart").textContent).groupTemplates
|
|
337
|
+
.nodomain_10_2.template,
|
|
338
|
+
).toBe("Default");
|
|
339
|
+
});
|
|
340
|
+
|
|
341
|
+
it("preserves domain grouping for assigned templates", async () => {
|
|
342
|
+
useTemplateRelations.mockReturnValue({
|
|
343
|
+
resource: [{ resource_id: 10, template_id: 1, group_by_domain: true }],
|
|
344
|
+
loading: false,
|
|
345
|
+
});
|
|
346
|
+
const view = openCheckout([
|
|
347
|
+
structure(101, 10),
|
|
348
|
+
{ ...structure(102, 10), domain_ids: [2] },
|
|
349
|
+
]);
|
|
350
|
+
await waitFor(() =>
|
|
351
|
+
expect(view.getAllByPlaceholderText("Reason")).toHaveLength(2),
|
|
352
|
+
);
|
|
353
|
+
expect(view.queryByRole("combobox")).not.toBeInTheDocument();
|
|
354
|
+
const cart = JSON.parse(view.getByTestId("cart").textContent);
|
|
355
|
+
expect(Object.keys(cart.groupTemplates)).toEqual(["1_10_1", "2_10_1"]);
|
|
356
|
+
});
|
|
357
|
+
|
|
358
|
+
it("renders a late default after the template list loads and preserves edits on refresh", async () => {
|
|
359
|
+
useTemplatesByIds.mockReturnValue({});
|
|
360
|
+
const view = openCheckout([structure(101, 10)]);
|
|
361
|
+
await view.findByRole("option", { name: "Alternative" });
|
|
362
|
+
expect(view.queryByPlaceholderText("Reason")).not.toBeInTheDocument();
|
|
363
|
+
useTemplatesByIds.mockReturnValue({ 2: fallback });
|
|
364
|
+
view.rerender(<CheckoutView />);
|
|
365
|
+
const user = userEvent.setup({ delay: null });
|
|
366
|
+
await user.type(await view.findByPlaceholderText("Reason"), "Default draft");
|
|
367
|
+
useTemplatesByIds.mockReturnValue({ 2: { ...fallback } });
|
|
368
|
+
view.rerender(<CheckoutView />);
|
|
369
|
+
expect(view.getByPlaceholderText("Reason")).toHaveValue("Default draft");
|
|
370
|
+
});
|
|
371
|
+
|
|
372
|
+
it("retains a single draft created before per-group storage", async () => {
|
|
373
|
+
const view = openCheckout([structure(101, 10)], {
|
|
374
|
+
template: "Alternative",
|
|
375
|
+
templateContent: { reason: { value: "Existing draft", origin: "user" } },
|
|
376
|
+
validCart: true,
|
|
377
|
+
});
|
|
378
|
+
await waitFor(() =>
|
|
379
|
+
expect(view.getByPlaceholderText("Reason")).toHaveValue("Existing draft"),
|
|
380
|
+
);
|
|
381
|
+
expect(
|
|
382
|
+
JSON.parse(view.getByTestId("cart").textContent).groupTemplates
|
|
383
|
+
.nodomain_others_notype.template,
|
|
384
|
+
).toBe("Alternative");
|
|
385
|
+
});
|
|
386
|
+
|
|
387
|
+
it("submits all systems and domains in one group when no grouping policy exists", async () => {
|
|
388
|
+
useTemplateRelations.mockReturnValue({
|
|
389
|
+
resource: [],
|
|
390
|
+
default: null,
|
|
391
|
+
loading: false,
|
|
392
|
+
});
|
|
393
|
+
const view = openCheckout([
|
|
394
|
+
structure(101, 10),
|
|
395
|
+
{ ...structure(201, 20), domain_ids: [2] },
|
|
396
|
+
]);
|
|
397
|
+
const user = userEvent.setup({ delay: null });
|
|
398
|
+
expect(await view.findAllByRole("combobox")).toHaveLength(1);
|
|
399
|
+
await user.click(await view.findByRole("option", { name: "Alternative" }));
|
|
400
|
+
await user.type(view.getByPlaceholderText("Reason"), "Shared draft");
|
|
401
|
+
view.rerender(<CheckoutView visible={false} />);
|
|
402
|
+
view.rerender(<CheckoutView />);
|
|
403
|
+
await waitFor(() =>
|
|
404
|
+
expect(view.getByPlaceholderText("Reason")).toHaveValue("Shared draft"),
|
|
405
|
+
);
|
|
406
|
+
await user.click(view.getByRole("button", { name: "actions.save" }));
|
|
407
|
+
expect(trigger).toHaveBeenCalledWith({
|
|
408
|
+
grant_request_group: expect.objectContaining({
|
|
409
|
+
type: "Alternative",
|
|
410
|
+
requests: [
|
|
411
|
+
expect.objectContaining({ data_structure_id: 101 }),
|
|
412
|
+
expect.objectContaining({ data_structure_id: 201 }),
|
|
413
|
+
],
|
|
414
|
+
}),
|
|
415
|
+
});
|
|
416
|
+
});
|
|
@@ -180,12 +180,14 @@ describe("StructureGrantCartCheckout", () => {
|
|
|
180
180
|
const mockTrigger = jest.fn().mockResolvedValue({});
|
|
181
181
|
const mockDispatch = jest.fn();
|
|
182
182
|
const mockInitGroupTemplates = jest.fn();
|
|
183
|
+
const mockSelectGrantRequestTemplate = jest.fn();
|
|
183
184
|
const mockSelectGrantRequestTemplateForGroup = jest.fn();
|
|
184
185
|
const mockUpdateGrantRequestTemplateContentForGroup = jest.fn();
|
|
185
186
|
|
|
186
187
|
const defaultProps = {
|
|
187
188
|
grantRequestsCart: grantRequestsCartWithModification,
|
|
188
189
|
initGroupTemplates: mockInitGroupTemplates,
|
|
190
|
+
selectGrantRequestTemplate: mockSelectGrantRequestTemplate,
|
|
189
191
|
selectGrantRequestTemplateForGroup: mockSelectGrantRequestTemplateForGroup,
|
|
190
192
|
updateGrantRequestTemplateContentForGroup:
|
|
191
193
|
mockUpdateGrantRequestTemplateContentForGroup,
|
|
@@ -220,6 +222,28 @@ describe("StructureGrantCartCheckout", () => {
|
|
|
220
222
|
expect(rendered.container).toMatchSnapshot();
|
|
221
223
|
});
|
|
222
224
|
|
|
225
|
+
it("applies the default template when an update has no selection", async () => {
|
|
226
|
+
useWebContext.mockReturnValue({ scopesWithRelations: ["gr"] });
|
|
227
|
+
useTemplateRelations.mockReturnValue({
|
|
228
|
+
resource: [],
|
|
229
|
+
default: { template_id: 3 },
|
|
230
|
+
});
|
|
231
|
+
useTemplatesByIds.mockReturnValue({
|
|
232
|
+
3: { id: 3, name: "Default template", content: [] },
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
const rendered = render(
|
|
236
|
+
<StructureGrantCartCheckout {...defaultProps} />,
|
|
237
|
+
renderOpts,
|
|
238
|
+
);
|
|
239
|
+
await waitForLoad(rendered);
|
|
240
|
+
|
|
241
|
+
expect(useTemplatesByIds).toHaveBeenCalledWith([3]);
|
|
242
|
+
expect(mockSelectGrantRequestTemplate).toHaveBeenCalledWith(
|
|
243
|
+
"Default template",
|
|
244
|
+
);
|
|
245
|
+
});
|
|
246
|
+
|
|
223
247
|
it("matches snapshot when single flow without modification and empty cart", async () => {
|
|
224
248
|
useWebContext.mockReturnValue({ scopesWithRelations: [] });
|
|
225
249
|
useTemplateRelations.mockReturnValue({ resource: [] });
|
|
@@ -334,7 +358,7 @@ describe("StructureGrantCartCheckout", () => {
|
|
|
334
358
|
expect(rendered.container).toMatchSnapshot();
|
|
335
359
|
});
|
|
336
360
|
|
|
337
|
-
it("matches snapshot with single domain -
|
|
361
|
+
it("matches snapshot with single domain - uses single-group information panel", async () => {
|
|
338
362
|
useWebContext.mockReturnValue({ scopesWithRelations: ["gr"] });
|
|
339
363
|
useTemplateRelations.mockReturnValue({
|
|
340
364
|
resource: [{ resource_id: 10, template_id: 1, group_by_domain: true }],
|
|
@@ -371,12 +395,16 @@ describe("StructureGrantCartCheckout", () => {
|
|
|
371
395
|
1: { name: "Template A", content: [] },
|
|
372
396
|
2: { name: "Template B", content: [] },
|
|
373
397
|
});
|
|
398
|
+
const grantRequestsCart = {
|
|
399
|
+
...grantRequestsCartMultiGroup,
|
|
400
|
+
groupTemplates: {},
|
|
401
|
+
};
|
|
374
402
|
const props = {
|
|
375
403
|
...defaultProps,
|
|
376
|
-
grantRequestsCart
|
|
404
|
+
grantRequestsCart,
|
|
377
405
|
};
|
|
378
406
|
const renderOpts = {
|
|
379
|
-
state: { grantRequestsCart
|
|
407
|
+
state: { grantRequestsCart },
|
|
380
408
|
mocks: [
|
|
381
409
|
singleTemplateMock({ scope: "gr" }),
|
|
382
410
|
domainsMock({ action: "createForeignGrantRequest" }),
|
|
@@ -399,6 +427,47 @@ describe("StructureGrantCartCheckout", () => {
|
|
|
399
427
|
expect(mockInitGroupTemplates).not.toHaveBeenCalled();
|
|
400
428
|
});
|
|
401
429
|
|
|
430
|
+
it("preserves group selections when no automatic template is assigned", async () => {
|
|
431
|
+
useWebContext.mockReturnValue({ scopesWithRelations: ["gr"] });
|
|
432
|
+
useTemplateRelations.mockReturnValue({
|
|
433
|
+
resource: [
|
|
434
|
+
{ resource_id: 10, template_id: null },
|
|
435
|
+
{ resource_id: 20, template_id: null },
|
|
436
|
+
],
|
|
437
|
+
});
|
|
438
|
+
const grantRequestsCart = {
|
|
439
|
+
...grantRequestsCartMultiGroup,
|
|
440
|
+
groupTemplates: {
|
|
441
|
+
nodomain_10_notype: {
|
|
442
|
+
template: "user template A",
|
|
443
|
+
templateContent: {},
|
|
444
|
+
valid: true,
|
|
445
|
+
},
|
|
446
|
+
nodomain_20_notype: {
|
|
447
|
+
template: "user template B",
|
|
448
|
+
templateContent: {},
|
|
449
|
+
valid: true,
|
|
450
|
+
},
|
|
451
|
+
},
|
|
452
|
+
};
|
|
453
|
+
const props = { ...defaultProps, grantRequestsCart };
|
|
454
|
+
const renderOpts = {
|
|
455
|
+
state: { grantRequestsCart },
|
|
456
|
+
mocks: [
|
|
457
|
+
singleTemplateMock({ scope: "gr" }),
|
|
458
|
+
domainsMock({ action: "createForeignGrantRequest" }),
|
|
459
|
+
],
|
|
460
|
+
};
|
|
461
|
+
|
|
462
|
+
const rendered = render(
|
|
463
|
+
<StructureGrantCartCheckout {...props} />,
|
|
464
|
+
renderOpts,
|
|
465
|
+
);
|
|
466
|
+
await waitForLoad(rendered);
|
|
467
|
+
|
|
468
|
+
expect(mockInitGroupTemplates).not.toHaveBeenCalled();
|
|
469
|
+
});
|
|
470
|
+
|
|
402
471
|
it("sends user_group_id and not user_id on single checkout", async () => {
|
|
403
472
|
const user = userEvent.setup({ delay: null });
|
|
404
473
|
useWebContext.mockReturnValue({ scopesWithRelations: [] });
|
|
@@ -438,4 +507,109 @@ describe("StructureGrantCartCheckout", () => {
|
|
|
438
507
|
expect(grant_request_group.user_group_id).toBe(10);
|
|
439
508
|
expect(grant_request_group.user_id).toBeUndefined();
|
|
440
509
|
});
|
|
510
|
+
|
|
511
|
+
it("preselects default template for groups without a system relation", async () => {
|
|
512
|
+
useWebContext.mockReturnValue({ scopesWithRelations: ["gr"] });
|
|
513
|
+
useTemplateRelations.mockReturnValue({
|
|
514
|
+
resource: [
|
|
515
|
+
{ resource_id: 10, template_id: 1 },
|
|
516
|
+
{ resource_id: 20, template_id: 2 },
|
|
517
|
+
],
|
|
518
|
+
default: { template_id: 3 },
|
|
519
|
+
});
|
|
520
|
+
useTemplatesByIds.mockReturnValue({
|
|
521
|
+
1: { name: "foo", content: [] },
|
|
522
|
+
2: { name: "bar", content: [] },
|
|
523
|
+
3: { name: "baz", content: [] },
|
|
524
|
+
});
|
|
525
|
+
const grantRequestsCart = {
|
|
526
|
+
...grantRequestsCartMultiGroup,
|
|
527
|
+
structures: [
|
|
528
|
+
...grantRequestsCartMultiGroup.structures,
|
|
529
|
+
{
|
|
530
|
+
id: 301,
|
|
531
|
+
system_id: 30,
|
|
532
|
+
system: { id: 30, name: "foo system" },
|
|
533
|
+
domain_ids: [3],
|
|
534
|
+
domains: [{ id: 3, name: "foo domain" }],
|
|
535
|
+
},
|
|
536
|
+
],
|
|
537
|
+
};
|
|
538
|
+
const props = {
|
|
539
|
+
...defaultProps,
|
|
540
|
+
grantRequestsCart,
|
|
541
|
+
};
|
|
542
|
+
const renderOpts = {
|
|
543
|
+
state: { grantRequestsCart },
|
|
544
|
+
mocks: [
|
|
545
|
+
singleTemplateMock({ scope: "gr" }),
|
|
546
|
+
domainsMock({ action: "createForeignGrantRequest" }),
|
|
547
|
+
],
|
|
548
|
+
};
|
|
549
|
+
const rendered = render(
|
|
550
|
+
<StructureGrantCartCheckout {...props} />,
|
|
551
|
+
renderOpts,
|
|
552
|
+
);
|
|
553
|
+
await waitForLoad(rendered);
|
|
554
|
+
expect(mockSelectGrantRequestTemplateForGroup).toHaveBeenCalledWith({
|
|
555
|
+
key: "nodomain_others_notype",
|
|
556
|
+
template: "baz",
|
|
557
|
+
});
|
|
558
|
+
});
|
|
559
|
+
|
|
560
|
+
it("preserves a selected template when revisiting a defaulted group", async () => {
|
|
561
|
+
useWebContext.mockReturnValue({ scopesWithRelations: ["gr"] });
|
|
562
|
+
useTemplateRelations.mockReturnValue({
|
|
563
|
+
resource: [
|
|
564
|
+
{ resource_id: 10, template_id: 1 },
|
|
565
|
+
{ resource_id: 20, template_id: 2 },
|
|
566
|
+
],
|
|
567
|
+
default: { template_id: 3 },
|
|
568
|
+
});
|
|
569
|
+
useTemplatesByIds.mockReturnValue({
|
|
570
|
+
1: { name: "foo", content: [] },
|
|
571
|
+
2: { name: "bar", content: [] },
|
|
572
|
+
3: { name: "baz", content: [] },
|
|
573
|
+
});
|
|
574
|
+
const grantRequestsCart = {
|
|
575
|
+
...grantRequestsCartMultiGroup,
|
|
576
|
+
structures: [
|
|
577
|
+
...grantRequestsCartMultiGroup.structures,
|
|
578
|
+
{
|
|
579
|
+
id: 301,
|
|
580
|
+
system_id: 30,
|
|
581
|
+
system: { id: 30, name: "foo system" },
|
|
582
|
+
domain_ids: [3],
|
|
583
|
+
domains: [{ id: 3, name: "foo domain" }],
|
|
584
|
+
},
|
|
585
|
+
],
|
|
586
|
+
groupTemplates: {
|
|
587
|
+
...grantRequestsCartMultiGroup.groupTemplates,
|
|
588
|
+
nodomain_others_notype: {
|
|
589
|
+
template: "user selected template",
|
|
590
|
+
templateContent: {},
|
|
591
|
+
valid: true,
|
|
592
|
+
},
|
|
593
|
+
},
|
|
594
|
+
};
|
|
595
|
+
const props = { ...defaultProps, grantRequestsCart };
|
|
596
|
+
const renderOpts = {
|
|
597
|
+
state: { grantRequestsCart },
|
|
598
|
+
mocks: [
|
|
599
|
+
singleTemplateMock({ scope: "gr" }),
|
|
600
|
+
domainsMock({ action: "createForeignGrantRequest" }),
|
|
601
|
+
],
|
|
602
|
+
};
|
|
603
|
+
|
|
604
|
+
const rendered = render(
|
|
605
|
+
<StructureGrantCartCheckout {...props} />,
|
|
606
|
+
renderOpts,
|
|
607
|
+
);
|
|
608
|
+
await waitForLoad(rendered);
|
|
609
|
+
|
|
610
|
+
expect(mockSelectGrantRequestTemplateForGroup).not.toHaveBeenCalledWith({
|
|
611
|
+
key: "nodomain_others_notype",
|
|
612
|
+
template: "baz",
|
|
613
|
+
});
|
|
614
|
+
});
|
|
441
615
|
});
|