@truedat/dd 4.54.12 → 4.56.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 (44) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/package.json +6 -6
  3. package/src/api/mutations.js +28 -0
  4. package/src/api/queries.js +36 -0
  5. package/src/components/GrantApprovalRule.js +149 -0
  6. package/src/components/GrantApprovalRuleConditions.js +134 -0
  7. package/src/components/GrantApprovalRuleDeleteButton.js +44 -0
  8. package/src/components/GrantApprovalRuleEdit.js +38 -0
  9. package/src/components/GrantApprovalRuleForm.js +264 -0
  10. package/src/components/GrantApprovalRuleNew.js +15 -0
  11. package/src/components/GrantApprovalRules.js +113 -0
  12. package/src/components/GrantRequest.js +4 -3
  13. package/src/components/GrantRoutes.js +30 -0
  14. package/src/components/__tests__/GrantApprovalRule.spec.js +69 -0
  15. package/src/components/__tests__/GrantApprovalRuleConditions.spec.js +120 -0
  16. package/src/components/__tests__/GrantApprovalRuleDeleteButton.spec.js +56 -0
  17. package/src/components/__tests__/GrantApprovalRuleEdit.spec.js +76 -0
  18. package/src/components/__tests__/GrantApprovalRuleForm.spec.js +135 -0
  19. package/src/components/__tests__/GrantApprovalRuleNew.spec.js +40 -0
  20. package/src/components/__tests__/GrantApprovalRules.spec.js +107 -0
  21. package/src/components/__tests__/StructureGrantDropdown.spec.js +1 -1
  22. package/src/components/__tests__/__snapshots__/GrantApprovalRule.spec.js.snap +239 -0
  23. package/src/components/__tests__/__snapshots__/GrantApprovalRuleConditions.spec.js.snap +819 -0
  24. package/src/components/__tests__/__snapshots__/GrantApprovalRuleDeleteButton.spec.js.snap +11 -0
  25. package/src/components/__tests__/__snapshots__/GrantApprovalRuleEdit.spec.js.snap +487 -0
  26. package/src/components/__tests__/__snapshots__/GrantApprovalRuleForm.spec.js.snap +1036 -0
  27. package/src/components/__tests__/__snapshots__/GrantApprovalRuleNew.spec.js.snap +464 -0
  28. package/src/components/__tests__/__snapshots__/GrantApprovalRules.spec.js.snap +266 -0
  29. package/src/components/__tests__/__snapshots__/GrantRoutes.spec.js.snap +27 -5
  30. package/src/messages/en.js +40 -0
  31. package/src/messages/es.js +41 -0
  32. package/src/reducers/structureRedirect.js +23 -3
  33. package/src/routines.js +3 -0
  34. package/src/sagas/__tests__/createGrantApprovalRule.spec.js +96 -0
  35. package/src/sagas/__tests__/deleteGrantApprovalRule.spec.js +85 -0
  36. package/src/sagas/__tests__/updateGrantApprovalRule.spec.js +96 -0
  37. package/src/sagas/createGrantApprovalRule.js +26 -0
  38. package/src/sagas/deleteGrantApprovalRule.js +32 -0
  39. package/src/sagas/index.js +9 -0
  40. package/src/sagas/updateGrantApprovalRule.js +26 -0
  41. package/src/selectors/__tests__/getGrantsColumns.spec.js +4 -0
  42. package/src/components/GrantRequestCrumbs.js +0 -25
  43. package/src/components/__tests__/GrantRequestCrumbs.spec.js +0 -18
  44. package/src/components/__tests__/__snapshots__/GrantRequestCrumbs.spec.js.snap +0 -100
@@ -0,0 +1,56 @@
1
+ import React from "react";
2
+ import { render } from "@truedat/test/render";
3
+ import userEvent from "@testing-library/user-event";
4
+ import GrantApprovalRuleDeleteButton from "../GrantApprovalRuleDeleteButton";
5
+ import { deleteGrantApprovalRule } from "../../routines";
6
+ import en from "../../messages/en";
7
+
8
+ const renderOpts = {
9
+ messages: {
10
+ en: { ...en, "confirmation.yes": "yes", "confirmation.no": "no" },
11
+ },
12
+ };
13
+
14
+ describe("<GrantApprovalRuleDeleteButton />", () => {
15
+ const approvalRule = {
16
+ __typename: "ApprovalRule",
17
+ id: "123",
18
+ };
19
+ it("matches the last snapshot", () => {
20
+ const props = { deleteGrantApprovalRule, approvalRule };
21
+ const { container } = render(
22
+ <GrantApprovalRuleDeleteButton {...props} />,
23
+ renderOpts
24
+ );
25
+ expect(container).toMatchSnapshot();
26
+ });
27
+
28
+ it("call deleteGrantApprovalRule routine on confirm", () => {
29
+ const dispatch = jest.fn();
30
+ const props = { deleteGrantApprovalRule, approvalRule };
31
+ const { getByRole } = render(<GrantApprovalRuleDeleteButton {...props} />, {
32
+ ...renderOpts,
33
+ dispatch,
34
+ });
35
+ userEvent.click(getByRole("button", { name: /delete/i }));
36
+ userEvent.click(getByRole("button", { name: /yes/i }));
37
+
38
+ expect(dispatch).toHaveBeenLastCalledWith({
39
+ ...deleteGrantApprovalRule(),
40
+ payload: { id: approvalRule.id },
41
+ });
42
+ });
43
+
44
+ it("do not call deleteGrantApprovalRule routine on cancel", () => {
45
+ const dispatch = jest.fn();
46
+ const props = { deleteGrantApprovalRule, approvalRule };
47
+ const { getByRole } = render(<GrantApprovalRuleDeleteButton {...props} />, {
48
+ ...renderOpts,
49
+ dispatch,
50
+ });
51
+ userEvent.click(getByRole("button", { name: /delete/i }));
52
+ userEvent.click(getByRole("button", { name: /no/i }));
53
+
54
+ expect(dispatch).toHaveBeenCalledTimes(0);
55
+ });
56
+ });
@@ -0,0 +1,76 @@
1
+ import React from "react";
2
+ import { render } from "@truedat/test/render";
3
+ import { waitFor } from "@testing-library/react";
4
+ import { domainsMock, rolesMock } from "@truedat/test/mocks";
5
+ import { GrantApprovalRuleEdit } from "../GrantApprovalRuleEdit";
6
+ import { GRANT_APPROVAL_RULE_QUERY } from "../../api/queries";
7
+ import en from "../../messages/en";
8
+
9
+ jest.mock("react-router-dom", () => ({
10
+ ...jest.requireActual("react-router-dom"),
11
+ useParams: () => ({ id: 123 }),
12
+ }));
13
+
14
+ const grantApprovalRule = {
15
+ grantApprovalRule: {
16
+ __typename: "ApprovalRule",
17
+ id: "123",
18
+ name: "rule_name",
19
+ action: "approve",
20
+ role: "grants",
21
+ domainIds: ["1"],
22
+ userId: "1",
23
+ domains: [
24
+ {
25
+ __typename: "Domain",
26
+ name: "domain_test",
27
+ },
28
+ ],
29
+ conditions: [
30
+ {
31
+ __typename: "Condition",
32
+ field: "request.foo",
33
+ operator: "neq",
34
+ value: "bar",
35
+ },
36
+ ],
37
+ comment: "comment",
38
+ },
39
+ };
40
+
41
+ const approvalRuleMock = {
42
+ request: { query: GRANT_APPROVAL_RULE_QUERY, variables: { id: 123 } },
43
+ result: { data: grantApprovalRule },
44
+ };
45
+
46
+ const renderOpts = {
47
+ mocks: [
48
+ approvalRuleMock,
49
+ domainsMock({ action: "approveGrantRequests" }),
50
+ rolesMock({ domainId: "1", permission: "approve_grant_request" }),
51
+ ],
52
+ messages: {
53
+ en: {
54
+ ...en,
55
+ "domain.selector.label": "Domain",
56
+ "domain.selector.placeholder": "Domain",
57
+ "actions.save": "Save",
58
+ "actions.cancel": "Cancel",
59
+ },
60
+ },
61
+ fallback: "lazy",
62
+ };
63
+
64
+ describe("<GrantApprovalRuleEdit />", () => {
65
+ it("matches the last snapshot", async () => {
66
+ const { container, queryByText } = render(
67
+ <GrantApprovalRuleEdit />,
68
+ renderOpts
69
+ );
70
+ await waitFor(() => expect(queryByText(/lazy/i)).not.toBeInTheDocument());
71
+ await waitFor(() =>
72
+ expect(queryByText(/loading/i)).not.toBeInTheDocument()
73
+ );
74
+ expect(container).toMatchSnapshot();
75
+ });
76
+ });
@@ -0,0 +1,135 @@
1
+ import React from "react";
2
+ import { render } from "@truedat/test/render";
3
+ import { waitFor } from "@testing-library/react";
4
+ import userEvent from "@testing-library/user-event";
5
+ import { domainsMock, rolesMock } from "@truedat/test/mocks";
6
+ import GrantApprovalRuleForm from "../GrantApprovalRuleForm";
7
+ import en from "../../messages/en";
8
+
9
+ const renderOpts = {
10
+ mocks: [
11
+ domainsMock({ action: "approveGrantRequests" }),
12
+ rolesMock({ domainId: "1", permission: "approve_grant_request" }),
13
+ ],
14
+ messages: {
15
+ en: {
16
+ ...en,
17
+ "domain.selector.label": "Domain",
18
+ "domain.selector.placeholder": "Domain",
19
+ "actions.save": "Save",
20
+ "actions.cancel": "Cancel",
21
+ },
22
+ },
23
+ fallback: "lazy",
24
+ };
25
+ const approvalRule = {
26
+ __typename: "ApprovalRule",
27
+ id: "123",
28
+ name: "rule_name",
29
+ action: "approve",
30
+ role: "grants",
31
+ domainIds: ["1"],
32
+ userId: "1",
33
+ domains: [
34
+ {
35
+ __typename: "Domain",
36
+ name: "domain_test",
37
+ },
38
+ ],
39
+ conditions: [
40
+ {
41
+ __typename: "Condition",
42
+ field: "request.foo",
43
+ operator: "neq",
44
+ value: "bar",
45
+ },
46
+ {
47
+ __typename: "Condition",
48
+ field: "invalid_condition",
49
+ operator: "neq",
50
+ value: "bar",
51
+ },
52
+ ],
53
+ comment: "comment",
54
+ };
55
+
56
+ describe("<GrantApprovalRuleForm />", () => {
57
+ it("matches the last snapshot", async () => {
58
+ const props = {
59
+ onSubmit: jest.fn(),
60
+ approvalRule,
61
+ };
62
+ const { container, findByRole } = render(
63
+ <GrantApprovalRuleForm {...props} />,
64
+ renderOpts
65
+ );
66
+ await findByRole("button", { name: /save/i });
67
+ expect(container).toMatchSnapshot();
68
+ });
69
+
70
+ it("matches the last snapshot for new rule", async () => {
71
+ const props = { onSubmit: jest.fn() };
72
+ const { container, findByRole } = render(
73
+ <GrantApprovalRuleForm {...props} />,
74
+ renderOpts
75
+ );
76
+ await findByRole("button", { name: /save/i });
77
+ expect(container).toMatchSnapshot();
78
+ });
79
+
80
+ it("call onSubmit with data", async () => {
81
+ const onSubmit = jest.fn();
82
+ const props = { onSubmit };
83
+ const { getByRole, findByText, debug } = render(
84
+ <GrantApprovalRuleForm {...props} />,
85
+ renderOpts
86
+ );
87
+ await waitFor(() =>
88
+ expect(getByRole("button", { name: /save/i })).toBeDisabled()
89
+ );
90
+
91
+ userEvent.type(getByRole("textbox", { name: /name/i }), "rule_name");
92
+
93
+ userEvent.click(await findByText(/fooDomain/));
94
+
95
+ await waitFor(() =>
96
+ expect(getByRole("textbox", { name: /comment/i })).not.toBeDisabled()
97
+ );
98
+
99
+ userEvent.click(await findByText(/role1/i));
100
+ userEvent.click(await findByText(/reject/i));
101
+
102
+ userEvent.type(getByRole("textbox", { name: /comment/i }), "rule_comment");
103
+
104
+ userEvent.click(await findByText(/request metadata/i));
105
+ userEvent.type(getByRole("textbox", { name: /field/i }), "field");
106
+ userEvent.click(await findByText(/is equal/i));
107
+ userEvent.type(getByRole("textbox", { name: /value/i }), "value");
108
+
109
+ await waitFor(() =>
110
+ expect(getByRole("button", { name: /save/i })).not.toBeDisabled()
111
+ );
112
+
113
+ userEvent.click(getByRole("button", { name: /save/i }));
114
+
115
+ await waitFor(() => {
116
+ expect(onSubmit.mock.calls.length).toBe(1);
117
+ });
118
+ expect(onSubmit.mock.calls[0][0]).toMatchObject({
119
+ approvalRule: {
120
+ action: "reject",
121
+ comment: "rule_comment",
122
+ conditions: [
123
+ {
124
+ field: "request.field",
125
+ operator: "eq",
126
+ value: "value",
127
+ },
128
+ ],
129
+ domainIds: ["1"],
130
+ name: "rule_name",
131
+ role: "role1",
132
+ },
133
+ });
134
+ });
135
+ });
@@ -0,0 +1,40 @@
1
+ import React from "react";
2
+ import { render } from "@truedat/test/render";
3
+ import { waitFor } from "@testing-library/react";
4
+ import { domainsMock, rolesMock } from "@truedat/test/mocks";
5
+ import { GrantApprovalRuleNew } from "../GrantApprovalRuleNew";
6
+ import en from "../../messages/en";
7
+
8
+ const createGrantApprovalRule = jest.fn();
9
+
10
+ const renderOpts = {
11
+ mocks: [
12
+ domainsMock({ action: "approveGrantRequests" }),
13
+ rolesMock({ domainId: "1", permission: "approve_grant_request" }),
14
+ ],
15
+ messages: {
16
+ en: {
17
+ ...en,
18
+ "domain.selector.label": "Domain",
19
+ "domain.selector.placeholder": "Domain",
20
+ "actions.save": "Save",
21
+ "actions.cancel": "Cancel",
22
+ },
23
+ },
24
+ fallback: "lazy",
25
+ };
26
+
27
+ describe("<GrantApprovalRuleNew />", () => {
28
+ it("matches the last snapshot", async () => {
29
+ const props = { createGrantApprovalRule };
30
+ const { container, queryByText } = render(
31
+ <GrantApprovalRuleNew {...props} />,
32
+ renderOpts
33
+ );
34
+ await waitFor(() => expect(queryByText(/lazy/i)).not.toBeInTheDocument());
35
+ await waitFor(() =>
36
+ expect(queryByText(/loading/i)).not.toBeInTheDocument()
37
+ );
38
+ expect(container).toMatchSnapshot();
39
+ });
40
+ });
@@ -0,0 +1,107 @@
1
+ import React from "react";
2
+ import { render } from "@truedat/test/render";
3
+ import userEvent from "@testing-library/user-event";
4
+ import { waitFor } from "@testing-library/react";
5
+ import { linkTo } from "@truedat/core/routes";
6
+ import GrantApprovalRules from "../GrantApprovalRules";
7
+ import { GRANT_APPROVAL_RULES_QUERY } from "../../api/queries";
8
+ import en from "../../messages/en";
9
+
10
+ const mockHistory = {
11
+ push: jest.fn(),
12
+ };
13
+
14
+ jest.mock("react-router-dom", () => ({
15
+ ...jest.requireActual("react-router-dom"),
16
+ useHistory: () => mockHistory,
17
+ }));
18
+
19
+ const grantApprovalRules = {
20
+ grantApprovalRules: [
21
+ {
22
+ __typename: "ApprovalRule",
23
+ id: "123",
24
+ name: "rule_name",
25
+ action: "approve",
26
+ role: "grants",
27
+ domains: [
28
+ {
29
+ __typename: "Domain",
30
+ name: "domain_test",
31
+ },
32
+ ],
33
+ },
34
+ {
35
+ __typename: "ApprovalRule",
36
+ id: "456",
37
+ name: "reject rule",
38
+ action: "reject",
39
+ role: "grants",
40
+ domains: [
41
+ {
42
+ __typename: "Domain",
43
+ name: "domain_test",
44
+ },
45
+ ],
46
+ },
47
+ ],
48
+ };
49
+
50
+ const emptyGrantApprovalRules = {
51
+ grantApprovalRules: [],
52
+ };
53
+
54
+ const approvalRulesMock = (data) => ({
55
+ request: { query: GRANT_APPROVAL_RULES_QUERY },
56
+ result: { data },
57
+ });
58
+
59
+ const renderOpts = (data) => ({
60
+ mocks: [approvalRulesMock(data)],
61
+ messages: { en },
62
+ fallback: "lazy",
63
+ });
64
+
65
+ describe("<GrantApprovalRules />", () => {
66
+ it("matches the last snapshot", async () => {
67
+ const { container, queryByText } = render(
68
+ <GrantApprovalRules />,
69
+ renderOpts(grantApprovalRules)
70
+ );
71
+ await waitFor(() => expect(queryByText(/lazy/i)).not.toBeInTheDocument());
72
+ expect(container).toMatchSnapshot();
73
+ await waitFor(() =>
74
+ expect(queryByText(/loading/i)).not.toBeInTheDocument()
75
+ );
76
+ expect(container).toMatchSnapshot();
77
+ });
78
+
79
+ it("matches snapshot for empty data", async () => {
80
+ const { container, queryByText } = render(
81
+ <GrantApprovalRules />,
82
+ renderOpts(emptyGrantApprovalRules)
83
+ );
84
+ await waitFor(() => expect(queryByText(/lazy/i)).not.toBeInTheDocument());
85
+ expect(container).toMatchSnapshot();
86
+ await waitFor(() =>
87
+ expect(queryByText(/loading/i)).not.toBeInTheDocument()
88
+ );
89
+ expect(container).toMatchSnapshot();
90
+ });
91
+
92
+ it("navigates to ApprovalRule detail on click", async () => {
93
+ const { queryByText } = render(
94
+ <GrantApprovalRules />,
95
+ renderOpts(grantApprovalRules)
96
+ );
97
+ await waitFor(() => expect(queryByText(/lazy/i)).not.toBeInTheDocument());
98
+ await waitFor(() =>
99
+ expect(queryByText(/loading/i)).not.toBeInTheDocument()
100
+ );
101
+
102
+ userEvent.click(queryByText(/rule_name/i));
103
+ expect(mockHistory.push).toHaveBeenCalledWith(
104
+ linkTo.GRANT_APPROVAL_RULE({ id: 123 })
105
+ );
106
+ });
107
+ });
@@ -143,6 +143,6 @@ describe("<StructureGrantDropdown />", () => {
143
143
  await findByText(en["grant.actions.view_request.confirmation.header"])
144
144
  );
145
145
 
146
- expect(mockHistory.push).toHaveBeenCalledWith("/grant_requests/8");
146
+ expect(mockHistory.push).toHaveBeenCalledWith("/grantRequests/8");
147
147
  });
148
148
  });
@@ -0,0 +1,239 @@
1
+ // Jest Snapshot v1, https://goo.gl/fbAQLP
2
+
3
+ exports[`<GrantApprovalRule /> matches the last snapshot 1`] = `
4
+ <div>
5
+ <div
6
+ class="ui breadcrumb"
7
+ >
8
+ <a
9
+ class="section"
10
+ >
11
+ Approval rules
12
+ </a>
13
+ <i
14
+ aria-hidden="true"
15
+ class="right angle icon divider"
16
+ />
17
+ <div
18
+ class="active section"
19
+ >
20
+ rule_name
21
+ </div>
22
+ </div>
23
+ <div
24
+ class="ui segment"
25
+ >
26
+ <div
27
+ class="ui grid"
28
+ >
29
+ <div
30
+ class="eight wide column"
31
+ >
32
+ <h2
33
+ class="ui header"
34
+ >
35
+ <i
36
+ aria-hidden="true"
37
+ class="key circular icon"
38
+ />
39
+ <div
40
+ class="content"
41
+ >
42
+ Grant approval rule
43
+ </div>
44
+ </h2>
45
+ </div>
46
+ <div
47
+ class="right aligned eight wide column"
48
+ >
49
+ <a
50
+ class="ui primary button"
51
+ href="/grantApproveRules/123/edit"
52
+ role="button"
53
+ >
54
+ Edit
55
+ </a>
56
+ <button
57
+ class="ui button"
58
+ >
59
+ Delete
60
+ </button>
61
+ </div>
62
+ </div>
63
+ <div
64
+ class="ui big relaxed list"
65
+ role="list"
66
+ >
67
+ <div
68
+ class="item"
69
+ role="listitem"
70
+ >
71
+ <div
72
+ class="header"
73
+ >
74
+ Name
75
+ </div>
76
+ <div
77
+ class="content"
78
+ >
79
+ rule_name
80
+ </div>
81
+ </div>
82
+ <div
83
+ class="item"
84
+ role="listitem"
85
+ >
86
+ <div
87
+ class="header"
88
+ >
89
+ Domain
90
+ </div>
91
+ <div
92
+ class="content"
93
+ >
94
+ domain_test
95
+ </div>
96
+ </div>
97
+ <div
98
+ class="item"
99
+ role="listitem"
100
+ >
101
+ <div
102
+ class="header"
103
+ >
104
+ Role
105
+ </div>
106
+ <div
107
+ class="content"
108
+ >
109
+ grants
110
+ </div>
111
+ </div>
112
+ <div
113
+ class="item"
114
+ role="listitem"
115
+ >
116
+ <div
117
+ class="header"
118
+ >
119
+ Action
120
+ </div>
121
+ <div
122
+ class="content"
123
+ >
124
+ Approve
125
+ </div>
126
+ </div>
127
+ <div
128
+ class="item"
129
+ role="listitem"
130
+ >
131
+ <div
132
+ class="header"
133
+ >
134
+ Comment
135
+ </div>
136
+ <div
137
+ class="content"
138
+ >
139
+ comment invalid
140
+ </div>
141
+ </div>
142
+ <div
143
+ class="item"
144
+ role="listitem"
145
+ >
146
+ <div
147
+ class="header"
148
+ >
149
+ Conditions
150
+ </div>
151
+ <table
152
+ class="ui celled collapsing compact table"
153
+ >
154
+ <thead
155
+ class=""
156
+ >
157
+ <tr
158
+ class="center aligned"
159
+ >
160
+ <th
161
+ class=""
162
+ >
163
+ Context
164
+ </th>
165
+ <th
166
+ class=""
167
+ >
168
+ Field
169
+ </th>
170
+ <th
171
+ class=""
172
+ >
173
+ Operator
174
+ </th>
175
+ <th
176
+ class=""
177
+ >
178
+ Value
179
+ </th>
180
+ </tr>
181
+ </thead>
182
+ <tbody
183
+ class=""
184
+ >
185
+ <tr
186
+ class="center aligned"
187
+ >
188
+ <td
189
+ class=""
190
+ >
191
+ Request metadata
192
+ </td>
193
+ <td
194
+ class=""
195
+ >
196
+ foo
197
+ </td>
198
+ <td
199
+ class=""
200
+ >
201
+ Is not equal
202
+ </td>
203
+ <td
204
+ class=""
205
+ >
206
+ bar
207
+ </td>
208
+ </tr>
209
+ <tr
210
+ class="center aligned"
211
+ >
212
+ <td
213
+ class=""
214
+ >
215
+ -
216
+ </td>
217
+ <td
218
+ class=""
219
+ >
220
+ invalid_field
221
+ </td>
222
+ <td
223
+ class=""
224
+ >
225
+ Is equal
226
+ </td>
227
+ <td
228
+ class=""
229
+ >
230
+ baz
231
+ </td>
232
+ </tr>
233
+ </tbody>
234
+ </table>
235
+ </div>
236
+ </div>
237
+ </div>
238
+ </div>
239
+ `;