@truedat/dq 4.44.4 → 4.45.1

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 (29) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/package.json +24 -17
  3. package/src/components/NewRuleImplementation.js +6 -29
  4. package/src/components/RemediationForm.js +20 -47
  5. package/src/components/RemediationPlan.js +1 -1
  6. package/src/components/RuleFilters.js +1 -5
  7. package/src/components/RuleForm.js +20 -59
  8. package/src/components/RuleImplementationFilters.js +1 -5
  9. package/src/components/__tests__/ExecutionForm.spec.js +14 -12
  10. package/src/components/__tests__/NewRuleImplementation.spec.js +27 -15
  11. package/src/components/__tests__/RemediationForm.spec.js +11 -46
  12. package/src/components/__tests__/RemediationPlan.spec.js +49 -73
  13. package/src/components/__tests__/RuleForm.spec.js +22 -52
  14. package/src/components/__tests__/RuleProperties.spec.js +39 -27
  15. package/src/components/__tests__/__snapshots__/ExecutionForm.spec.js.snap +75 -5
  16. package/src/components/__tests__/__snapshots__/NewRuleImplementation.spec.js.snap +202 -2
  17. package/src/components/__tests__/__snapshots__/RemediationForm.spec.js.snap +77 -3
  18. package/src/components/__tests__/__snapshots__/RemediationPlan.spec.js.snap +40 -1
  19. package/src/components/__tests__/__snapshots__/RuleForm.spec.js.snap +156 -6
  20. package/src/components/__tests__/__snapshots__/RuleImplementationsSearch.spec.js.snap +1 -1
  21. package/src/components/__tests__/__snapshots__/RuleProperties.spec.js.snap +68 -56
  22. package/src/components/__tests__/__snapshots__/RuleSearch.spec.js.snap +1 -1
  23. package/src/components/ruleImplementationForm/InformationForm.js +14 -33
  24. package/src/components/ruleImplementationForm/RuleImplementationForm.js +7 -11
  25. package/src/components/ruleImplementationForm/RuleImplementationRawForm.js +20 -39
  26. package/src/components/ruleImplementationForm/__tests__/RuleImplementationForm.spec.js +11 -7
  27. package/src/components/ruleImplementationForm/__tests__/RuleImplementationRawForm.spec.js +175 -156
  28. package/src/components/ruleImplementationForm/__tests__/__snapshots__/RuleImplementationForm.spec.js.snap +75 -0
  29. package/src/components/ruleImplementationForm/__tests__/__snapshots__/RuleImplementationRawForm.spec.js.snap +412 -165
@@ -1,27 +1,43 @@
1
1
  import _ from "lodash/fp";
2
2
  import React from "react";
3
- import { shallow } from "enzyme";
4
- import { intl } from "@truedat/test/intl-stub";
5
- import { FormattedMessage } from "react-intl";
6
- import { RuleImplementationRawForm } from "../RuleImplementationRawForm";
3
+ import { waitFor } from "@testing-library/react";
4
+ import userEvent from "@testing-library/user-event";
5
+ import { render } from "@truedat/test/render";
6
+ import { multipleTemplatesMock } from "@truedat/test/mocks";
7
+ import RuleImplementationRawForm from "../RuleImplementationRawForm";
7
8
 
8
- // workaround for enzyme issue with React.useContext
9
- // see https://github.com/airbnb/enzyme/issues/2176#issuecomment-532361526
10
- jest.spyOn(React, "useContext").mockImplementation(() => intl);
11
- jest.mock("react-router-dom", () => ({
12
- ...jest.requireActual("react-router-dom"),
13
- useHistory: () => ({ push: jest.fn() }),
14
- }));
9
+ const renderOpts = {
10
+ mocks: [multipleTemplatesMock({ scope: "ri", domainIds: [1] })],
11
+ state: {
12
+ rule: { domain_id: 1 },
13
+ ruleImplementationCreating: false,
14
+ },
15
+ fallback: "lazy",
16
+ };
15
17
 
16
- describe("<RuleImplementationRawForm />", () => {
17
- const onSubmit = jest.fn();
18
- const onChange = jest.fn();
19
- const isSubmitting = false;
20
- const sources = [
18
+ const props = {
19
+ onChange: jest.fn(),
20
+ onSubmit: jest.fn(),
21
+ implementationKey: "",
22
+ isSubmitting: false,
23
+ rawContent: {
24
+ dataset: "",
25
+ source_id: 1,
26
+ population: "",
27
+ validations: "",
28
+ },
29
+ ruleImplementation: {
30
+ id: 1,
31
+ executable: true,
32
+ goal: "10",
33
+ minimum: "1",
34
+ result_type: "percentage",
35
+ },
36
+ sources: [
21
37
  {
22
38
  id: "1",
23
39
  config: { alias: "source1", job_types: ["quality"] },
24
- external_id: "ext_id_1",
40
+ externalId: "ext_id_1",
25
41
  },
26
42
  {
27
43
  id: "2",
@@ -30,53 +46,31 @@ describe("<RuleImplementationRawForm />", () => {
30
46
  job_types: ["quality"],
31
47
  databases: ["db1", "db2"],
32
48
  },
33
- external_id: "ext_id_2",
34
- },
35
- { id: "3", config: {}, external_id: "ext_id_3" },
36
- ];
37
- const setImplementationRawContent = jest.fn();
38
-
39
- const props = {
40
- implementationKey: "",
41
- setImplementationKey: jest.fn(),
42
- ruleImplementation: {
43
- id: 1,
44
- executable: true,
45
- goal: "10",
46
- minimum: "1",
47
- result_type: "percentage",
48
- },
49
- rawContent: {
50
- dataset: "",
51
- source_id: 1,
52
- population: "",
53
- validations: "",
49
+ externalId: "ext_id_2",
54
50
  },
55
- setImplementationRawContent: setImplementationRawContent,
56
- onSubmit,
57
- isSubmitting,
58
- sources,
59
- onChange,
60
- };
51
+ { id: "3", config: {}, externalId: "ext_id_3" },
52
+ ],
53
+ };
61
54
 
62
- it("matches the latest snapshot", () => {
63
- const wrapper = shallow(<RuleImplementationRawForm {...props} />);
64
- expect(wrapper).toMatchSnapshot();
55
+ describe("<RuleImplementationRawForm />", () => {
56
+ it("matches the latest snapshot", async () => {
57
+ const { container, queryByText } = render(
58
+ <RuleImplementationRawForm {...props} />,
59
+ renderOpts
60
+ );
61
+ await waitFor(() =>
62
+ expect(queryByText(/loading/i)).not.toBeInTheDocument()
63
+ );
64
+ await waitFor(() => expect(queryByText(/lazy/i)).not.toBeInTheDocument());
65
+ expect(container).toMatchSnapshot();
65
66
  });
66
67
 
67
68
  it("renders sources", () => {
68
- const customProps = {
69
- ...props,
70
- rawContent: {
71
- dataset: "cliente c join address a on c.address_id=a.id",
72
- population: "",
73
- validations: "a.city='MADRID'",
74
- },
75
- };
76
-
77
- const wrapper = shallow(<RuleImplementationRawForm {...customProps} />);
78
- const sourcesDropdown = wrapper.find("FormDropdown[name='source_id']");
79
- expect(sourcesDropdown).toHaveLength(1);
69
+ const { queryByRole } = render(
70
+ <RuleImplementationRawForm {...props} />,
71
+ renderOpts
72
+ );
73
+ expect(queryByRole("option", { name: "ext_id_1" })).toBeInTheDocument();
80
74
  });
81
75
 
82
76
  it("renders databases dropdown if selected source has databases", () => {
@@ -90,9 +84,11 @@ describe("<RuleImplementationRawForm />", () => {
90
84
  },
91
85
  };
92
86
 
93
- const wrapper = shallow(<RuleImplementationRawForm {...customProps} />);
94
- const databaseDropdown = wrapper.find("FormDropdown[name='database']");
95
- expect(databaseDropdown).toHaveLength(1);
87
+ const { queryByRole } = render(
88
+ <RuleImplementationRawForm {...customProps} />,
89
+ renderOpts
90
+ );
91
+ expect(queryByRole("option", { name: "db1" })).toBeInTheDocument();
96
92
  });
97
93
 
98
94
  it("dont renders databases dropdown if selected source doesnt have databases", () => {
@@ -106,15 +102,21 @@ describe("<RuleImplementationRawForm />", () => {
106
102
  },
107
103
  };
108
104
 
109
- const wrapper = shallow(<RuleImplementationRawForm {...customProps} />);
110
- const databaseDropdown = wrapper.find("FormDropdown[name='database']");
111
- expect(databaseDropdown).toHaveLength(0);
105
+ const { queryByRole } = render(
106
+ <RuleImplementationRawForm {...customProps} />,
107
+ renderOpts
108
+ );
109
+ expect(queryByRole("option", { name: "db1" })).not.toBeInTheDocument();
112
110
  });
113
111
 
114
- it("submit button enabled if there is valid content in form with database without rule", () => {
115
- const domainProps = _.set("ruleImplementation.domain_id", 5)(props);
112
+ it("submit button enabled if there is valid content in form with database without rule", async () => {
113
+ const updatedProps = _.flow(
114
+ _.set("ruleImplementation.domain_id", 1),
115
+ _.set("ruleImplementation.dfName", "template1"),
116
+ _.set("ruleImplementation.dfContent", { field1: "foo" })
117
+ )(props);
116
118
  const customProps = {
117
- ...domainProps,
119
+ ...updatedProps,
118
120
  rawContent: {
119
121
  dataset: "address a on c.address_id=a.id",
120
122
  source_id: 2,
@@ -122,17 +124,24 @@ describe("<RuleImplementationRawForm />", () => {
122
124
  population: "",
123
125
  validations: "a.city='MADRID'",
124
126
  },
125
- template: { id: 1 },
126
- dfContent: {},
127
127
  };
128
- const wrapper = shallow(<RuleImplementationRawForm {...customProps} />);
129
- const submitButton = wrapper.find("FormButton[type='submit']");
130
- expect(submitButton.props().disabled).toBe(false);
128
+
129
+ const { getByRole } = render(
130
+ <RuleImplementationRawForm {...customProps} />,
131
+ renderOpts
132
+ );
133
+ await waitFor(() => {
134
+ expect(getByRole("button", { name: "Save" })).toBeEnabled();
135
+ });
131
136
  });
132
137
 
133
- it("submit button enabled if there is valid content in form with database", () => {
138
+ it("submit button enabled if there is valid content in form with database", async () => {
139
+ const updatedProps = _.flow(
140
+ _.set("ruleImplementation.dfName", "template1"),
141
+ _.set("ruleImplementation.dfContent", { field1: "foo" })
142
+ )(props);
134
143
  const customProps = {
135
- ...props,
144
+ ...updatedProps,
136
145
  rawContent: {
137
146
  dataset: "address a on c.address_id=a.id",
138
147
  source_id: 2,
@@ -140,16 +149,19 @@ describe("<RuleImplementationRawForm />", () => {
140
149
  population: "",
141
150
  validations: "a.city='MADRID'",
142
151
  },
143
- template: { id: 1 },
144
- dfContent: {},
145
152
  rule: { id: 5, name: "regla" },
146
153
  };
147
- const wrapper = shallow(<RuleImplementationRawForm {...customProps} />);
148
- const submitButton = wrapper.find("FormButton[type='submit']");
149
- expect(submitButton.props().disabled).toBe(false);
154
+
155
+ const { queryByRole } = render(
156
+ <RuleImplementationRawForm {...customProps} />,
157
+ renderOpts
158
+ );
159
+ await waitFor(() =>
160
+ expect(queryByRole("button", { name: /save/i })).toBeEnabled()
161
+ );
150
162
  });
151
163
 
152
- it("submit button disabled if databases are available and not selected", () => {
164
+ it("submit button disabled if databases are available and not selected", async () => {
153
165
  const customProps = {
154
166
  ...props,
155
167
  rawContent: {
@@ -159,96 +171,109 @@ describe("<RuleImplementationRawForm />", () => {
159
171
  validations: "a.city='MADRID'",
160
172
  },
161
173
  };
162
- const wrapper = shallow(<RuleImplementationRawForm {...customProps} />);
163
- const submitButton = wrapper.find("FormButton[type='submit']");
164
- expect(submitButton.props().disabled).toBe(true);
174
+
175
+ const { queryByText, getByRole } = render(
176
+ <RuleImplementationRawForm {...customProps} />,
177
+ renderOpts
178
+ );
179
+ await waitFor(() =>
180
+ expect(queryByText(/loading/i)).not.toBeInTheDocument()
181
+ );
182
+ await waitFor(() => expect(queryByText(/lazy/i)).not.toBeInTheDocument());
183
+ expect(getByRole("button", { name: "Save" })).toBeDisabled();
165
184
  });
166
185
 
167
- it("calls setImplementationRawContent when filling raw content input fields", () => {
168
- const wrapper = shallow(<RuleImplementationRawForm {...props} />);
169
- expect(setImplementationRawContent.mock.calls).toHaveLength(0);
170
- wrapper.find("FormTextArea").at(0).simulate("change", null, {
171
- value: "cliente c join address a on c.address_id=a.id",
186
+ it("calls setImplementationRawContent when filling raw content input fields", async () => {
187
+ const setImplementationRawContent = jest.fn();
188
+ const { findByLabelText } = render(
189
+ <RuleImplementationRawForm
190
+ setImplementationRawContent={setImplementationRawContent}
191
+ {...props}
192
+ />,
193
+ renderOpts
194
+ );
195
+ userEvent.type(await findByLabelText("Dataset"), "A");
196
+ expect(setImplementationRawContent).toBeCalledWith({
197
+ dataset: "A",
198
+ population: "",
199
+ source_id: 1,
200
+ validations: "",
172
201
  });
173
- expect(setImplementationRawContent.mock.calls).toHaveLength(1);
174
202
  });
175
203
 
176
204
  it("calls setImplementationRawContent when selecting source", () => {
177
205
  const setImplementationRawContent = jest.fn();
178
- const customProps = {
179
- ...props,
180
- setImplementationRawContent,
181
- };
182
- const wrapper = shallow(<RuleImplementationRawForm {...customProps} />);
183
-
184
- expect(setImplementationRawContent.mock.calls).toHaveLength(0);
185
- wrapper
186
- .find("FormDropdown[name='source_id']")
187
- .at(0)
188
- .simulate("change", null, {
189
- value: 2,
190
- });
191
- expect(setImplementationRawContent.mock.calls).toHaveLength(1);
192
- expect(setImplementationRawContent).toHaveBeenCalledWith({
206
+ const { queryByRole } = render(
207
+ <RuleImplementationRawForm
208
+ setImplementationRawContent={setImplementationRawContent}
209
+ {...props}
210
+ />,
211
+ renderOpts
212
+ );
213
+ userEvent.click(queryByRole("option", { name: "ext_id_2" }));
214
+ expect(setImplementationRawContent).toBeCalledWith({
193
215
  database: "",
194
216
  dataset: "",
195
217
  population: "",
196
- source_id: 2,
218
+ source_id: "2",
197
219
  validations: "",
198
220
  });
199
221
  });
200
222
 
201
- it("submit button enabled if there is valid content in form without rule", () => {
202
- const domainProps = _.set("ruleImplementation.domain_id", 5)(props);
203
- const customProps = {
204
- ...domainProps,
205
- rawContent: {
206
- dataset: "cliente c join address a on c.address_id=a.id",
207
- source_id: 1,
208
- population: "",
209
- validations: "a.city='MADRID'",
210
- },
211
- template: { id: 1 },
212
- dfContent: {},
213
- };
214
- const wrapper = shallow(<RuleImplementationRawForm {...customProps} />);
215
- const submitButton = wrapper.find("FormButton[type='submit']");
216
- expect(submitButton.props().disabled).toBe(false);
217
- });
218
-
219
- it("submit button enabled if there is valid content in form", () => {
223
+ it("submit button enabled if there is valid content in form without rule", async () => {
224
+ const updatedProps = _.flow(
225
+ _.set("ruleImplementation.domain_id", 1),
226
+ _.set("ruleImplementation.dfName", "template1"),
227
+ _.set("ruleImplementation.dfContent", { field1: "foo" })
228
+ )(props);
220
229
  const customProps = {
221
- ...props,
230
+ ...updatedProps,
222
231
  rawContent: {
223
232
  dataset: "cliente c join address a on c.address_id=a.id",
224
233
  source_id: 1,
225
234
  population: "",
226
235
  validations: "a.city='MADRID'",
227
236
  },
228
- template: { id: 1 },
229
- dfContent: {},
230
- rule: { id: 5, name: "regla" },
231
237
  };
232
- const wrapper = shallow(<RuleImplementationRawForm {...customProps} />);
233
- const submitButton = wrapper.find("FormButton[type='submit']");
234
- expect(submitButton.props().disabled).toBe(false);
238
+ const { queryByRole, queryByText } = render(
239
+ <RuleImplementationRawForm {...customProps} />,
240
+ renderOpts
241
+ );
242
+ await waitFor(() =>
243
+ expect(queryByText(/loading/i)).not.toBeInTheDocument()
244
+ );
245
+ await waitFor(() => expect(queryByText(/lazy/i)).not.toBeInTheDocument());
246
+ await waitFor(() =>
247
+ expect(queryByRole("button", { name: /save/i })).toBeEnabled()
248
+ );
235
249
  });
236
250
 
237
- it("submit button disabled if there is invalid content in form", () => {
251
+ it("submit button disabled if there is invalid content in form", async () => {
252
+ const updatedProps = _.flow(
253
+ _.set("ruleImplementation.domain_id", 1),
254
+ _.set("ruleImplementation.dfName", "template1"),
255
+ _.set("ruleImplementation.dfContent", {})
256
+ )(props);
238
257
  const customProps = {
239
- ...props,
258
+ ...updatedProps,
240
259
  rawContent: {
241
260
  dataset: "insert into cliente c join address a on c.address_id=a.id",
242
261
  population: "",
243
262
  validations: "a.city='MADRID'",
244
263
  },
245
264
  };
246
- const wrapper = shallow(<RuleImplementationRawForm {...customProps} />);
247
- const submitButton = wrapper.find("FormButton[type='submit']");
248
- expect(submitButton.props().disabled).toBe(true);
265
+ const { queryByRole, queryByText } = render(
266
+ <RuleImplementationRawForm {...customProps} />,
267
+ renderOpts
268
+ );
269
+ await waitFor(() =>
270
+ expect(queryByText(/loading/i)).not.toBeInTheDocument()
271
+ );
272
+ await waitFor(() => expect(queryByText(/lazy/i)).not.toBeInTheDocument());
273
+ expect(queryByRole("button", { name: /save/i })).toBeDisabled();
249
274
  });
250
275
 
251
- it("input errors if invalid content in dataset field", () => {
276
+ it("input errors if invalid content in dataset field", async () => {
252
277
  const customProps = {
253
278
  ...props,
254
279
  rawContent: {
@@ -258,26 +283,20 @@ describe("<RuleImplementationRawForm />", () => {
258
283
  validations: "a.city='MADRID'",
259
284
  },
260
285
  };
261
- const wrapper = shallow(<RuleImplementationRawForm {...customProps} />);
262
- const datasetTextArea = wrapper.find("FormTextArea").at(0);
263
- expect(datasetTextArea.props().error).toEqual({
264
- content: (
265
- <FormattedMessage
266
- id="ruleImplementationRawForm.rawText.error"
267
- values={{ invalid_content: <i>insert</i> }}
268
- />
269
- ),
270
- });
286
+ const { queryByText } = render(
287
+ <RuleImplementationRawForm {...customProps} />,
288
+ renderOpts
289
+ );
290
+ expect(queryByText(/entered content not allowed.*/i)).toBeInTheDocument();
271
291
  });
272
292
 
273
- it("handles executable field", () => {
274
- const wrapper = shallow(<RuleImplementationRawForm {...props} />);
275
- const radio = wrapper.find({ name: "executable" }).at(0);
276
-
277
- radio.simulate("change", null, {
278
- checked: false,
279
- });
280
-
293
+ it("handles executable field", async () => {
294
+ const onChange = jest.fn();
295
+ const { findByText } = render(
296
+ <RuleImplementationRawForm {...props} onChange={onChange} />,
297
+ renderOpts
298
+ );
299
+ userEvent.click(await findByText("Executable"));
281
300
  expect(onChange).toHaveBeenCalledWith("executable", false);
282
301
  });
283
302
  });
@@ -351,6 +351,81 @@ exports[`<RuleImplementationForm /> matches the latest snapshot 1`] = `
351
351
  </div>
352
352
  </div>
353
353
  </div>
354
+ <div
355
+ class="required field"
356
+ >
357
+ <label>
358
+ Template
359
+ <div
360
+ class="ui left pointing label"
361
+ >
362
+ Empty required field
363
+ </div>
364
+ </label>
365
+ <div
366
+ class="field"
367
+ >
368
+ <div
369
+ aria-busy="false"
370
+ aria-expanded="false"
371
+ class="ui search selection dropdown"
372
+ name="template"
373
+ role="combobox"
374
+ >
375
+ <input
376
+ aria-autocomplete="list"
377
+ autocomplete="off"
378
+ class="search"
379
+ tabindex="0"
380
+ type="text"
381
+ value=""
382
+ />
383
+ <div
384
+ aria-atomic="true"
385
+ aria-live="polite"
386
+ class="divider default text"
387
+ role="alert"
388
+ >
389
+ Select a template...
390
+ </div>
391
+ <i
392
+ aria-hidden="true"
393
+ class="dropdown icon"
394
+ />
395
+ <div
396
+ class="menu transition"
397
+ role="listbox"
398
+ >
399
+ <div
400
+ aria-checked="false"
401
+ aria-selected="true"
402
+ class="selected item"
403
+ role="option"
404
+ style="pointer-events: all;"
405
+ >
406
+ <span
407
+ class="text"
408
+ >
409
+ template1
410
+ </span>
411
+ </div>
412
+ <div
413
+ aria-checked="false"
414
+ aria-selected="false"
415
+ class="item"
416
+ role="option"
417
+ style="pointer-events: all;"
418
+ >
419
+ <span
420
+ class="text"
421
+ >
422
+ template2
423
+ </span>
424
+ </div>
425
+ </div>
426
+ </div>
427
+ </div>
428
+ </div>
354
429
  </form>
355
430
  </div>
356
431
  <div