@truedat/dq 4.50.2 → 4.51.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 (33) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/package.json +4 -4
  3. package/src/api/queries.js +33 -0
  4. package/src/components/ExecutionGroup.js +28 -22
  5. package/src/components/ExecutionGroupBreadcrumbs.js +25 -0
  6. package/src/components/ExecutionGroupContent.js +42 -0
  7. package/src/components/ExecutionGroupLink.js +18 -0
  8. package/src/components/ExecutionGroupLoader.js +1 -2
  9. package/src/components/ExecutionGroupMessage.js +1 -1
  10. package/src/components/ExecutionGroups.js +80 -0
  11. package/src/components/ExecutionGroupsTable.js +61 -0
  12. package/src/components/QualityRoutes.js +20 -12
  13. package/src/components/__tests__/ExecutionGroup.spec.js +44 -47
  14. package/src/components/__tests__/ExecutionGroupBreadcrumbs.spec.js +11 -0
  15. package/src/components/__tests__/ExecutionGroupContent.spec.js +30 -0
  16. package/src/components/__tests__/ExecutionGroupLink.spec.js +11 -0
  17. package/src/components/__tests__/ExecutionGroups.spec.js +51 -0
  18. package/src/components/__tests__/ExecutionGroupsTable.spec.js +30 -0
  19. package/src/components/__tests__/__snapshots__/ExecutionGroup.spec.js.snap +136 -100
  20. package/src/components/__tests__/__snapshots__/ExecutionGroupBreadcrumbs.spec.js.snap +29 -0
  21. package/src/components/__tests__/__snapshots__/ExecutionGroupContent.spec.js.snap +37 -0
  22. package/src/components/__tests__/__snapshots__/ExecutionGroupLink.spec.js.snap +15 -0
  23. package/src/components/__tests__/__snapshots__/ExecutionGroups.spec.js.snap +155 -0
  24. package/src/components/__tests__/__snapshots__/ExecutionGroupsTable.spec.js.snap +84 -0
  25. package/src/components/ruleImplementationForm/InformationForm.js +1 -0
  26. package/src/components/ruleImplementationForm/RuleImplementationRawForm.js +1 -0
  27. package/src/messages/en.js +106 -96
  28. package/src/messages/es.js +178 -168
  29. package/src/reducers/executionGroup.js +7 -1
  30. package/src/reducers/executionGroupLoading.js +9 -1
  31. package/src/selectors/__tests__/executionGroupsColumnsSelector.spec.js +19 -0
  32. package/src/selectors/executionGroupsColumnsSelector.js +41 -0
  33. package/src/selectors/index.js +1 -0
package/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # Changelog
2
2
 
3
+ ## [4.51.0] 2022-09-13
4
+
5
+ ### Added
6
+
7
+ - [TD-3415] View with current user's execution groups
8
+
9
+ ## [4.50.4] 2022-08-31
10
+
11
+ ### Added
12
+
13
+ - [TD-5091] Add labels attribute for DomainSelector component
14
+
3
15
  ## [4.50.0] 2022-08-18
4
16
 
5
17
  ## Changed
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@truedat/dq",
3
- "version": "4.50.2",
3
+ "version": "4.51.0",
4
4
  "description": "Truedat Web Data Quality Module",
5
5
  "sideEffects": false,
6
6
  "jsnext:main": "src/index.js",
@@ -93,8 +93,8 @@
93
93
  },
94
94
  "dependencies": {
95
95
  "@apollo/client": "^3.6.4",
96
- "@truedat/core": "4.50.2",
97
- "@truedat/df": "4.50.2",
96
+ "@truedat/core": "4.51.0",
97
+ "@truedat/df": "4.51.0",
98
98
  "graphql": "^15.5.3",
99
99
  "path-to-regexp": "^1.7.0",
100
100
  "prop-types": "^15.8.1",
@@ -114,5 +114,5 @@
114
114
  "react-dom": ">= 16.8.6 < 17",
115
115
  "semantic-ui-react": ">= 0.88.2 < 2.1"
116
116
  },
117
- "gitHead": "5785ca8828b3c25947e8d2587c909f729fbee317"
117
+ "gitHead": "a3d9290433e773d9c605d9a0d9fa51908c5becad"
118
118
  }
@@ -65,3 +65,36 @@ export const IMPLEMENTATION_RESULT = gql`
65
65
  }
66
66
  }
67
67
  `;
68
+
69
+ export const MY_EXECUTION_GROUPS_QUERY = gql`
70
+ query MyExecutionGroups(
71
+ $after: Cursor
72
+ $before: Cursor
73
+ $last: Int
74
+ $first: Int
75
+ ) {
76
+ me {
77
+ id
78
+ executionGroupsConnection(
79
+ first: $first
80
+ last: $last
81
+ before: $before
82
+ after: $after
83
+ ) {
84
+ totalCount
85
+ page {
86
+ id
87
+ dfContent
88
+ insertedAt
89
+ statusCounts
90
+ }
91
+ pageInfo {
92
+ startCursor
93
+ endCursor
94
+ hasNextPage
95
+ hasPreviousPage
96
+ }
97
+ }
98
+ }
99
+ }
100
+ `;
@@ -1,21 +1,23 @@
1
- import { flow, pick, filter, reject, size, prop, sortBy } from "lodash/fp";
1
+ import _ from "lodash/fp";
2
2
  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
6
  import { Table } from "semantic-ui-react";
7
+ import { DateTime } from "@truedat/core/components";
7
8
  import { columnDecorator } from "@truedat/core/services";
8
- import DateTime from "@truedat/core/components/DateTime";
9
9
  import ExecutionGroupMessage from "./ExecutionGroupMessage";
10
10
  import RuleResultDecorator from "./RuleResultDecorator";
11
11
  import RuleImplementationResultsLink from "./RuleImplementationResultsLink";
12
12
  import RuleEventDecorator from "./RuleEventDecorator";
13
+ import ExecutionGroupContent from "./ExecutionGroupContent";
14
+ import ExecutionGroupBreadcrumbs from "./ExecutionGroupBreadcrumbs";
13
15
 
14
- const columns = [
16
+ const COLUMNS = [
15
17
  {
16
18
  name: "ruleImplementations.props.status",
17
19
  fieldDecorator: RuleEventDecorator,
18
- fieldSelector: pick(["_embedded.quality_events"]),
20
+ fieldSelector: _.pick(["_embedded.quality_events"]),
19
21
  },
20
22
  {
21
23
  name: "ruleImplementations.props.implementation_key",
@@ -54,9 +56,9 @@ const columns = [
54
56
 
55
57
  export const ExecutionRow = ({ execution }) => (
56
58
  <Table.Row>
57
- {columns.map(({ textAlign, ...column }, j) => (
59
+ {COLUMNS.map(({ textAlign, ...column }, i) => (
58
60
  <Table.Cell
59
- key={j}
61
+ key={i}
60
62
  content={columnDecorator(column)(execution)}
61
63
  textAlign={textAlign}
62
64
  />
@@ -68,31 +70,35 @@ ExecutionRow.propTypes = {
68
70
  execution: PropTypes.object,
69
71
  };
70
72
 
71
- export const ExecutionGroup = ({ executionGroup, executionGroupLoading }) => {
73
+ const isCompleted = _.flow(_.prop("_embedded.status"), (s) =>
74
+ _.includes(s)(["SUCCEEDED", "FAILED"])
75
+ );
76
+
77
+ export const ExecutionGroup = ({ executionGroup }) => {
72
78
  const { formatMessage } = useIntl();
73
- const executions = sortBy("_embedded.implementation.implementation_key")(
79
+ const executions = _.sortBy("_embedded.implementation.implementation_key")(
74
80
  executionGroup?._embedded?.executions
75
81
  );
76
- const count = size(executions);
77
- const missingResultCount = flow(reject("_embedded.result"), size)(executions);
78
- const errorCount = flow(
79
- filter(flow(prop("_embedded.quality_events"), size)),
80
- size
81
- )(executions);
82
+ const count = _.size(executions);
83
+ const pendingCount = _.flow(_.reject(isCompleted), _.size)(executions);
84
+ const content = executionGroup?.df_content;
82
85
 
83
- return executionGroupLoading ? null : (
86
+ /*
87
+ * executionGroupLoading is causing an empty table to flash briefly when
88
+ * navigating between execution groups, so executionGroup === null is
89
+ * used here instead.
90
+ */
91
+ return executionGroup === null ? null : (
84
92
  <>
85
- <ExecutionGroupMessage
86
- count={count}
87
- pending={missingResultCount - errorCount}
88
- error={errorCount}
89
- />
93
+ <ExecutionGroupBreadcrumbs timestamp={executionGroup?.inserted_at} />
94
+ {_.isEmpty(content) ? null : <ExecutionGroupContent content={content} />}
95
+ <ExecutionGroupMessage count={count} pending={pendingCount} />
90
96
  <Table>
91
97
  <Table.Header>
92
98
  <Table.Row>
93
- {columns.map(({ name: id, textAlign }) => (
99
+ {COLUMNS.map(({ name: id, textAlign }, i) => (
94
100
  <Table.HeaderCell
95
- key={id}
101
+ key={i}
96
102
  content={formatMessage({ id })}
97
103
  textAlign={textAlign}
98
104
  />
@@ -0,0 +1,25 @@
1
+ import React from "react";
2
+ import PropTypes from "prop-types";
3
+ import { Breadcrumb } from "semantic-ui-react";
4
+ import { Link } from "react-router-dom";
5
+ import { FormattedMessage } from "react-intl";
6
+ import { DateTime } from "@truedat/core/components";
7
+ import { EXECUTION_GROUPS } from "@truedat/core/routes";
8
+
9
+ export const ExecutionGroupBreadcrumbs = ({ timestamp }) => (
10
+ <Breadcrumb>
11
+ <Breadcrumb.Section as={Link} to={EXECUTION_GROUPS}>
12
+ <FormattedMessage id="sidemenu.executions" />
13
+ </Breadcrumb.Section>
14
+ <Breadcrumb.Divider icon="right angle" />
15
+ <Breadcrumb.Section active>
16
+ <DateTime value={timestamp} />
17
+ </Breadcrumb.Section>
18
+ </Breadcrumb>
19
+ );
20
+
21
+ ExecutionGroupBreadcrumbs.propTypes = {
22
+ timestamp: PropTypes.string,
23
+ };
24
+
25
+ export default ExecutionGroupBreadcrumbs;
@@ -0,0 +1,42 @@
1
+ import _ from "lodash/fp";
2
+ import React from "react";
3
+ import PropTypes from "prop-types";
4
+ import { useQuery } from "@apollo/client";
5
+ import { TEMPLATES_QUERY } from "@truedat/core/api/queries";
6
+
7
+ const DynamicFormViewer = React.lazy(() =>
8
+ import("@truedat/df/components/DynamicFormViewer")
9
+ );
10
+
11
+ const templateFieldNames = _.flow(
12
+ _.prop("content"),
13
+ _.flatMap("fields"),
14
+ _.map("name")
15
+ );
16
+
17
+ export const ExecutionGroupContent = ({ content }) => {
18
+ const { data } = useQuery(TEMPLATES_QUERY, {
19
+ variables: { scope: "qe" },
20
+ });
21
+ // NOTE: We don't have the template name, so select one with fields matching content
22
+ const contentFieldNames = _.keys(content);
23
+ const matchingFieldCount = _.flow(
24
+ templateFieldNames,
25
+ _.intersection(contentFieldNames),
26
+ _.size
27
+ );
28
+ const template = _.flow(
29
+ _.propOr([], "templates"),
30
+ _.maxBy(matchingFieldCount)
31
+ )(data);
32
+
33
+ return template ? (
34
+ <DynamicFormViewer template={template} content={content} />
35
+ ) : null;
36
+ };
37
+
38
+ ExecutionGroupContent.propTypes = {
39
+ content: PropTypes.object,
40
+ };
41
+
42
+ export default ExecutionGroupContent;
@@ -0,0 +1,18 @@
1
+ import React from "react";
2
+ import PropTypes from "prop-types";
3
+ import { Link } from "react-router-dom";
4
+ import { DateTime } from "@truedat/core/components";
5
+ import { linkTo } from "@truedat/core/routes";
6
+
7
+ export const ExecutionGroupLink = ({ id, insertedAt }) => (
8
+ <Link to={linkTo.EXECUTION_GROUP({ id })}>
9
+ <DateTime value={insertedAt} />
10
+ </Link>
11
+ );
12
+
13
+ ExecutionGroupLink.propTypes = {
14
+ id: PropTypes.string,
15
+ insertedAt: PropTypes.string,
16
+ };
17
+
18
+ export default ExecutionGroupLink;
@@ -6,7 +6,7 @@ import { fetchExecutionGroup, clearExecutionGroup } from "../routines";
6
6
 
7
7
  export const ExecutionGroupLoader = ({
8
8
  fetchExecutionGroup,
9
- clearExecutionGroup
9
+ clearExecutionGroup,
10
10
  }) => {
11
11
  const { id } = useParams();
12
12
  useEffect(() => {
@@ -23,7 +23,6 @@ export const ExecutionGroupLoader = ({
23
23
  ExecutionGroupLoader.propTypes = {
24
24
  clearExecutionGroup: PropTypes.func,
25
25
  fetchExecutionGroup: PropTypes.func,
26
- id: PropTypes.number
27
26
  };
28
27
 
29
28
  const mapDispatchToProps = { clearExecutionGroup, fetchExecutionGroup };
@@ -21,7 +21,7 @@ export const ExecutionGroupMessage = ({ count, pending }) => {
21
21
 
22
22
  ExecutionGroupMessage.propTypes = {
23
23
  count: PropTypes.number,
24
- pending: PropTypes.number
24
+ pending: PropTypes.number,
25
25
  };
26
26
 
27
27
  export default ExecutionGroupMessage;
@@ -0,0 +1,80 @@
1
+ import React from "react";
2
+ import PropTypes from "prop-types";
3
+ import { useQuery } from "@apollo/client";
4
+ import queryString from "query-string";
5
+ import { useIntl } from "react-intl";
6
+ import { useLocation, Link } from "react-router-dom";
7
+ import { Header, Menu, Segment } from "semantic-ui-react";
8
+ import { MY_EXECUTION_GROUPS_QUERY } from "../api/queries";
9
+ import ExecutionGroupsTable from "./ExecutionGroupsTable";
10
+
11
+ const PAGE_SIZE = 20;
12
+
13
+ export const Pagination = ({ pageInfo }) => {
14
+ const { endCursor, startCursor, hasPreviousPage, hasNextPage } =
15
+ pageInfo || {};
16
+ const next =
17
+ hasNextPage && endCursor ? { search: "?after=" + endCursor } : null;
18
+ const prev =
19
+ hasPreviousPage && startCursor
20
+ ? { search: "?before=" + startCursor }
21
+ : null;
22
+ return (
23
+ <Menu pagination role="navigation">
24
+ <Menu.Item
25
+ as={next ? Link : null}
26
+ link={!!next}
27
+ content="«"
28
+ disabled={!hasNextPage}
29
+ to={{ search: "" }}
30
+ replace={next ? true : null}
31
+ aria-label="Latest"
32
+ />
33
+ <Menu.Item
34
+ as={next ? Link : null}
35
+ link={!!next}
36
+ content="⟨"
37
+ disabled={!hasNextPage}
38
+ to={next}
39
+ replace={next ? true : null}
40
+ aria-label="Later"
41
+ />
42
+ <Menu.Item
43
+ as={prev ? Link : null}
44
+ link={!!prev}
45
+ content="⟩"
46
+ disabled={!hasPreviousPage}
47
+ to={prev}
48
+ replace={prev ? true : null}
49
+ aria-label="Earlier"
50
+ />
51
+ </Menu>
52
+ );
53
+ };
54
+
55
+ Pagination.propTypes = {
56
+ pageInfo: PropTypes.object,
57
+ };
58
+
59
+ export const ExecutionGroups = () => {
60
+ const { formatMessage } = useIntl();
61
+ const { search } = useLocation();
62
+ const { before, after } = queryString.parse(search);
63
+ const variables = after
64
+ ? { after, before, first: PAGE_SIZE }
65
+ : { after, before, last: PAGE_SIZE };
66
+ const { loading, error, data } = useQuery(MY_EXECUTION_GROUPS_QUERY, {
67
+ variables,
68
+ });
69
+ if (error) return null;
70
+ const connection = data?.me?.executionGroupsConnection;
71
+ return (
72
+ <Segment loading={loading}>
73
+ <Header as="h2" content={formatMessage({ id: "sidemenu.executions" })} />
74
+ <Pagination {...connection} />
75
+ {loading ? null : <ExecutionGroupsTable {...connection} />}
76
+ </Segment>
77
+ );
78
+ };
79
+
80
+ export default ExecutionGroups;
@@ -0,0 +1,61 @@
1
+ import React from "react";
2
+ import PropTypes from "prop-types";
3
+ import { FormattedMessage } from "react-intl";
4
+ import { connect } from "react-redux";
5
+ import { Table } from "semantic-ui-react";
6
+ import { columnDecorator } from "@truedat/core/services";
7
+ import { executionGroupsColumnsSelector } from "../selectors/executionGroupsColumnsSelector";
8
+
9
+ export const HeaderRow = ({ columns }) => (
10
+ <Table.Row>
11
+ {columns.map(({ name: id, textAlign }, i) => (
12
+ <Table.HeaderCell key={i} textAlign={textAlign}>
13
+ <FormattedMessage id={id} defaultMessage={id} />
14
+ </Table.HeaderCell>
15
+ ))}
16
+ </Table.Row>
17
+ );
18
+
19
+ HeaderRow.propTypes = {
20
+ columns: PropTypes.arrayOf(PropTypes.object),
21
+ };
22
+
23
+ export const ExecutionGroupRow = ({ columns, ...props }) => (
24
+ <Table.Row>
25
+ {columns.map((col, i) => (
26
+ <Table.Cell
27
+ key={i}
28
+ textAlign={col.textAlign}
29
+ content={columnDecorator(col)(props)}
30
+ />
31
+ ))}
32
+ </Table.Row>
33
+ );
34
+
35
+ ExecutionGroupRow.propTypes = {
36
+ columns: PropTypes.arrayOf(PropTypes.object),
37
+ };
38
+
39
+ export const ExecutionGroupsTable = ({ columns, page }) => (
40
+ <Table>
41
+ <Table.Header>
42
+ <HeaderRow columns={columns} />
43
+ </Table.Header>
44
+ <Table.Body>
45
+ {page?.map((props, key) => (
46
+ <ExecutionGroupRow key={key} columns={columns} {...props} />
47
+ ))}
48
+ </Table.Body>
49
+ </Table>
50
+ );
51
+
52
+ ExecutionGroupsTable.propTypes = {
53
+ columns: PropTypes.array,
54
+ page: PropTypes.array,
55
+ };
56
+
57
+ const mapStateToProps = (state) => ({
58
+ columns: executionGroupsColumnsSelector(state),
59
+ });
60
+
61
+ export default connect(mapStateToProps)(ExecutionGroupsTable);
@@ -1,35 +1,43 @@
1
1
  import React from "react";
2
- import { Route } from "react-router-dom";
2
+ import { Route, Switch } from "react-router-dom";
3
3
  import { Unauthorized } from "@truedat/core/components";
4
4
  import { useAuthorized } from "@truedat/core/hooks";
5
5
  import {
6
6
  EXECUTION_GROUP,
7
+ EXECUTION_GROUPS,
7
8
  IMPLEMENTATIONS,
8
9
  IMPLEMENTATIONS_PENDING,
9
10
  RULES,
10
11
  } from "@truedat/core/routes";
11
12
  import ExecutionGroup from "./ExecutionGroup";
12
13
  import ExecutionGroupLoader from "./ExecutionGroupLoader";
14
+ import ExecutionGroups from "./ExecutionGroups";
13
15
  import ImplementationsRoutes from "./ImplementationsRoutes";
14
16
  import RulesRoutes from "./RulesRoutes";
15
17
 
18
+ const ExecutionsRoutes = () => (
19
+ <Switch>
20
+ <Route
21
+ path={EXECUTION_GROUP}
22
+ render={() => (
23
+ <>
24
+ <ExecutionGroupLoader />
25
+ <ExecutionGroup />
26
+ </>
27
+ )}
28
+ />
29
+ <Route path={EXECUTION_GROUPS} render={() => <ExecutionGroups />} />
30
+ </Switch>
31
+ );
32
+
16
33
  const QualityRoutes = () => {
17
34
  const authorized = useAuthorized("quality");
18
35
 
19
36
  return (
20
37
  <>
21
38
  <Route
22
- path={EXECUTION_GROUP}
23
- render={() =>
24
- authorized ? (
25
- <>
26
- <ExecutionGroupLoader />
27
- <ExecutionGroup />
28
- </>
29
- ) : (
30
- <Unauthorized />
31
- )
32
- }
39
+ path={EXECUTION_GROUPS}
40
+ render={() => (authorized ? <ExecutionsRoutes /> : <Unauthorized />)}
33
41
  />
34
42
  <Route
35
43
  path={RULES}
@@ -1,54 +1,51 @@
1
1
  import React from "react";
2
- import { shallow } from "enzyme";
3
- import { intl } from "@truedat/test/intl-stub";
4
- import { ExecutionGroup } from "../ExecutionGroup";
2
+ import { render } from "@truedat/test/render";
3
+ import ExecutionGroup from "../ExecutionGroup";
5
4
 
6
- // workaround for enzyme issue with React.useContext
7
- // see https://github.com/airbnb/enzyme/issues/2176#issuecomment-532361526
8
- jest.spyOn(React, "useContext").mockImplementation(() => intl);
9
-
10
- describe("<ExecutionGroup />", () => {
11
- const rule = {
12
- id: 1,
13
- name: "rule_name",
14
- };
15
- const implementation = {
5
+ const rule = {
6
+ id: 1,
7
+ name: "rule_name",
8
+ };
9
+ const implementation = {
10
+ id: 1,
11
+ implementation_key: "foo",
12
+ rule_id: 1,
13
+ result_type: "percentage",
14
+ minimum: 10,
15
+ goal: 20,
16
+ };
17
+ const result = {
18
+ id: 1,
19
+ implementation_key: "foo",
20
+ date: "2020-01-01 00:00:00Z",
21
+ result: 30,
22
+ records: 100,
23
+ errors: 30,
24
+ result_type: "percentage",
25
+ minimum: 10,
26
+ goal: 20,
27
+ };
28
+ const quality_events = [
29
+ {
30
+ inserted_at: "2020-01-01 00:00:00Z",
31
+ execution_id: 1,
16
32
  id: 1,
17
- implementation_key: "foo",
18
- rule_id: 1,
19
- result_type: "percentage",
20
- minimum: 10,
21
- goal: 20,
22
- };
23
- const result = {
24
- id: 1,
25
- implementation_key: "foo",
26
- date: "2020-01-01 00:00:00Z",
27
- result: 30,
28
- records: 100,
29
- errors: 30,
30
- result_type: "percentage",
31
- minimum: 10,
32
- goal: 20,
33
- };
34
- const quality_events = [
35
- {
36
- inserted_at: "2020-01-01 00:00:00Z",
37
- execution_id: 1,
38
- id: 1,
39
- type: "SUCCESS",
40
- message: "",
41
- },
42
- ];
43
- const executions = [
44
- { id: 1, _embedded: { rule, implementation, result, quality_events } },
45
- ];
46
- const props = {
47
- executionGroup: { id: 1, _embedded: { executions } },
48
- };
33
+ type: "SUCCESS",
34
+ message: "",
35
+ },
36
+ ];
37
+ const executions = [
38
+ { id: 1, _embedded: { rule, implementation, result, quality_events } },
39
+ ];
40
+ const state = {
41
+ executionGroup: { id: 1, _embedded: { executions } },
42
+ executionGroupsLoading: false,
43
+ };
44
+ const renderOpts = { state };
49
45
 
46
+ describe("<ExecutionGroup />", () => {
50
47
  it("matches the latest snapshot", () => {
51
- const wrapper = shallow(<ExecutionGroup {...props} />);
52
- expect(wrapper).toMatchSnapshot();
48
+ const { container } = render(<ExecutionGroup />, renderOpts);
49
+ expect(container).toMatchSnapshot();
53
50
  });
54
51
  });
@@ -0,0 +1,11 @@
1
+ import React from "react";
2
+ import { render } from "@truedat/test/render";
3
+ import ExecutionGroupBreadcrumbs from "../ExecutionGroupBreadcrumbs";
4
+
5
+ describe("<ExecutionGroupBreadcrumbs />", () => {
6
+ it("matches the latest snapshot", () => {
7
+ const props = { timestamp: "2022-02-02T12:34:56.789Z" };
8
+ const { container } = render(<ExecutionGroupBreadcrumbs {...props} />);
9
+ expect(container).toMatchSnapshot();
10
+ });
11
+ });
@@ -0,0 +1,30 @@
1
+ import React from "react";
2
+ import { waitFor } from "@testing-library/react";
3
+ import { render } from "@truedat/test/render";
4
+ import { multipleTemplatesMock } from "@truedat/test/mocks";
5
+ import ExecutionGroupContent from "../ExecutionGroupContent";
6
+
7
+ const renderOpts = {
8
+ fallback: "lazy",
9
+ mocks: [multipleTemplatesMock({ scope: "qe" })],
10
+ state: {},
11
+ };
12
+
13
+ describe("<ExecutionGroupContent />", () => {
14
+ const content = { field1: "foo" };
15
+
16
+ it("matches the latest snapshot and renders dynamic content", async () => {
17
+ const { container, queryByText } = render(
18
+ <ExecutionGroupContent content={content} />,
19
+ renderOpts
20
+ );
21
+ await waitFor(() => expect(queryByText(/lazy/i)).not.toBeInTheDocument());
22
+ await waitFor(() =>
23
+ expect(queryByText(/loading/i)).not.toBeInTheDocument()
24
+ );
25
+
26
+ expect(container).toMatchSnapshot();
27
+ expect(queryByText("field1")).toBeInTheDocument();
28
+ expect(queryByText("foo")).toBeInTheDocument();
29
+ });
30
+ });
@@ -0,0 +1,11 @@
1
+ import React from "react";
2
+ import { render } from "@truedat/test/render";
3
+ import ExecutionGroupLink from "../ExecutionGroupLink";
4
+
5
+ describe("<ExecutionGroupLink />", () => {
6
+ it("matches the latest snapshot", () => {
7
+ const props = { id: "123", insertedAt: "2022-02-02T12:34:56.789Z" };
8
+ const { container } = render(<ExecutionGroupLink {...props} />);
9
+ expect(container).toMatchSnapshot();
10
+ });
11
+ });