@truedat/dd 8.12.2 → 8.12.5

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 (91) hide show
  1. package/package.json +3 -3
  2. package/src/api/__tests__/queries.spec.js +55 -0
  3. package/src/api/queries.js +31 -0
  4. package/src/components/Grant.js +106 -16
  5. package/src/components/GrantRequest.js +2 -2
  6. package/src/components/GrantRequestBulkApprovalPopup.js +0 -1
  7. package/src/components/GrantsDownloadButton.js +9 -1
  8. package/src/components/GrantsTable.js +4 -1
  9. package/src/components/StructureFields.js +7 -4
  10. package/src/components/StructureGrantCartUserSelector.js +29 -4
  11. package/src/components/StructureGrantDropdown.js +7 -1
  12. package/src/components/StructureGrants.js +4 -1
  13. package/src/components/StructureNoteActions.js +10 -1
  14. package/src/components/StructureNotes.js +42 -6
  15. package/src/components/StructureNotesEdit.js +38 -8
  16. package/src/components/StructuresUpdateOption.js +2 -2
  17. package/src/components/SystemStructures.js +3 -3
  18. package/src/components/__tests__/CatalogViewConfigForm.spec.js +2 -0
  19. package/src/components/__tests__/EditDomainForm.spec.js +3 -1
  20. package/src/components/__tests__/Grant.spec.js +171 -0
  21. package/src/components/__tests__/GrantApprovalRuleForm.spec.js +2 -0
  22. package/src/components/__tests__/GrantApprovalRuleNew.spec.js +8 -0
  23. package/src/components/__tests__/GrantApprovalRules.spec.js +4 -0
  24. package/src/components/__tests__/GrantRequest.spec.js +4 -4
  25. package/src/components/__tests__/GrantRequestBulkActions.spec.js +1 -4
  26. package/src/components/__tests__/GrantRequestsSearchResults.spec.js +2 -0
  27. package/src/components/__tests__/GrantsDownloadButton.spec.js +58 -2
  28. package/src/components/__tests__/GrantsTable.spec.js +22 -2
  29. package/src/components/__tests__/LineageDefs.spec.js +6 -1
  30. package/src/components/__tests__/LineageForm.spec.js +1 -0
  31. package/src/components/__tests__/LineageGraphToolbar.spec.js +19 -17
  32. package/src/components/__tests__/LineageInspectorPanel.spec.js +17 -9
  33. package/src/components/__tests__/ProfileGroup.spec.js +7 -1
  34. package/src/components/__tests__/ReferenceDatasetForm.spec.js +2 -0
  35. package/src/components/__tests__/ReferenceDatasetRoutes.spec.js +9 -0
  36. package/src/components/__tests__/StructureGrantCartCheckout.spec.js +39 -1
  37. package/src/components/__tests__/StructureGrantCartInformation.spec.js +5 -2
  38. package/src/components/__tests__/StructureGrantCartUserSelector.spec.js +128 -0
  39. package/src/components/__tests__/StructureGrantDropdown.spec.js +92 -0
  40. package/src/components/__tests__/StructureGrantListButton.spec.js +70 -0
  41. package/src/components/__tests__/StructureGrantSummaryButton.spec.js +36 -1
  42. package/src/components/__tests__/StructureGrants.spec.js +12 -0
  43. package/src/components/__tests__/StructureNoteActions.spec.js +96 -0
  44. package/src/components/__tests__/StructureNotes.spec.js +170 -1
  45. package/src/components/__tests__/StructureNotesEdit.spec.js +185 -1
  46. package/src/components/__tests__/StructureRelationRow.spec.js +72 -4
  47. package/src/components/__tests__/StructureRelations.spec.js +18 -0
  48. package/src/components/__tests__/StructureRoles.spec.js +9 -0
  49. package/src/components/__tests__/StructureRow.spec.js +13 -4
  50. package/src/components/__tests__/StructureSelector.spec.js +2 -0
  51. package/src/components/__tests__/StructureTag.spec.js +2 -0
  52. package/src/components/__tests__/StructureTagForm.spec.js +2 -0
  53. package/src/components/__tests__/StructureType.spec.js +3 -1
  54. package/src/components/__tests__/StructureTypeForm.spec.js +3 -1
  55. package/src/components/__tests__/StructureView.spec.js +17 -1
  56. package/src/components/__tests__/StructuresGrantRequestView.spec.js +2 -0
  57. package/src/components/__tests__/StructuresView.spec.js +2 -1
  58. package/src/components/__tests__/Tag.spec.js +4 -1
  59. package/src/components/__tests__/Tags.spec.js +7 -1
  60. package/src/components/__tests__/__snapshots__/GrantsTable.spec.js.snap +34 -2
  61. package/src/components/__tests__/__snapshots__/LineageDefs.spec.js.snap +32 -30
  62. package/src/components/__tests__/__snapshots__/StructureFields.spec.js.snap +1 -16
  63. package/src/components/__tests__/__snapshots__/StructureNoteActions.spec.js.snap +2 -1
  64. package/src/components/__tests__/__snapshots__/StructureNotesEdit.spec.js.snap +3 -0
  65. package/src/components/__tests__/__snapshots__/StructureRelationRow.spec.js.snap +23 -19
  66. package/src/components/__tests__/__snapshots__/StructureRow.spec.js.snap +28 -24
  67. package/src/components/__tests__/__snapshots__/StructuresUpdateOption.spec.js.snap +7 -3
  68. package/src/messages/en.js +2 -2
  69. package/src/messages/es.js +2 -2
  70. package/src/reducers/__tests__/ddMessage.spec.js +48 -0
  71. package/src/reducers/__tests__/grantRequestsCart.spec.js +17 -0
  72. package/src/reducers/__tests__/structureRedirect.spec.js +22 -0
  73. package/src/reducers/__tests__/structureRelations.spec.js +28 -0
  74. package/src/reducers/ddMessage.js +32 -0
  75. package/src/reducers/grantRequestsCart.js +3 -0
  76. package/src/reducers/structureRedirect.js +7 -2
  77. package/src/selectors/__tests__/__snapshots__/getGrantsColumns.spec.js.snap +1 -9
  78. package/src/selectors/__tests__/getGrantRequestsSearchColumns.spec.js +11 -4
  79. package/src/selectors/__tests__/getGrantsColumns.spec.js +295 -0
  80. package/src/selectors/__tests__/messages.spec.js +14 -0
  81. package/src/selectors/getBusinessConceptsLinksToStructuresColumnsSelector.js +3 -5
  82. package/src/selectors/getGrantRequestsSearchColumns.js +3 -5
  83. package/src/selectors/getGrantsColumns.js +85 -11
  84. package/src/selectors/getLinkedImplementationsToStructuresColumns.js +3 -7
  85. package/src/selectors/getStructureFieldColumns.js +7 -6
  86. package/src/selectors/getStructureProperties.js +2 -5
  87. package/src/selectors/getStructuresUploadEventColumns.js +2 -7
  88. package/src/selectors/messages.js +30 -0
  89. package/src/selectors/structureColumnsSelector.js +13 -16
  90. package/src/utils/__tests__/structureNoteActions.spec.js +196 -0
  91. package/src/utils/structureNoteActions.js +22 -0
@@ -25,7 +25,7 @@ describe("<LineageInspectorPanel />", () => {
25
25
  jest.useRealTimers();
26
26
  });
27
27
 
28
- it("shows real node facts, quality and conditional resource actions", () => {
28
+ it("shows real node facts, quality and conditional resource actions", async () => {
29
29
  render(
30
30
  <LineageInspectorPanel
31
31
  context={{
@@ -110,10 +110,14 @@ describe("<LineageInspectorPanel />", () => {
110
110
  .querySelector(".lineage-inspector__action-divider"),
111
111
  ).toBeInTheDocument();
112
112
 
113
- fireEvent.click(screen.getByRole("button", { name: "Center in graph" }));
114
- fireEvent.click(screen.getByRole("button", { name: "Minimize inspector" }));
115
- fireEvent.click(screen.getByRole("button", { name: "Unpin focus" }));
116
- fireEvent.click(screen.getByRole("button", { name: "Lineage" }));
113
+ await act(async () => {
114
+ fireEvent.click(screen.getByRole("button", { name: "Center in graph" }));
115
+ fireEvent.click(
116
+ screen.getByRole("button", { name: "Minimize inspector" }),
117
+ );
118
+ fireEvent.click(screen.getByRole("button", { name: "Unpin focus" }));
119
+ fireEvent.click(screen.getByRole("button", { name: "Lineage" }));
120
+ });
117
121
 
118
122
  expect(onCenterNode).toHaveBeenCalledWith("resource-1");
119
123
  expect(onMinimize).toHaveBeenCalledTimes(1);
@@ -153,7 +157,7 @@ describe("<LineageInspectorPanel />", () => {
153
157
  expect(screen.getAllByText("Table")).toHaveLength(2);
154
158
  });
155
159
 
156
- it("keeps each grouped original available for catalogue and lineage actions", () => {
160
+ it("keeps each grouped original available for catalogue and lineage actions", async () => {
157
161
  render(
158
162
  <LineageInspectorPanel
159
163
  context={{
@@ -184,13 +188,17 @@ describe("<LineageInspectorPanel />", () => {
184
188
  expect(
185
189
  screen.getByRole("link", { name: "View in Data Catalog" }).href,
186
190
  ).toContain("11");
187
- fireEvent.change(selector, {
188
- target: { value: selector.options[1].value },
191
+ await act(async () => {
192
+ fireEvent.change(selector, {
193
+ target: { value: selector.options[1].value },
194
+ });
189
195
  });
190
196
  expect(
191
197
  screen.getByRole("link", { name: "View in Data Catalog" }).href,
192
198
  ).toContain("22");
193
- fireEvent.click(screen.getByRole("button", { name: "Impact" }));
199
+ await act(async () =>
200
+ fireEvent.click(screen.getByRole("button", { name: "Impact" })),
201
+ );
194
202
  expect(createGraph).toHaveBeenCalledWith(
195
203
  expect.objectContaining({
196
204
  ids: [selector.options[1].value],
@@ -56,7 +56,13 @@ describe("<ExecutionRow />", () => {
56
56
  const props = { execution: { ...execution, profileGroup } };
57
57
 
58
58
  it("renders all columns when props match", async () => {
59
- const rendered = render(<ExecutionRow {...props} />);
59
+ const rendered = render(
60
+ <table>
61
+ <tbody>
62
+ <ExecutionRow {...props} />
63
+ </tbody>
64
+ </table>,
65
+ );
60
66
  await waitForLoad(rendered);
61
67
  expect(rendered.getAllByRole("cell").length).toBe(columns.length);
62
68
  });
@@ -19,6 +19,8 @@ const dataset = {
19
19
  const renderOpts = { mocks: [domainsMock({ action: "viewDataStructure" })] };
20
20
 
21
21
  describe("<ReferenceDatasetForm />", () => {
22
+ beforeEach(() => jest.useRealTimers());
23
+
22
24
  const onSubmit = jest.fn();
23
25
  const props = { dataset, onSubmit };
24
26
 
@@ -8,6 +8,15 @@ import {
8
8
  } from "@truedat/core/routes";
9
9
  import { ReferenceDatasetRoutes } from "../ReferenceDatasetRoutes";
10
10
 
11
+ jest.mock("@apollo/client", () => ({
12
+ ...jest.requireActual("@apollo/client"),
13
+ useQuery: jest.fn(() => ({
14
+ loading: false,
15
+ error: null,
16
+ data: { referenceDataset: null },
17
+ })),
18
+ }));
19
+
11
20
  jest.mock("@truedat/core/hooks/useAuthorized", () => ({
12
21
  useAuthorized: jest.fn(() => true),
13
22
  }));
@@ -1,6 +1,7 @@
1
1
  import userEvent from "@testing-library/user-event";
2
2
  import { render, waitForLoad } from "@truedat/test/render";
3
3
  import { singleTemplateMock, domainsMock } from "@truedat/test/mocks";
4
+ import { DOMAINS_QUERY, FUNCTIONS_QUERY } from "@truedat/core/api/queries";
4
5
  import { StructureGrantCartCheckout } from "../StructureGrantCartCheckout";
5
6
 
6
7
  jest.mock("@truedat/core/webContext", () => ({
@@ -23,6 +24,10 @@ jest.mock("@truedat/auth/hooks/useUsers", () => ({
23
24
  useGrantRequestableUsers: jest.fn(),
24
25
  }));
25
26
 
27
+ jest.mock("@truedat/auth/hooks/useRoles", () => ({
28
+ useRoles: () => ({ data: undefined }),
29
+ }));
30
+
26
31
  jest.mock("react-redux", () => {
27
32
  const actual = jest.requireActual("react-redux");
28
33
  return {
@@ -177,6 +182,17 @@ const grantRequestsCartSingleDomain = {
177
182
  };
178
183
 
179
184
  describe("StructureGrantCartCheckout", () => {
185
+ const domainQueryErrorMock = {
186
+ request: {
187
+ query: DOMAINS_QUERY,
188
+ variables: { action: "createForeignGrantRequest" },
189
+ },
190
+ error: new Error("Domains are not used by this checkout test"),
191
+ };
192
+ const functionsQueryErrorMock = {
193
+ request: { query: FUNCTIONS_QUERY },
194
+ error: new Error("Functions are not used by this checkout test"),
195
+ };
180
196
  const mockTrigger = jest.fn().mockResolvedValue({});
181
197
  const mockDispatch = jest.fn();
182
198
  const mockInitGroupTemplates = jest.fn();
@@ -208,7 +224,11 @@ describe("StructureGrantCartCheckout", () => {
208
224
 
209
225
  const variables = { scope: "gr" };
210
226
  const renderOpts = {
211
- mocks: [singleTemplateMock(variables)],
227
+ mocks: [
228
+ singleTemplateMock(variables),
229
+ functionsQueryErrorMock,
230
+ domainQueryErrorMock,
231
+ ],
212
232
  };
213
233
 
214
234
  it("matches snapshot when modification grant (single flow)", async () => {
@@ -272,6 +292,8 @@ describe("StructureGrantCartCheckout", () => {
272
292
  mocks: [
273
293
  singleTemplateMock({ scope: "gr" }),
274
294
  domainsMock({ action: "createForeignGrantRequest" }),
295
+ functionsQueryErrorMock,
296
+ domainQueryErrorMock,
275
297
  ],
276
298
  };
277
299
  const rendered = render(
@@ -297,6 +319,8 @@ describe("StructureGrantCartCheckout", () => {
297
319
  mocks: [
298
320
  singleTemplateMock({ scope: "gr" }),
299
321
  domainsMock({ action: "createForeignGrantRequest" }),
322
+ functionsQueryErrorMock,
323
+ domainQueryErrorMock,
300
324
  ],
301
325
  };
302
326
  const rendered = render(
@@ -324,6 +348,8 @@ describe("StructureGrantCartCheckout", () => {
324
348
  mocks: [
325
349
  singleTemplateMock({ scope: "gr" }),
326
350
  domainsMock({ action: "createForeignGrantRequest" }),
351
+ functionsQueryErrorMock,
352
+ domainQueryErrorMock,
327
353
  ],
328
354
  };
329
355
  const rendered = render(
@@ -348,6 +374,8 @@ describe("StructureGrantCartCheckout", () => {
348
374
  mocks: [
349
375
  singleTemplateMock({ scope: "gr" }),
350
376
  domainsMock({ action: "createForeignGrantRequest" }),
377
+ functionsQueryErrorMock,
378
+ domainQueryErrorMock,
351
379
  ],
352
380
  };
353
381
  const rendered = render(
@@ -373,6 +401,8 @@ describe("StructureGrantCartCheckout", () => {
373
401
  mocks: [
374
402
  singleTemplateMock({ scope: "gr" }),
375
403
  domainsMock({ action: "createForeignGrantRequest" }),
404
+ functionsQueryErrorMock,
405
+ domainQueryErrorMock,
376
406
  ],
377
407
  };
378
408
  const rendered = render(
@@ -408,6 +438,8 @@ describe("StructureGrantCartCheckout", () => {
408
438
  mocks: [
409
439
  singleTemplateMock({ scope: "gr" }),
410
440
  domainsMock({ action: "createForeignGrantRequest" }),
441
+ functionsQueryErrorMock,
442
+ domainQueryErrorMock,
411
443
  ],
412
444
  };
413
445
  const rendered = render(
@@ -456,6 +488,8 @@ describe("StructureGrantCartCheckout", () => {
456
488
  mocks: [
457
489
  singleTemplateMock({ scope: "gr" }),
458
490
  domainsMock({ action: "createForeignGrantRequest" }),
491
+ functionsQueryErrorMock,
492
+ domainQueryErrorMock,
459
493
  ],
460
494
  };
461
495
 
@@ -544,6 +578,8 @@ describe("StructureGrantCartCheckout", () => {
544
578
  mocks: [
545
579
  singleTemplateMock({ scope: "gr" }),
546
580
  domainsMock({ action: "createForeignGrantRequest" }),
581
+ functionsQueryErrorMock,
582
+ domainQueryErrorMock,
547
583
  ],
548
584
  };
549
585
  const rendered = render(
@@ -598,6 +634,8 @@ describe("StructureGrantCartCheckout", () => {
598
634
  mocks: [
599
635
  singleTemplateMock({ scope: "gr" }),
600
636
  domainsMock({ action: "createForeignGrantRequest" }),
637
+ functionsQueryErrorMock,
638
+ domainQueryErrorMock,
601
639
  ],
602
640
  };
603
641
 
@@ -1,5 +1,5 @@
1
1
  import { render, waitForLoad } from "@truedat/test/render";
2
- import { singleTemplateMock } from "@truedat/test/mocks";
2
+ import { singleTemplateMock, domainsMock } from "@truedat/test/mocks";
3
3
  import { StructureGrantCartInformation } from "../StructureGrantCartInformation";
4
4
 
5
5
  jest.mock("@truedat/auth/hooks/useRoles", () => {
@@ -38,7 +38,10 @@ describe("<StructureGrantCartInformation />", () => {
38
38
  };
39
39
 
40
40
  const renderOpts = {
41
- mocks: [singleTemplateMock(variables)],
41
+ mocks: [
42
+ singleTemplateMock(variables),
43
+ domainsMock({ action: "createForeignGrantRequest" }, []),
44
+ ],
42
45
  };
43
46
 
44
47
  it("matches the latest snapshot", async () => {
@@ -4,6 +4,10 @@ import { render, waitForLoad } from "@truedat/test/render";
4
4
  import { useGrantRequestableUsers } from "@truedat/auth/hooks/useUsers";
5
5
  import StructureGrantCartUserSelector from "../StructureGrantCartUserSelector";
6
6
 
7
+ jest.mock("@truedat/auth/hooks/useRoles", () => ({
8
+ useRoles: () => ({ data: undefined }),
9
+ }));
10
+
7
11
  jest.mock("@truedat/auth/hooks/useUsers", () => ({
8
12
  useGrantRequestableUsers: jest.fn(() => ({
9
13
  users: [
@@ -179,4 +183,128 @@ describe("<StructureGrantCartUserSelector />", () => {
179
183
  );
180
184
  expect(payloads.some((payload) => payload?.id === "group:10")).toBe(false);
181
185
  });
186
+
187
+ it("preselects user group when modificationGrant has user_group_id", async () => {
188
+ const dispatch = jest.fn();
189
+ useGrantRequestableUsers.mockImplementation(() => ({
190
+ users: [{ id: 1, full_name: "foo user" }],
191
+ groups: [{ id: 10, name: "foo", alias: "bar" }],
192
+ trigger: jest.fn(),
193
+ }));
194
+
195
+ const rendered = render(<StructureGrantCartUserSelector />, {
196
+ state: {
197
+ grantRequestsCart: {
198
+ modificationGrant: {
199
+ user_group_id: 10,
200
+ user_group: { id: 10, name: "foo", alias: "bar" },
201
+ user_id: null,
202
+ },
203
+ },
204
+ },
205
+ dispatch,
206
+ });
207
+ await waitForLoad(rendered);
208
+
209
+ await waitFor(() => {
210
+ const payloads = dispatch.mock.calls.map(
211
+ ([action]) => action?.payload ?? action,
212
+ );
213
+ expect(payloads.some((payload) => payload?.user_group_id === 10)).toBe(
214
+ true,
215
+ );
216
+ });
217
+ });
218
+
219
+ it("shows group alias in the modification header", async () => {
220
+ const rendered = render(<StructureGrantCartUserSelector />, {
221
+ state: {
222
+ grantRequestsCart: {
223
+ modificationGrant: {
224
+ user_group_id: 10,
225
+ user_group: { id: 10, alias: "bar", name: "foo" },
226
+ user_id: null,
227
+ },
228
+ },
229
+ },
230
+ messages: {
231
+ en: {
232
+ "grantRequestCart.user.label": "Request for:",
233
+ },
234
+ },
235
+ });
236
+ await waitForLoad(rendered);
237
+
238
+ expect(rendered.getByText(/bar/i)).toBeInTheDocument();
239
+ expect(rendered.queryByText(/undefined/i)).not.toBeInTheDocument();
240
+ });
241
+
242
+ it("shows group name in the modification header when alias is empty", async () => {
243
+ const rendered = render(<StructureGrantCartUserSelector />, {
244
+ state: {
245
+ grantRequestsCart: {
246
+ modificationGrant: {
247
+ user_group_id: 10,
248
+ user_group: { id: 10, alias: "", name: "foo" },
249
+ user_id: null,
250
+ },
251
+ },
252
+ },
253
+ messages: {
254
+ en: {
255
+ "grantRequestCart.user.label": "Request for:",
256
+ },
257
+ },
258
+ });
259
+ await waitForLoad(rendered);
260
+
261
+ expect(rendered.getByText(/foo/i)).toBeInTheDocument();
262
+ expect(rendered.queryByText(/bar/i)).not.toBeInTheDocument();
263
+ expect(rendered.queryByText(/undefined/i)).not.toBeInTheDocument();
264
+ });
265
+
266
+ it("shows deleted group in the modification header when the group is missing", async () => {
267
+ const rendered = render(<StructureGrantCartUserSelector />, {
268
+ state: {
269
+ grantRequestsCart: {
270
+ modificationGrant: {
271
+ user_group_id: 10,
272
+ user_group: null,
273
+ user_id: null,
274
+ },
275
+ },
276
+ },
277
+ messages: {
278
+ en: {
279
+ "grantRequestCart.user.label": "Request for:",
280
+ "deleted.group": "Deleted group",
281
+ },
282
+ },
283
+ });
284
+ await waitForLoad(rendered);
285
+
286
+ expect(rendered.getByText(/deleted group/i)).toBeInTheDocument();
287
+ expect(rendered.queryByText(/undefined/i)).not.toBeInTheDocument();
288
+ });
289
+
290
+ it("shows user full name in the modification header for a personal grant", async () => {
291
+ const rendered = render(<StructureGrantCartUserSelector />, {
292
+ state: {
293
+ grantRequestsCart: {
294
+ modificationGrant: {
295
+ user: { full_name: "foo user" },
296
+ },
297
+ },
298
+ },
299
+ messages: {
300
+ en: {
301
+ "grantRequestCart.user.label": "Request for:",
302
+ },
303
+ },
304
+ });
305
+ await waitForLoad(rendered);
306
+
307
+ expect(rendered.getByText(/foo user/i)).toBeInTheDocument();
308
+ expect(rendered.queryByText(/undefined/i)).not.toBeInTheDocument();
309
+ });
182
310
  });
@@ -276,4 +276,96 @@ describe("<StructureGrantDropdown />", () => {
276
276
  rendered.getByText(/grant.actions.viewrequest.confirmation.header/i),
277
277
  ).toBeInTheDocument();
278
278
  });
279
+
280
+ it("shows users icon and change action for active group grant", async () => {
281
+ const user = userEvent.setup({ delay: null });
282
+ const grant = {
283
+ pending_removal: false,
284
+ user_group_id: 10,
285
+ user_group: { id: 10, name: "foo", alias: "bar" },
286
+ };
287
+ const props = {
288
+ ...propsBuilder(grant),
289
+ grantRequest: null,
290
+ grantRequestStatus: null,
291
+ };
292
+ const rendered = render(<StructureGrantDropdown {...props} />);
293
+ await waitForLoad(rendered);
294
+
295
+ expect(
296
+ rendered.container.querySelector(".button.group-actions .users.icon"),
297
+ ).not.toBeInTheDocument();
298
+
299
+ fireEvent.mouseOver(await rendered.findByRole("listbox"));
300
+ expect(
301
+ await rendered.findByText(/structure.grant.has_grant/i),
302
+ ).toBeInTheDocument();
303
+ expect(
304
+ rendered.baseElement.querySelector(".users.icon"),
305
+ ).toBeInTheDocument();
306
+
307
+ await user.click(await rendered.findByRole("listbox"));
308
+ expect(
309
+ rendered.getByText(/structure.grant.request_grant_change/i),
310
+ ).toBeInTheDocument();
311
+ });
312
+
313
+ it("shows users icon in popup for pending group grant request", async () => {
314
+ const grantRequest = {
315
+ group: {
316
+ user_group_id: 10,
317
+ _embedded: {
318
+ user_group: { id: 10, name: "foo" },
319
+ },
320
+ },
321
+ };
322
+ const props = {
323
+ ...propsBuilder(null),
324
+ grantRequest,
325
+ grantRequestStatus: "pending",
326
+ };
327
+ const rendered = render(<StructureGrantDropdown {...props} />, {
328
+ state: { authentication: { user_name: "foo" } },
329
+ });
330
+ await waitForLoad(rendered);
331
+
332
+ expect(
333
+ rendered.container.querySelector(".button.group-actions .users.icon"),
334
+ ).not.toBeInTheDocument();
335
+
336
+ fireEvent.mouseOver(await rendered.findByRole("listbox"));
337
+ expect(
338
+ await rendered.findByText(/structure.grant.requested_access/i),
339
+ ).toBeInTheDocument();
340
+ expect(
341
+ rendered.baseElement.querySelector(".users.icon"),
342
+ ).toBeInTheDocument();
343
+ });
344
+
345
+ it("shows users icon in popup for active group grant", async () => {
346
+ const grant = {
347
+ pending_removal: false,
348
+ user_group_id: 10,
349
+ user_group: { id: 10, name: "foo", alias: "bar" },
350
+ };
351
+ const props = {
352
+ ...propsBuilder(grant),
353
+ grantRequest: null,
354
+ grantRequestStatus: null,
355
+ };
356
+ const rendered = render(<StructureGrantDropdown {...props} />);
357
+ await waitForLoad(rendered);
358
+
359
+ expect(
360
+ rendered.container.querySelector(".button.group-actions .users.icon"),
361
+ ).not.toBeInTheDocument();
362
+
363
+ fireEvent.mouseOver(await rendered.findByRole("listbox"));
364
+ expect(
365
+ await rendered.findByText(/structure.grant.has_grant/i),
366
+ ).toBeInTheDocument();
367
+ expect(
368
+ rendered.baseElement.querySelector(".users.icon"),
369
+ ).toBeInTheDocument();
370
+ });
279
371
  });
@@ -1,4 +1,5 @@
1
1
  import userEvent from "@testing-library/user-event";
2
+ import { fireEvent } from "@testing-library/react";
2
3
  import { render, waitForLoad } from "@truedat/test/render";
3
4
  import SearchContextWrapper from "@truedat/core/components/common/SearchContextWrapper";
4
5
  import StructureGrantListButton from "../StructureGrantListButton";
@@ -117,4 +118,73 @@ describe("<StructureGrantListButton />", () => {
117
118
  onSuccess();
118
119
  expect(refreshSearch).toHaveBeenCalled();
119
120
  });
121
+
122
+ it("shows users icon in popup for group grant in my_grants", async () => {
123
+ const groupStructure = {
124
+ ...structure,
125
+ my_grants: [
126
+ {
127
+ id: 2,
128
+ user_group_id: 10,
129
+ user_group: { id: 10, name: "foo" },
130
+ },
131
+ ],
132
+ };
133
+
134
+ const rendered = render(
135
+ <SearchContextWrapper props={searchProps}>
136
+ <StructureGrantListButton structure={groupStructure} />
137
+ </SearchContextWrapper>,
138
+ renderOpts,
139
+ );
140
+ await waitForLoad(rendered);
141
+
142
+ expect(
143
+ rendered.container.querySelector(".button.group-actions .users.icon"),
144
+ ).not.toBeInTheDocument();
145
+
146
+ fireEvent.mouseOver(await rendered.findByRole("listbox"));
147
+ expect(
148
+ await rendered.findByText(/structure.grant.has_grant/i),
149
+ ).toBeInTheDocument();
150
+ expect(
151
+ rendered.baseElement.querySelector(".users.icon"),
152
+ ).toBeInTheDocument();
153
+ });
154
+
155
+ it("shows users icon in popup for pending group grant request in my_grant_request", async () => {
156
+ const pendingGroupStructure = {
157
+ ...structure,
158
+ my_grant_request: {
159
+ status: "pending",
160
+ _embedded: { group: { user_group_id: 10 } },
161
+ },
162
+ };
163
+
164
+ const rendered = render(
165
+ <SearchContextWrapper props={searchProps}>
166
+ <StructureGrantListButton structure={pendingGroupStructure} />
167
+ </SearchContextWrapper>,
168
+ {
169
+ ...renderOpts,
170
+ state: {
171
+ ...renderOpts.state,
172
+ authentication: { user_name: "foo" },
173
+ },
174
+ },
175
+ );
176
+ await waitForLoad(rendered);
177
+
178
+ expect(
179
+ rendered.container.querySelector(".button.group-actions .users.icon"),
180
+ ).not.toBeInTheDocument();
181
+
182
+ fireEvent.mouseOver(await rendered.findByRole("listbox"));
183
+ expect(
184
+ await rendered.findByText(/structure.grant.requested_access/i),
185
+ ).toBeInTheDocument();
186
+ expect(
187
+ rendered.baseElement.querySelector(".users.icon"),
188
+ ).toBeInTheDocument();
189
+ });
120
190
  });
@@ -1,7 +1,12 @@
1
1
  import { waitFor } from "@testing-library/react";
2
2
  import userEvent from "@testing-library/user-event";
3
3
  import { render, waitForLoad } from "@truedat/test/render";
4
- import { LATEST_GRANT_REQUEST_BY_DS_QUERY } from "../../api/queries";
4
+ import {
5
+ buildDSVQuery,
6
+ DATA_STRUCTURE_VERSION_GRANTS_QUERY,
7
+ LEGACY_DATA_STRUCTURE_VERSION_QUERY,
8
+ LATEST_GRANT_REQUEST_BY_DS_QUERY,
9
+ } from "../../api/queries";
5
10
  import StructureGrantSummaryButton from "../StructureGrantSummaryButton";
6
11
  import { createGrantRequestStatus } from "../../routines";
7
12
  import en from "../../messages/en";
@@ -209,4 +214,34 @@ describe("<StructureGrantSummaryButton />", () => {
209
214
  ).toHaveLength(0);
210
215
  });
211
216
  });
217
+
218
+ it("requests user group fields in data structure version grants query", () => {
219
+ const query = DATA_STRUCTURE_VERSION_GRANTS_QUERY.loc.source.body;
220
+ expect(query).toMatch(/user_group_id|userGroupId/);
221
+ expect(query).toMatch(/user_group|userGroup/);
222
+ expect(query).toMatch(/source_group_name|sourceGroupName/);
223
+ expect(query).toMatch(/\balias\b/);
224
+ expect(query).toMatch(/\bname\b/);
225
+ });
226
+
227
+ it("requests user group fields in legacy data structure version query grant", () => {
228
+ const query = LEGACY_DATA_STRUCTURE_VERSION_QUERY.loc.source.body;
229
+ expect(query).toMatch(/user_group_id|userGroupId/);
230
+ expect(query).toMatch(/user_group|userGroup/);
231
+ expect(query).toMatch(/source_group_name|sourceGroupName/);
232
+ });
233
+
234
+ it("requests user group fields in data structure version query grant", () => {
235
+ const query = buildDSVQuery(false).loc.source.body;
236
+ expect(query).toMatch(/user_group_id|userGroupId/);
237
+ expect(query).toMatch(/user_group|userGroup/);
238
+ expect(query).toMatch(/source_group_name|sourceGroupName/);
239
+ });
240
+ });
241
+
242
+ describe("data structure version grants query current user group member", () => {
243
+ it("requests current user group member in data structure version grants query", () => {
244
+ const query = DATA_STRUCTURE_VERSION_GRANTS_QUERY.loc.source.body;
245
+ expect(query).toMatch(/current_user_group_member|currentUserGroupMember/);
246
+ });
212
247
  });
@@ -93,4 +93,16 @@ describe("<StructureGrants />", () => {
93
93
  const { container } = render(<StructureGrants {...props} />);
94
94
  expect(container).toMatchSnapshot();
95
95
  });
96
+
97
+ it("shows User / Group header for the user column", () => {
98
+ const rendered = render(<StructureGrants {...props} />, {
99
+ messages: {
100
+ en: {
101
+ "grant.header.user": "User",
102
+ "grants.props.user_or_group": "User / Group",
103
+ },
104
+ },
105
+ });
106
+ expect(rendered.getByText(/user \/ group/i)).toBeInTheDocument();
107
+ });
96
108
  });