@truedat/dq 4.53.9 → 4.53.11

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 (57) hide show
  1. package/CHANGELOG.md +7 -1
  2. package/package.json +6 -6
  3. package/src/api/queries.js +76 -16
  4. package/src/components/ConceptRules.js +1 -1
  5. package/src/components/ConditionSummary.js +1 -1
  6. package/src/components/ExecutionGroups.js +4 -50
  7. package/src/components/ImplementationExecutionFilters.js +79 -0
  8. package/src/components/ImplementationExecutions.js +125 -0
  9. package/src/components/ImplementationStructureDelete.js +2 -6
  10. package/src/components/ImplementationsRoutes.js +18 -1
  11. package/src/components/ImplementationsUploadButton.js +1 -1
  12. package/src/components/RuleImplementationHistory.js +5 -8
  13. package/src/components/RuleImplementationHistoryRow.js +23 -28
  14. package/src/components/RuleImplementationResultTabs.js +2 -2
  15. package/src/components/RuleImplementationResults.js +24 -149
  16. package/src/components/RuleImplementationSelectedFilters.js +2 -1
  17. package/src/components/RuleImplementationTabs.js +14 -8
  18. package/src/components/RuleImplementationsTable.js +1 -4
  19. package/src/components/RuleResultDecorator.js +10 -9
  20. package/src/components/RuleResultRow.js +9 -15
  21. package/src/components/RuleResultsRoutes.js +2 -2
  22. package/src/components/RuleResultsTable.js +117 -0
  23. package/src/components/RuleResultsUpload.js +2 -6
  24. package/src/components/RulesUploadButton.js +1 -1
  25. package/src/components/__tests__/ImplementationExecutionFilters.spec.js +24 -0
  26. package/src/components/__tests__/ImplementationExecutions.spec.js +32 -0
  27. package/src/components/__tests__/RuleImplementationResults.spec.js +16 -45
  28. package/src/components/__tests__/RuleImplementationTabs.spec.js +1 -18
  29. package/src/components/__tests__/RuleResultDecorator.spec.js +12 -32
  30. package/src/components/__tests__/RuleResultDetails.spec.js +3 -3
  31. package/src/components/__tests__/RuleResultRemediations.spec.js +3 -3
  32. package/src/components/__tests__/RuleResultRoutes.spec.js +5 -5
  33. package/src/components/__tests__/RuleResultRow.spec.js +86 -82
  34. package/src/components/__tests__/RuleResultSegmentRow.spec.js +6 -6
  35. package/src/components/__tests__/RuleResultSegments.spec.js +1 -1
  36. package/src/components/__tests__/__fixtures__/executionMocks.js +80 -0
  37. package/src/components/__tests__/__snapshots__/ExecutionGroups.spec.js.snap +0 -1
  38. package/src/components/__tests__/__snapshots__/ImplementationExecutionFilters.spec.js.snap +37 -0
  39. package/src/components/__tests__/__snapshots__/ImplementationExecutions.spec.js.snap +194 -0
  40. package/src/components/__tests__/__snapshots__/RuleImplementation.spec.js.snap +6 -0
  41. package/src/components/__tests__/__snapshots__/RuleImplementationHistory.spec.js.snap +12 -24
  42. package/src/components/__tests__/__snapshots__/RuleImplementationResults.spec.js.snap +8 -8
  43. package/src/components/__tests__/__snapshots__/RuleImplementationTabs.spec.js.snap +12 -6
  44. package/src/components/__tests__/__snapshots__/RuleResultDecorator.spec.js.snap +4 -4
  45. package/src/components/ruleImplementationForm/DateField.js +2 -2
  46. package/src/components/ruleImplementationForm/DateTimeField.js +2 -2
  47. package/src/components/ruleImplementationForm/FieldModifier.js +1 -1
  48. package/src/components/ruleImplementationForm/FiltersFormGroup.js +3 -3
  49. package/src/components/ruleImplementationForm/FiltersGroup.js +2 -2
  50. package/src/components/ruleImplementationForm/LimitsForm.js +6 -6
  51. package/src/components/ruleImplementationForm/ValueConditions.js +1 -1
  52. package/src/functions/selectors.js +12 -4
  53. package/src/messages/en.js +6 -0
  54. package/src/messages/es.js +6 -0
  55. package/src/selectors/getRuleImplementationColumns.js +4 -2
  56. package/src/selectors/index.js +1 -1
  57. package/src/selectors/{getRuleResultsColumns.js → ruleResultsColumnsSelector.js} +1 -1
@@ -21,7 +21,7 @@ export const RuleImplementationResultTabs = ({
21
21
  templates,
22
22
  }) => {
23
23
  const renderSegmentResult = () => {
24
- return ruleResult.has_segments ? (
24
+ return ruleResult.hasSegments ? (
25
25
  <Menu.Item
26
26
  active={match.path === IMPLEMENTATION_RESULT_SEGMENTS_RESULTS}
27
27
  as={Link}
@@ -37,7 +37,7 @@ export const RuleImplementationResultTabs = ({
37
37
 
38
38
  const renderRemediationPlan = () => {
39
39
  const canCreateRemediation = authCreateRemediation && !_.isEmpty(templates);
40
- const renderCondition = ruleResult.has_remediation || canCreateRemediation;
40
+ const renderCondition = ruleResult.hasRemediation || canCreateRemediation;
41
41
 
42
42
  return renderCondition ? (
43
43
  <Menu.Item
@@ -4,31 +4,18 @@ import { useParams } from "react-router-dom";
4
4
  import { useQuery } from "@apollo/client";
5
5
  import PropTypes from "prop-types";
6
6
  import { useIntl } from "react-intl";
7
- import { connect } from "react-redux";
8
- import { Table, Message, Divider } from "semantic-ui-react";
7
+ import { Message, Divider } from "semantic-ui-react";
9
8
  import { columnDecorator } from "@truedat/core/services";
10
- import Moment from "react-moment";
11
- import { Loading } from "@truedat/core/components";
12
- import { getRuleResultsColumns } from "../selectors";
13
- import { IMPLEMENTATION_RESULTS } from "../api/queries";
14
- import RuleResultRow from "./RuleResultRow";
15
-
16
- const optionalColumns = ["errors", "records", "details"];
17
- const getOptionalColumnsWithData = (ruleResults) =>
18
- _.filter((column) =>
19
- _.any(_.flow(_.prop(column), _.negate(_.isNil)))(ruleResults)
20
- )(optionalColumns);
9
+ import { DateTime, Loading } from "@truedat/core/components";
10
+ import { IMPLEMENTATION_RESULTS_QUERY } from "../api/queries";
11
+ import RuleResultsTable from "./RuleResultsTable";
21
12
 
22
13
  export const getCustomColumnsWithData = (ruleResults, columns) =>
23
14
  _.filter((column) =>
24
15
  _.any(_.flow(columnDecorator(column), _.negate(_.isEmpty)))(ruleResults)
25
16
  )(columns);
26
17
 
27
- export const RuleImplementationResults = ({
28
- ruleImplementation,
29
- ruleResultsColumns,
30
- isAdmin,
31
- }) => {
18
+ export const RuleImplementationResults = ({ ruleImplementation }) => {
32
19
  const { formatMessage, locale } = useIntl();
33
20
 
34
21
  if (_.isEmpty(ruleImplementation)) return null;
@@ -39,164 +26,52 @@ export const RuleImplementationResults = ({
39
26
  _.first
40
27
  )(implementationVersions);
41
28
 
42
- const commonProps = {
43
- ruleImplementation,
44
- isAdmin,
45
- ruleResultsColumns,
46
- formatMessage,
47
- };
48
-
49
- const { last_quality_event } = ruleImplementation;
29
+ const { lastQualityEvent } = ruleImplementation;
50
30
 
51
31
  return (
52
32
  <>
53
- {last_quality_event?.type === "FAILED" ? (
33
+ {lastQualityEvent?.type === "FAILED" ? (
54
34
  <Message negative style={{ marginTop: "14px" }}>
55
- <Message.Header>
56
- {formatMessage({
57
- id: "quality.error",
58
- })}
59
- </Message.Header>
35
+ <Message.Header content={formatMessage({ id: "quality.error" })} />
60
36
  <Divider />
61
- <Moment
37
+ <span
62
38
  style={{
63
39
  position: "absolute",
64
40
  top: "14px",
65
41
  right: "21px",
66
42
  }}
67
- locale={locale}
68
- date={last_quality_event.inserted_at}
69
- format="YYYY-MM-DD HH:mm"
70
- />
71
- <p
72
- style={{
73
- whiteSpace: "pre-wrap",
74
- }}
75
43
  >
76
- {last_quality_event.message}
77
- </p>
44
+ <DateTime value={lastQualityEvent.insertedAt} locale={locale} />
45
+ </span>
46
+ <p style={{ whiteSpace: "pre-wrap" }}>{lastQualityEvent.message}</p>
78
47
  </Message>
79
48
  ) : null}
80
- {resultsTable({
81
- commonProps,
82
- implementationVersions,
83
- currentImplementationVersion,
84
- })}
49
+ <RuleResultsTable
50
+ currentImplementationVersion={currentImplementationVersion}
51
+ implementationVersions={implementationVersions}
52
+ />
85
53
  </>
86
54
  );
87
55
  };
88
56
 
89
- export const resultsTable = ({
90
- commonProps,
91
- implementationVersions,
92
- currentImplementationVersion,
93
- }) => {
94
- const { isAdmin, ruleResultsColumns, formatMessage } = commonProps;
95
-
96
- const ruleResults = _.flow(
97
- _.map((iv) => _.pathOr([], "results")(iv)),
98
- _.flatten
99
- )(implementationVersions);
100
- const optionalColumns = getOptionalColumnsWithData(ruleResults);
101
- const customColumns = getCustomColumnsWithData(
102
- ruleResults,
103
- ruleResultsColumns
104
- );
105
-
106
- return _.isEmpty(ruleResults) ? (
107
- <Message
108
- style={{ marginTop: "14px" }}
109
- header={formatMessage({
110
- id: "rule.ruleImplementation.results.empty",
111
- })}
112
- />
113
- ) : (
114
- <Table className="implementation-results small">
115
- <Table.Header>
116
- <Table.Row>
117
- <Table.HeaderCell
118
- content={formatMessage({ id: "ruleResult.props.date" })}
119
- />
120
- <Table.HeaderCell
121
- content={formatMessage({ id: "ruleResult.props.quality" })}
122
- />
123
- {_.includes("records")(optionalColumns) ? (
124
- <Table.HeaderCell
125
- content={formatMessage({ id: "ruleResult.props.records" })}
126
- />
127
- ) : null}
128
- {_.includes("errors")(optionalColumns) ? (
129
- <Table.HeaderCell
130
- content={formatMessage({ id: "ruleResult.props.errors" })}
131
- />
132
- ) : null}
133
- {customColumns.map((column, index) => (
134
- <Table.HeaderCell
135
- key={index}
136
- content={formatMessage({
137
- id: `ruleResult.props.${column.name}`,
138
- defaultMessage: column.name,
139
- })}
140
- />
141
- ))}
142
- {<Table.HeaderCell />}
143
- {isAdmin ? <Table.HeaderCell /> : null}
144
- </Table.Row>
145
- </Table.Header>
146
- <Table.Body>
147
- {implementationVersions.map((version, i) =>
148
- _.pathOr(
149
- [],
150
- "results"
151
- )(version).map((result, j) => (
152
- <RuleResultRow
153
- key={"version" + i + "_result" + j}
154
- optionalColumns={optionalColumns}
155
- ruleResult={result}
156
- customColumns={customColumns}
157
- isAdmin={isAdmin}
158
- ruleImplementation={version}
159
- active={currentImplementationVersion.id == version.id}
160
- tagLabel={
161
- j === 0
162
- ? `${formatMessage({
163
- id: "ruleImplementations.props.version",
164
- })}: ${version.version}`
165
- : null
166
- }
167
- />
168
- ))
169
- )}
170
- </Table.Body>
171
- </Table>
172
- );
173
- };
174
-
175
57
  RuleImplementationResults.propTypes = {
176
58
  ruleImplementation: PropTypes.object,
177
- isAdmin: PropTypes.bool,
178
- ruleResultsColumns: PropTypes.array,
179
59
  };
180
60
 
181
- const mapStateToProps = (state) => ({ state: state });
182
-
183
61
  export const RuleImplementationResultsLoader = (props) => {
184
- const { state } = props;
185
62
  const { implementation_id: id } = useParams();
186
- const { loading, error, data } = useQuery(IMPLEMENTATION_RESULTS, {
63
+ const { loading, error, data } = useQuery(IMPLEMENTATION_RESULTS_QUERY, {
187
64
  variables: { id },
188
65
  });
189
66
  if (error) return null;
190
67
  if (loading) return <Loading />;
191
68
  const ruleImplementation = data?.implementation;
192
- const componentProperties = {
193
- ruleImplementation,
194
- ruleResultsColumns: getRuleResultsColumns(state),
195
- isAdmin: state.authentication.role === "admin",
196
- };
197
- return <RuleImplementationResults {...componentProperties} />;
69
+ return (
70
+ <RuleImplementationResults
71
+ ruleImplementation={ruleImplementation}
72
+ {...props}
73
+ />
74
+ );
198
75
  };
199
76
 
200
- export default connect(mapStateToProps, { RuleImplementationResults })(
201
- RuleImplementationResultsLoader
202
- );
77
+ export default RuleImplementationResultsLoader;
@@ -24,7 +24,8 @@ import {
24
24
  } from "../selectors";
25
25
 
26
26
  const translations = (formatMessage) => ({
27
- "execution_result_info.result_text": (v) => formatMessage({ id: v }),
27
+ "execution_result_info.result_text": (v) =>
28
+ formatMessage({ id: v, defaultMessage: v }),
28
29
  });
29
30
 
30
31
  export const mapStateToProps = (state, ownProps) => {
@@ -2,14 +2,14 @@ import _ from "lodash/fp";
2
2
  import React from "react";
3
3
  import PropTypes from "prop-types";
4
4
  import { Menu } from "semantic-ui-react";
5
- import { Link, withRouter } from "react-router-dom";
6
- import { compose } from "redux";
5
+ import { Link, useRouteMatch } from "react-router-dom";
7
6
  import { connect } from "react-redux";
8
7
  import { FormattedMessage } from "react-intl";
9
8
  import {
10
9
  IMPLEMENTATION_CONCEPT_LINKS_NEW,
11
10
  IMPLEMENTATION_CONCEPT_LINKS,
12
11
  IMPLEMENTATION_EVENTS,
12
+ IMPLEMENTATION_EXECUTIONS,
13
13
  IMPLEMENTATION_HISTORY,
14
14
  IMPLEMENTATION_MOVE,
15
15
  IMPLEMENTATION_RESULTS_DETAILS,
@@ -23,9 +23,9 @@ import {
23
23
  export const RuleImplementationTabs = ({
24
24
  rule,
25
25
  ruleImplementation,
26
- match,
27
26
  canCreateLink,
28
27
  }) => {
28
+ const match = useRouteMatch();
29
29
  const latest_rule_id = _.flow(
30
30
  _.pathOr([], "results"),
31
31
  _.head,
@@ -83,7 +83,16 @@ export const RuleImplementationTabs = ({
83
83
  >
84
84
  <FormattedMessage id="tabs.dq.ruleImplementation.results" />
85
85
  </Menu.Item>
86
- {!_.isNil(latest_rule_id) && (
86
+ <Menu.Item
87
+ active={match.path === IMPLEMENTATION_EXECUTIONS}
88
+ as={Link}
89
+ to={linkTo.IMPLEMENTATION_EXECUTIONS({
90
+ implementation_id: ruleImplementation.id,
91
+ })}
92
+ >
93
+ <FormattedMessage id="tabs.dq.ruleImplementation.executions" />
94
+ </Menu.Item>
95
+ {_.isNil(latest_rule_id) ? null : (
87
96
  <Menu.Item
88
97
  active={match.path === IMPLEMENTATION_RESULTS_DETAILS}
89
98
  as={Link}
@@ -135,7 +144,4 @@ const mapStateToProps = ({
135
144
  canCreateLink: _.propOr(false, "link_concept")(implementationActions),
136
145
  });
137
146
 
138
- export default compose(
139
- withRouter,
140
- connect(mapStateToProps)
141
- )(RuleImplementationTabs);
147
+ export default connect(mapStateToProps)(RuleImplementationTabs);
@@ -128,10 +128,7 @@ RuleImplementationsTable.propTypes = {
128
128
 
129
129
  const mapStateToProps = (state, props) => ({
130
130
  columns: getRuleImplementationColumns(state),
131
- ruleImplementations:
132
- props.ruleImplementations ||
133
- state.structure?.implementations?.map(_.prop("implementation")) ||
134
- state.ruleImplementations,
131
+ ruleImplementations: props.ruleImplementations || state.ruleImplementations,
135
132
  ruleImplementationsLoading: state.ruleImplementationsLoading,
136
133
  implementationsSort: _.path("ruleImplementationQuery.sort")(state),
137
134
  });
@@ -1,4 +1,4 @@
1
- import { has, isNil } from "lodash/fp";
1
+ import { defaultTo, has, isNil } from "lodash/fp";
2
2
  import React from "react";
3
3
  import PropTypes from "prop-types";
4
4
  import { Icon } from "semantic-ui-react";
@@ -28,25 +28,26 @@ export const calculateResultDecoration = ({
28
28
  };
29
29
  }
30
30
 
31
- const resultType = ruleImplementation?.result_type;
31
+ const { resultType, result_type } = ruleImplementation;
32
+ const type = defaultTo(result_type)(resultType);
32
33
  const result =
33
- resultType === "errors_number"
34
+ type === "errors_number"
34
35
  ? formatNumber(ruleResult?.errors)
35
36
  : ruleResult?.result;
36
37
  const resultText = isNil(result)
37
38
  ? formatMessage({ id: "quality.result.no.data" })
38
39
  : date
39
40
  ? formatMessage(
40
- { id: `quality.result.${resultType}.date` },
41
+ { id: `quality.result.${type}.date` },
41
42
  { result, date: <DateTime value={date} /> }
42
43
  )
43
- : formatMessage(
44
- { id: `quality.result.${resultType}.description` },
45
- { result }
46
- );
44
+ : formatMessage({ id: `quality.result.${type}.description` }, { result });
47
45
  const color = selectColor({ ...ruleImplementation, ...ruleResult });
48
46
  const iconTitle = ruleResult?.result_text
49
- ? formatMessage({ id: ruleResult?.result_text })
47
+ ? formatMessage({
48
+ id: ruleResult?.result_text,
49
+ defaultMessage: ruleResult?.result_text,
50
+ })
50
51
  : null;
51
52
 
52
53
  return { iconName: "circle", color, resultText, iconTitle };
@@ -3,10 +3,10 @@ import React from "react";
3
3
  import PropTypes from "prop-types";
4
4
  import { connect } from "react-redux";
5
5
  import { useIntl } from "react-intl";
6
- import { useHistory } from "react-router-dom";
7
6
  import { Link } from "react-router-dom";
8
7
  import { Table, Icon, Label } from "semantic-ui-react";
9
8
  import { ConfirmModal, DateTime } from "@truedat/core/components";
9
+ import { useAuthorized } from "@truedat/core/hooks";
10
10
  import { columnDecorator } from "@truedat/core/services";
11
11
  import { linkTo } from "@truedat/core/routes";
12
12
  import { selectColor } from "../functions/selectors";
@@ -19,30 +19,25 @@ export const RuleResultRow = ({
19
19
  optionalColumns,
20
20
  ruleResult,
21
21
  customColumns,
22
- isAdmin,
23
22
  ruleImplementation,
24
23
  tagLabel,
25
24
  }) => {
25
+ const isAdmin = useAuthorized();
26
26
  const { formatMessage, formatNumber: _formatNumber } = useIntl();
27
27
  const formatNumber = (num) => (_.isNil(num) ? num : _formatNumber(num));
28
- const history = useHistory();
29
28
 
30
29
  return (
31
30
  <>
32
31
  {tagLabel ? (
33
32
  <Table.Row>
34
- <Table.Cell colSpan={8}>
33
+ <Table.Cell>
35
34
  <Label
36
- as="a"
35
+ as={Link}
36
+ to={linkTo.IMPLEMENTATION({
37
+ implementation_id: ruleImplementation.id,
38
+ })}
37
39
  active={active}
38
40
  ribbon
39
- onClick={() =>
40
- history.push(
41
- linkTo.IMPLEMENTATION({
42
- implementation_id: ruleImplementation.id,
43
- })
44
- )
45
- }
46
41
  >
47
42
  {tagLabel}
48
43
  </Label>
@@ -59,7 +54,7 @@ export const RuleResultRow = ({
59
54
  })}
60
55
  >
61
56
  <DateTime className="rule-result-date" value={ruleResult.date} />
62
- {ruleResult.has_segments ? (
57
+ {ruleResult.hasSegments ? (
63
58
  <Icon
64
59
  circular
65
60
  inverted
@@ -69,7 +64,7 @@ export const RuleResultRow = ({
69
64
  />
70
65
  ) : null}
71
66
 
72
- {ruleResult.has_remediation ? (
67
+ {ruleResult.hasRemediation ? (
73
68
  <Icon circular inverted name="rain" color="blue" size="small" />
74
69
  ) : null}
75
70
  </Link>
@@ -121,7 +116,6 @@ RuleResultRow.propTypes = {
121
116
  active: PropTypes.bool,
122
117
  customColumns: PropTypes.array,
123
118
  deleteRuleResult: PropTypes.func,
124
- isAdmin: PropTypes.bool,
125
119
  optionalColumns: PropTypes.array,
126
120
  ruleImplementation: PropTypes.object,
127
121
  ruleResult: PropTypes.object,
@@ -8,7 +8,7 @@ import {
8
8
  IMPLEMENTATION_RESULT_SEGMENTS_RESULTS,
9
9
  IMPLEMENTATION_RESULT_REMEDIATION_PLAN,
10
10
  } from "@truedat/core/routes";
11
- import { IMPLEMENTATION_RESULT } from "../api/queries";
11
+ import { IMPLEMENTATION_RESULT_QUERY } from "../api/queries";
12
12
  import RuleResult from "./RuleResult";
13
13
  import RuleResultDetails from "./RuleResultDetails";
14
14
  import RuleResultSegments from "./RuleResultSegments";
@@ -18,7 +18,7 @@ import RuleResultSegmentsLoader from "./RuleResultSegmentsLoader";
18
18
 
19
19
  export const RuleResultsRoutes = ({}) => {
20
20
  const { rule_result_id } = useParams();
21
- const { loading, error, data } = useQuery(IMPLEMENTATION_RESULT, {
21
+ const { loading, error, data } = useQuery(IMPLEMENTATION_RESULT_QUERY, {
22
22
  variables: { id: rule_result_id },
23
23
  });
24
24
  if (error) return null;
@@ -0,0 +1,117 @@
1
+ import _ from "lodash/fp";
2
+ import React from "react";
3
+ import PropTypes from "prop-types";
4
+ import { useIntl } from "react-intl";
5
+ import { connect } from "react-redux";
6
+ import { Table, Message } from "semantic-ui-react";
7
+ import { useAuthorized } from "@truedat/core/hooks";
8
+ import { columnDecorator } from "@truedat/core/services";
9
+ import { ruleResultsColumnsSelector } from "../selectors";
10
+ import RuleResultRow from "./RuleResultRow";
11
+
12
+ const optionalColumns = ["errors", "records", "details"];
13
+
14
+ const getOptionalColumnsWithData = (ruleResults) =>
15
+ _.filter((column) =>
16
+ _.any(_.flow(_.prop(column), _.negate(_.isNil)))(ruleResults)
17
+ )(optionalColumns);
18
+
19
+ export const getCustomColumnsWithData = (ruleResults, columns) =>
20
+ _.filter((column) =>
21
+ _.any(_.flow(columnDecorator(column), _.negate(_.isEmpty)))(ruleResults)
22
+ )(columns);
23
+
24
+ export const RuleResultsTable = ({
25
+ columns,
26
+ implementationVersions,
27
+ currentImplementationVersion,
28
+ }) => {
29
+ const isAdmin = useAuthorized();
30
+ const { formatMessage } = useIntl();
31
+
32
+ const ruleResults = _.flow(
33
+ _.map((iv) => _.pathOr([], "results")(iv)),
34
+ _.flatten
35
+ )(implementationVersions);
36
+ const optionalColumns = getOptionalColumnsWithData(ruleResults);
37
+ const customColumns = getCustomColumnsWithData(ruleResults, columns);
38
+
39
+ return _.isEmpty(ruleResults) ? (
40
+ <Message
41
+ style={{ marginTop: "14px" }}
42
+ header={formatMessage({
43
+ id: "rule.ruleImplementation.results.empty",
44
+ })}
45
+ />
46
+ ) : (
47
+ <Table className="implementation-results small">
48
+ <Table.Header>
49
+ <Table.Row>
50
+ <Table.HeaderCell
51
+ content={formatMessage({ id: "ruleResult.props.date" })}
52
+ />
53
+ <Table.HeaderCell
54
+ content={formatMessage({ id: "ruleResult.props.quality" })}
55
+ />
56
+ {_.includes("records")(optionalColumns) ? (
57
+ <Table.HeaderCell
58
+ content={formatMessage({ id: "ruleResult.props.records" })}
59
+ />
60
+ ) : null}
61
+ {_.includes("errors")(optionalColumns) ? (
62
+ <Table.HeaderCell
63
+ content={formatMessage({ id: "ruleResult.props.errors" })}
64
+ />
65
+ ) : null}
66
+ {customColumns.map((column, index) => (
67
+ <Table.HeaderCell
68
+ key={index}
69
+ content={formatMessage({
70
+ id: `ruleResult.props.${column.name}`,
71
+ defaultMessage: column.name,
72
+ })}
73
+ />
74
+ ))}
75
+ {<Table.HeaderCell />}
76
+ {isAdmin ? <Table.HeaderCell /> : null}
77
+ </Table.Row>
78
+ </Table.Header>
79
+ <Table.Body>
80
+ {implementationVersions.map((version) =>
81
+ _.pathOr(
82
+ [],
83
+ "results"
84
+ )(version).map((result, key) => (
85
+ <RuleResultRow
86
+ key={key}
87
+ optionalColumns={optionalColumns}
88
+ ruleResult={result}
89
+ customColumns={customColumns}
90
+ ruleImplementation={version}
91
+ active={currentImplementationVersion.id === version.id}
92
+ tagLabel={
93
+ key === 0
94
+ ? `${formatMessage({
95
+ id: "ruleImplementations.props.version",
96
+ })}: ${version.version}`
97
+ : null
98
+ }
99
+ />
100
+ ))
101
+ )}
102
+ </Table.Body>
103
+ </Table>
104
+ );
105
+ };
106
+
107
+ RuleResultsTable.propTypes = {
108
+ columns: PropTypes.array,
109
+ implementationVersions: PropTypes.array,
110
+ currentImplementationVersion: PropTypes.object,
111
+ };
112
+
113
+ const mapStateToProps = (state) => ({
114
+ columns: ruleResultsColumnsSelector(state),
115
+ });
116
+
117
+ export default connect(mapStateToProps)(RuleResultsTable);
@@ -25,12 +25,8 @@ export const RuleResultsUpload = ({ uploadResults, loading }) => {
25
25
  content={formatMessage({
26
26
  id: "uploadModal.actions.upload.confirmation.content",
27
27
  })}
28
- param={"rule_results"}
29
- handleSubmit={(data) =>
30
- uploadResults({
31
- data,
32
- })
33
- }
28
+ param="rule_results"
29
+ handleSubmit={(data) => uploadResults({ data })}
34
30
  />
35
31
  );
36
32
  };
@@ -34,7 +34,7 @@ export const RulesUploadButton = ({ uploadRules, loading }) => {
34
34
  content={
35
35
  <FormattedMessage id="rules.actions.upload.confirmation.content" />
36
36
  }
37
- param={"rules"}
37
+ param="rules"
38
38
  handleSubmit={(data) =>
39
39
  uploadRules({
40
40
  action: "upload",
@@ -0,0 +1,24 @@
1
+ import React from "react";
2
+ import { render } from "@truedat/test/render";
3
+ import ImplementationExecutionFilters from "../ImplementationExecutionFilters";
4
+ import { implementationExecutionFiltersMock } from "./__fixtures__/executionMocks";
5
+
6
+ jest.mock("react-router-dom", () => ({
7
+ ...jest.requireActual("react-router-dom"),
8
+ useParams: () => ({ implementation_id: "123" }),
9
+ }));
10
+
11
+ const renderOpts = {
12
+ mocks: [implementationExecutionFiltersMock({ id: "123" })],
13
+ };
14
+
15
+ describe("<ImplementationExecutionFilters />", () => {
16
+ it("matches snapshot with filter links", async () => {
17
+ const { container, findAllByRole } = render(
18
+ <ImplementationExecutionFilters />,
19
+ renderOpts
20
+ );
21
+ expect(await findAllByRole("link")).toHaveLength(4);
22
+ expect(container).toMatchSnapshot();
23
+ });
24
+ });
@@ -0,0 +1,32 @@
1
+ import React from "react";
2
+ import { render } from "@truedat/test/render";
3
+ import ImplementationExecutions from "../ImplementationExecutions";
4
+ import {
5
+ implementationExecutionFiltersMock,
6
+ implementationExecutionsMock,
7
+ } from "./__fixtures__/executionMocks";
8
+
9
+ jest.mock("react-router-dom", () => ({
10
+ ...jest.requireActual("react-router-dom"),
11
+ useParams: () => ({ implementation_id: "123" }),
12
+ }));
13
+
14
+ const renderOpts = {
15
+ mocks: [
16
+ implementationExecutionFiltersMock({ id: "123" }),
17
+ implementationExecutionsMock({ id: "123", last: 20, filters: [] }),
18
+ ],
19
+ };
20
+
21
+ describe("<ImplementationExecutions />", () => {
22
+ it("matches snapshot with executions table", async () => {
23
+ const { container, findByRole, getByRole, getAllByRole } = render(
24
+ <ImplementationExecutions />,
25
+ renderOpts
26
+ );
27
+ expect(await findByRole("table")).toBeInTheDocument();
28
+ expect(container).toMatchSnapshot();
29
+ expect(getAllByRole("row")).toHaveLength(3);
30
+ expect(getByRole("cell", { name: /succeeded/ })).toBeInTheDocument();
31
+ });
32
+ });