@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.
@@ -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 - shows singular domain message", async () => {
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: grantRequestsCartMultiGroup,
404
+ grantRequestsCart,
377
405
  };
378
406
  const renderOpts = {
379
- state: { grantRequestsCart: grantRequestsCartMultiGroup },
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
  });