@truedat/qx 8.2.0 → 8.2.2
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.
- package/package.json +3 -3
- package/src/components/qualityControls/ConceptLinkNew.js +38 -0
- package/src/components/qualityControls/ConceptLinks.js +15 -0
- package/src/components/qualityControls/QualityControlActions.js +7 -1
- package/src/components/qualityControls/QualityControlHeader.js +49 -2
- package/src/components/qualityControls/QualityControlRoutes.js +13 -2
- package/src/components/qualityControls/QualityControlTabs.js +51 -10
- package/src/components/qualityControls/StructureLinkNew.js +38 -0
- package/src/components/qualityControls/StructureLinks.js +18 -0
- package/src/components/qualityControls/__tests__/ConceptLinkNew.spec.js +63 -0
- package/src/components/qualityControls/__tests__/ConceptLinks.spec.js +43 -0
- package/src/components/qualityControls/__tests__/QualityControlHeader.spec.js +150 -0
- package/src/components/qualityControls/__tests__/StructureLinkNew.spec.js +93 -0
- package/src/components/qualityControls/__tests__/StructureLinks.spec.js +79 -0
- package/src/components/qualityControls/__tests__/__snapshots__/ConceptLinkNew.spec.js.snap +14 -0
- package/src/components/qualityControls/__tests__/__snapshots__/ConceptLinks.spec.js.snap +9 -0
- package/src/components/qualityControls/__tests__/__snapshots__/StructureLinkNew.spec.js.snap +36 -0
- package/src/components/qualityControls/__tests__/__snapshots__/StructureLinks.spec.js.snap +26 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@truedat/qx",
|
|
3
|
-
"version": "8.2.
|
|
3
|
+
"version": "8.2.2",
|
|
4
4
|
"description": "Truedat Web Quality Experience package",
|
|
5
5
|
"sideEffects": false,
|
|
6
6
|
"module": "src/index.js",
|
|
@@ -53,7 +53,7 @@
|
|
|
53
53
|
"@testing-library/jest-dom": "^6.6.3",
|
|
54
54
|
"@testing-library/react": "^16.3.0",
|
|
55
55
|
"@testing-library/user-event": "^14.6.1",
|
|
56
|
-
"@truedat/test": "8.2.
|
|
56
|
+
"@truedat/test": "8.2.2",
|
|
57
57
|
"identity-obj-proxy": "^3.0.0",
|
|
58
58
|
"jest": "^29.7.0",
|
|
59
59
|
"redux-saga-test-plan": "^4.0.6"
|
|
@@ -86,5 +86,5 @@
|
|
|
86
86
|
"semantic-ui-react": "^3.0.0-beta.2",
|
|
87
87
|
"swr": "^2.3.3"
|
|
88
88
|
},
|
|
89
|
-
"gitHead": "
|
|
89
|
+
"gitHead": "ef27133e5868b9aad2d95d7f2c69f36063d4327b"
|
|
90
90
|
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { lazy, use } from "react";
|
|
2
|
+
import { Header } from "semantic-ui-react";
|
|
3
|
+
import { useIntl } from "react-intl";
|
|
4
|
+
import QxContext from "../QxContext";
|
|
5
|
+
import { makeTagOptionsSelector } from "@truedat/core/selectors";
|
|
6
|
+
import { linkTo } from "@truedat/core/routes";
|
|
7
|
+
|
|
8
|
+
const ConceptLinkForm = lazy(
|
|
9
|
+
() => import("@truedat/lm/components/ConceptLinkForm")
|
|
10
|
+
);
|
|
11
|
+
|
|
12
|
+
const selectTagOptions = makeTagOptionsSelector("quality_control");
|
|
13
|
+
|
|
14
|
+
const ConceptLinkNew = () => {
|
|
15
|
+
const { formatMessage } = useIntl();
|
|
16
|
+
const { qualityControl } = use(QxContext);
|
|
17
|
+
return (
|
|
18
|
+
<>
|
|
19
|
+
<Header
|
|
20
|
+
as="h4"
|
|
21
|
+
content={formatMessage({
|
|
22
|
+
id: "quality_controls.relation.new.concept.header",
|
|
23
|
+
})}
|
|
24
|
+
/>
|
|
25
|
+
<ConceptLinkForm
|
|
26
|
+
targetId={qualityControl.id}
|
|
27
|
+
targetType="quality_control"
|
|
28
|
+
redirectUrl={linkTo.QUALITY_CONTROL_CONCEPTS({
|
|
29
|
+
id: qualityControl.id,
|
|
30
|
+
version: qualityControl.version,
|
|
31
|
+
})}
|
|
32
|
+
selectTagOptions={selectTagOptions}
|
|
33
|
+
/>
|
|
34
|
+
</>
|
|
35
|
+
);
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
export default ConceptLinkNew;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { lazy, use } from "react";
|
|
2
|
+
import QxContext from "../QxContext";
|
|
3
|
+
|
|
4
|
+
const QualityControlConcepts = lazy(
|
|
5
|
+
() => import("@truedat/lm/components/QualityControlConcepts")
|
|
6
|
+
);
|
|
7
|
+
|
|
8
|
+
const ConceptLinks = () => {
|
|
9
|
+
const { qualityControl, actions } = use(QxContext);
|
|
10
|
+
return (
|
|
11
|
+
<QualityControlConcepts qualityControl={qualityControl} actions={actions} />
|
|
12
|
+
);
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export default ConceptLinks;
|
|
@@ -114,7 +114,13 @@ export default function QualityControlActions() {
|
|
|
114
114
|
|
|
115
115
|
const availableActions = _.flow(
|
|
116
116
|
_.reject((action) =>
|
|
117
|
-
_.includes(action, [
|
|
117
|
+
_.includes(action, [
|
|
118
|
+
"delete_score",
|
|
119
|
+
"update_main",
|
|
120
|
+
"execute",
|
|
121
|
+
"link_to_business_concept",
|
|
122
|
+
"link_to_data_structure",
|
|
123
|
+
])
|
|
118
124
|
),
|
|
119
125
|
_.map((action) => {
|
|
120
126
|
const actionIcon = {
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import _ from "lodash/fp";
|
|
2
|
+
import { useEffect, useState } from "react";
|
|
2
3
|
import PropTypes from "prop-types";
|
|
3
4
|
import { FormattedMessage } from "react-intl";
|
|
4
5
|
import { useParams, Outlet } from "react-router";
|
|
5
6
|
import { Header, Icon, Segment, Grid, Label } from "semantic-ui-react";
|
|
6
7
|
import { colorForStatus } from "@truedat/core/services/statusColor";
|
|
8
|
+
import { useRelations } from "@truedat/lm/hooks/useRelations";
|
|
7
9
|
|
|
8
10
|
import { useQualityControl } from "../../hooks/useQualityControls";
|
|
9
11
|
import QxContext from "../QxContext";
|
|
@@ -23,10 +25,51 @@ export default function QualityControlHeader({ children }) {
|
|
|
23
25
|
? ["play", "green"]
|
|
24
26
|
: ["pause", "orange"];
|
|
25
27
|
|
|
28
|
+
const qualityControlId = qualityControl?.id;
|
|
29
|
+
const { trigger: searchRelations, isMutating: searchingRelations } =
|
|
30
|
+
useRelations();
|
|
31
|
+
const [conceptLinkCount, setConceptLinkCount] = useState(0);
|
|
32
|
+
const [structureLinkCount, setStructureLinkCount] = useState(0);
|
|
33
|
+
|
|
34
|
+
useEffect(() => {
|
|
35
|
+
if (qualityControlId) {
|
|
36
|
+
const conceptLinksPayload = {
|
|
37
|
+
must: {
|
|
38
|
+
target_id: qualityControlId,
|
|
39
|
+
target_type: "quality_control",
|
|
40
|
+
source_type: "business_concept",
|
|
41
|
+
},
|
|
42
|
+
size: 0,
|
|
43
|
+
};
|
|
44
|
+
const structureLinksPayload = {
|
|
45
|
+
must: {
|
|
46
|
+
source_id: qualityControlId,
|
|
47
|
+
source_type: "quality_control",
|
|
48
|
+
target_type: "data_structure",
|
|
49
|
+
},
|
|
50
|
+
size: 0,
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
Promise.all([
|
|
54
|
+
searchRelations(conceptLinksPayload),
|
|
55
|
+
searchRelations(structureLinksPayload),
|
|
56
|
+
]).then(([conceptResponse, structureResponse]) => {
|
|
57
|
+
const conceptCount = parseInt(
|
|
58
|
+
conceptResponse?.headers?.["x-total-count"] || "0"
|
|
59
|
+
);
|
|
60
|
+
const structureCount = parseInt(
|
|
61
|
+
structureResponse?.headers?.["x-total-count"] || "0"
|
|
62
|
+
);
|
|
63
|
+
setConceptLinkCount(conceptCount);
|
|
64
|
+
setStructureLinkCount(structureCount);
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
}, [qualityControlId, searchRelations]);
|
|
68
|
+
|
|
26
69
|
return (
|
|
27
70
|
<QxContext value={context}>
|
|
28
71
|
<QualityControlCrumbs qualityControl={qualityControl} />
|
|
29
|
-
<Segment loading={loading}>
|
|
72
|
+
<Segment loading={loading || searchingRelations}>
|
|
30
73
|
{!_.isEmpty(qualityControl) ? (
|
|
31
74
|
<>
|
|
32
75
|
<Grid>
|
|
@@ -70,7 +113,11 @@ export default function QualityControlHeader({ children }) {
|
|
|
70
113
|
id={`quality_control.status.${qualityControl.status}`}
|
|
71
114
|
/>
|
|
72
115
|
</Label>
|
|
73
|
-
<QualityControlTabs
|
|
116
|
+
<QualityControlTabs
|
|
117
|
+
conceptLinkCount={conceptLinkCount}
|
|
118
|
+
structureLinkCount={structureLinkCount}
|
|
119
|
+
actions={actions}
|
|
120
|
+
/>
|
|
74
121
|
{children ? children : <Outlet />}
|
|
75
122
|
</>
|
|
76
123
|
) : null}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Route, Routes, useParams } from "react-router";
|
|
2
2
|
import { SearchContextProvider } from "@truedat/core/search/SearchContext";
|
|
3
|
-
// This is only for information purposes,
|
|
3
|
+
// This is only for information purposes, don't remove
|
|
4
4
|
import {
|
|
5
5
|
QUALITY_CONTROLS_DEPRECATED,
|
|
6
6
|
QUALITY_CONTROLS_DRAFTS,
|
|
@@ -12,7 +12,6 @@ import {
|
|
|
12
12
|
QUALITY_CONTROL_HISTORY,
|
|
13
13
|
QUALITY_CONTROL_SCORES,
|
|
14
14
|
} from "@truedat/core/routes";
|
|
15
|
-
|
|
16
15
|
import {
|
|
17
16
|
useQualityControlsSearch,
|
|
18
17
|
useQualityControlsFilters,
|
|
@@ -25,6 +24,10 @@ import QualityControlScores from "./QualityControlScores";
|
|
|
25
24
|
import NewQualityControl from "./NewQualityControl";
|
|
26
25
|
import EditQualityControl from "./EditQualityControl";
|
|
27
26
|
import NewDraftQualityControl from "./NewDraftQualityControl";
|
|
27
|
+
import ConceptLinks from "./ConceptLinks";
|
|
28
|
+
import ConceptLinkNew from "./ConceptLinkNew";
|
|
29
|
+
import StructureLinks from "./StructureLinks";
|
|
30
|
+
import StructureLinkNew from "./StructureLinkNew";
|
|
28
31
|
import { ScoreContextProvider } from "../scores/ScoreContext";
|
|
29
32
|
import QualityControlEvents from "./QualityControlEvents";
|
|
30
33
|
|
|
@@ -94,6 +97,14 @@ export default function QualityControlRoutes() {
|
|
|
94
97
|
|
|
95
98
|
<Route path=":id/version/:version" element={<QualityControlHeader />}>
|
|
96
99
|
<Route index element={<QualityControl />} />
|
|
100
|
+
<Route path="links/concepts">
|
|
101
|
+
<Route index element={<ConceptLinks />} />
|
|
102
|
+
<Route path="new" element={<ConceptLinkNew />} />
|
|
103
|
+
</Route>
|
|
104
|
+
<Route path="links/structures">
|
|
105
|
+
<Route index element={<StructureLinks />} />
|
|
106
|
+
<Route path="new" element={<StructureLinkNew />} />
|
|
107
|
+
</Route>
|
|
97
108
|
<Route path="history" element={<QualityControlHistory />} />
|
|
98
109
|
<Route path="scores" element={<LoaderQualityControlScores />} />
|
|
99
110
|
<Route path="events" element={<QualityControlEvents />} />
|
|
@@ -1,39 +1,80 @@
|
|
|
1
|
+
import _ from "lodash/fp";
|
|
2
|
+
import PropTypes from "prop-types";
|
|
1
3
|
import { Menu } from "semantic-ui-react";
|
|
2
4
|
import { Link, useLocation } from "react-router";
|
|
3
5
|
import { useParams } from "react-router";
|
|
4
6
|
import { FormattedMessage } from "react-intl";
|
|
5
7
|
import { linkTo } from "@truedat/core/routes";
|
|
6
8
|
|
|
7
|
-
export default function QualityControlTabs(
|
|
9
|
+
export default function QualityControlTabs({
|
|
10
|
+
actions,
|
|
11
|
+
conceptLinkCount = 0,
|
|
12
|
+
structureLinkCount = 0,
|
|
13
|
+
}) {
|
|
8
14
|
const { pathname } = useLocation();
|
|
9
15
|
const urlParams = useParams();
|
|
16
|
+
const canCreateConceptLink = _.includes("link_to_business_concept")(actions);
|
|
17
|
+
const canCreateStructureLink = _.includes("link_to_data_structure")(actions);
|
|
10
18
|
|
|
11
19
|
const tabs = [
|
|
12
20
|
{
|
|
13
|
-
url: linkTo.QUALITY_CONTROL(urlParams),
|
|
21
|
+
url: [linkTo.QUALITY_CONTROL(urlParams)],
|
|
14
22
|
text: "quality_control.tabs.quality_control",
|
|
23
|
+
display: true,
|
|
15
24
|
},
|
|
16
25
|
{
|
|
17
|
-
url:
|
|
26
|
+
url: [
|
|
27
|
+
linkTo.QUALITY_CONTROL_CONCEPTS(urlParams),
|
|
28
|
+
linkTo.QUALITY_CONTROL_CONCEPT_LINKS_NEW(urlParams),
|
|
29
|
+
],
|
|
30
|
+
text: "quality_control.tabs.concept_links",
|
|
31
|
+
display: conceptLinkCount !== 0 || canCreateConceptLink,
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
url: [
|
|
35
|
+
linkTo.QUALITY_CONTROL_STRUCTURES(urlParams),
|
|
36
|
+
linkTo.QUALITY_CONTROL_STRUCTURE_LINKS_NEW(urlParams),
|
|
37
|
+
],
|
|
38
|
+
text: "quality_control.tabs.structure_links",
|
|
39
|
+
display: structureLinkCount !== 0 || canCreateStructureLink,
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
url: [linkTo.QUALITY_CONTROL_HISTORY(urlParams)],
|
|
18
43
|
text: "quality_control.tabs.history",
|
|
44
|
+
display: true,
|
|
19
45
|
},
|
|
20
46
|
{
|
|
21
|
-
url: linkTo.QUALITY_CONTROL_SCORES(urlParams),
|
|
47
|
+
url: [linkTo.QUALITY_CONTROL_SCORES(urlParams)],
|
|
22
48
|
text: "quality_control.tabs.scores",
|
|
49
|
+
display: true,
|
|
23
50
|
},
|
|
24
51
|
{
|
|
25
|
-
url: linkTo.QUALITY_CONTROL_EVENTS(urlParams),
|
|
52
|
+
url: [linkTo.QUALITY_CONTROL_EVENTS(urlParams)],
|
|
26
53
|
text: "quality_control.tabs.events",
|
|
54
|
+
display: true,
|
|
27
55
|
},
|
|
28
56
|
];
|
|
29
57
|
|
|
30
58
|
return (
|
|
31
59
|
<Menu attached="top" pointing secondary tabular>
|
|
32
|
-
{tabs
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
60
|
+
{tabs
|
|
61
|
+
.filter(({ display }) => display)
|
|
62
|
+
.map(({ url, text }, key) => (
|
|
63
|
+
<Menu.Item
|
|
64
|
+
key={key}
|
|
65
|
+
active={_.includes(pathname)(url)}
|
|
66
|
+
as={Link}
|
|
67
|
+
to={url[0]}
|
|
68
|
+
>
|
|
69
|
+
<FormattedMessage id={text} />
|
|
70
|
+
</Menu.Item>
|
|
71
|
+
))}
|
|
37
72
|
</Menu>
|
|
38
73
|
);
|
|
39
74
|
}
|
|
75
|
+
|
|
76
|
+
QualityControlTabs.propTypes = {
|
|
77
|
+
actions: PropTypes.array,
|
|
78
|
+
conceptLinkCount: PropTypes.number,
|
|
79
|
+
structureLinkCount: PropTypes.number,
|
|
80
|
+
};
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { lazy, use } from "react";
|
|
2
|
+
import { Header } from "semantic-ui-react";
|
|
3
|
+
import { useIntl } from "react-intl";
|
|
4
|
+
import QxContext from "../QxContext";
|
|
5
|
+
import { makeTagOptionsSelector } from "@truedat/core/selectors";
|
|
6
|
+
import { linkTo } from "@truedat/core/routes";
|
|
7
|
+
|
|
8
|
+
const StructureLinkForm = lazy(
|
|
9
|
+
() => import("@truedat/lm/components/StructureLinkForm")
|
|
10
|
+
);
|
|
11
|
+
|
|
12
|
+
const selectTagOptions = makeTagOptionsSelector("quality_control");
|
|
13
|
+
|
|
14
|
+
const StructureLinkNew = () => {
|
|
15
|
+
const { formatMessage } = useIntl();
|
|
16
|
+
const { qualityControl } = use(QxContext);
|
|
17
|
+
return (
|
|
18
|
+
<>
|
|
19
|
+
<Header
|
|
20
|
+
as="h4"
|
|
21
|
+
content={formatMessage({
|
|
22
|
+
id: "quality_controls.relation.new.structure.header",
|
|
23
|
+
})}
|
|
24
|
+
/>
|
|
25
|
+
<StructureLinkForm
|
|
26
|
+
sourceId={qualityControl.id}
|
|
27
|
+
sourceType="quality_control"
|
|
28
|
+
redirectUrl={linkTo.QUALITY_CONTROL_STRUCTURES({
|
|
29
|
+
id: qualityControl.id,
|
|
30
|
+
version: qualityControl.version,
|
|
31
|
+
})}
|
|
32
|
+
selectTagOptions={selectTagOptions}
|
|
33
|
+
/>
|
|
34
|
+
</>
|
|
35
|
+
);
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
export default StructureLinkNew;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { lazy, use } from "react";
|
|
2
|
+
import QxContext from "../QxContext";
|
|
3
|
+
|
|
4
|
+
const QualityControlStructures = lazy(
|
|
5
|
+
() => import("@truedat/lm/components/QualityControlStructures")
|
|
6
|
+
);
|
|
7
|
+
|
|
8
|
+
const StructureLinks = () => {
|
|
9
|
+
const { qualityControl, actions } = use(QxContext);
|
|
10
|
+
return (
|
|
11
|
+
<QualityControlStructures
|
|
12
|
+
qualityControl={qualityControl}
|
|
13
|
+
actions={actions}
|
|
14
|
+
/>
|
|
15
|
+
);
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export default StructureLinks;
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { render, waitForLoad } from "@truedat/test/render";
|
|
2
|
+
import ConceptLinkNew from "../ConceptLinkNew";
|
|
3
|
+
import QxContext from "../../QxContext";
|
|
4
|
+
|
|
5
|
+
jest.mock("react-intl", () => ({
|
|
6
|
+
...jest.requireActual("react-intl"),
|
|
7
|
+
useIntl: () => ({
|
|
8
|
+
formatMessage: ({ id }) => id,
|
|
9
|
+
}),
|
|
10
|
+
}));
|
|
11
|
+
|
|
12
|
+
jest.mock("@truedat/core/selectors", () => ({
|
|
13
|
+
makeTagOptionsSelector: jest.fn(() => jest.fn(() => [])),
|
|
14
|
+
}));
|
|
15
|
+
|
|
16
|
+
jest.mock("@truedat/core/routes", () => ({
|
|
17
|
+
linkTo: {
|
|
18
|
+
QUALITY_CONTROL_CONCEPTS: jest.fn(
|
|
19
|
+
({ id, version }) =>
|
|
20
|
+
`/qualityControls/${id}/version/${version}/links/concepts`
|
|
21
|
+
),
|
|
22
|
+
},
|
|
23
|
+
}));
|
|
24
|
+
|
|
25
|
+
jest.mock("@truedat/lm/components/ConceptLinkForm", () =>
|
|
26
|
+
jest.fn(() => <div>ConceptLinkForm</div>)
|
|
27
|
+
);
|
|
28
|
+
|
|
29
|
+
const qualityControl = {
|
|
30
|
+
id: 10,
|
|
31
|
+
version: 1,
|
|
32
|
+
name: "Test Quality Control",
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
const context = {
|
|
36
|
+
qualityControl,
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
describe("<ConceptLinkNew />", () => {
|
|
40
|
+
it("matches the latest snapshot", async () => {
|
|
41
|
+
const rendered = render(
|
|
42
|
+
<QxContext value={context}>
|
|
43
|
+
<ConceptLinkNew />
|
|
44
|
+
</QxContext>
|
|
45
|
+
);
|
|
46
|
+
await waitForLoad(rendered);
|
|
47
|
+
expect(rendered.container).toMatchSnapshot();
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it("renders header and ConceptLinkForm", async () => {
|
|
51
|
+
const rendered = render(
|
|
52
|
+
<QxContext value={context}>
|
|
53
|
+
<ConceptLinkNew />
|
|
54
|
+
</QxContext>
|
|
55
|
+
);
|
|
56
|
+
await waitForLoad(rendered);
|
|
57
|
+
|
|
58
|
+
expect(
|
|
59
|
+
rendered.getByText("quality_controls.relation.new.concept.header")
|
|
60
|
+
).toBeInTheDocument();
|
|
61
|
+
expect(rendered.getByText("ConceptLinkForm")).toBeInTheDocument();
|
|
62
|
+
});
|
|
63
|
+
});
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { render, waitForLoad } from "@truedat/test/render";
|
|
2
|
+
import ConceptLinks from "../ConceptLinks";
|
|
3
|
+
import QxContext from "../../QxContext";
|
|
4
|
+
|
|
5
|
+
jest.mock("@truedat/lm/components/QualityControlConcepts", () =>
|
|
6
|
+
jest.fn(() => <div>QualityControlConcepts</div>)
|
|
7
|
+
);
|
|
8
|
+
|
|
9
|
+
const qualityControl = {
|
|
10
|
+
id: 10,
|
|
11
|
+
version: 1,
|
|
12
|
+
name: "Test Quality Control",
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
const actions = ["link_to_business_concept"];
|
|
16
|
+
|
|
17
|
+
const context = {
|
|
18
|
+
qualityControl,
|
|
19
|
+
actions,
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
describe("<ConceptLinks />", () => {
|
|
23
|
+
it("matches the latest snapshot", async () => {
|
|
24
|
+
const rendered = render(
|
|
25
|
+
<QxContext value={context}>
|
|
26
|
+
<ConceptLinks />
|
|
27
|
+
</QxContext>
|
|
28
|
+
);
|
|
29
|
+
await waitForLoad(rendered);
|
|
30
|
+
expect(rendered.container).toMatchSnapshot();
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it("renders QualityControlConcepts", async () => {
|
|
34
|
+
const rendered = render(
|
|
35
|
+
<QxContext value={context}>
|
|
36
|
+
<ConceptLinks />
|
|
37
|
+
</QxContext>
|
|
38
|
+
);
|
|
39
|
+
await waitForLoad(rendered);
|
|
40
|
+
|
|
41
|
+
expect(rendered.getByText("QualityControlConcepts")).toBeInTheDocument();
|
|
42
|
+
});
|
|
43
|
+
});
|
|
@@ -1,13 +1,23 @@
|
|
|
1
|
+
import { waitFor } from "@testing-library/react";
|
|
1
2
|
import { render, waitForLoad } from "@truedat/test/render";
|
|
2
3
|
import { useQualityControl } from "@truedat/qx/hooks/useQualityControls";
|
|
3
4
|
import QualityControlHeader from "../QualityControlHeader";
|
|
4
5
|
import { qualityControlData } from "./__fixtures__/qualityControlHelper";
|
|
5
6
|
|
|
7
|
+
const mockSearchRelations = jest.fn();
|
|
8
|
+
|
|
6
9
|
jest.mock("react-router", () => ({
|
|
7
10
|
...jest.requireActual("react-router"),
|
|
8
11
|
useParams: () => ({ id: "8", version: "1" }),
|
|
9
12
|
}));
|
|
10
13
|
|
|
14
|
+
jest.mock("@truedat/lm/hooks/useRelations", () => ({
|
|
15
|
+
useRelations: () => ({
|
|
16
|
+
trigger: mockSearchRelations,
|
|
17
|
+
isMutating: false,
|
|
18
|
+
}),
|
|
19
|
+
}));
|
|
20
|
+
|
|
11
21
|
jest.mock("@truedat/qx/hooks/useQualityControls", () => {
|
|
12
22
|
const { qualityControlData } = jest.requireActual(
|
|
13
23
|
"@truedat/qx/components/qualityControls/__tests__/__fixtures__/qualityControlHelper"
|
|
@@ -49,6 +59,34 @@ jest.mock("@truedat/qx/hooks/useQualityControls", () => {
|
|
|
49
59
|
});
|
|
50
60
|
|
|
51
61
|
describe("<QualityControlHeader />", () => {
|
|
62
|
+
const defaultMockImplementation = () => ({
|
|
63
|
+
data: {
|
|
64
|
+
data: qualityControlData(),
|
|
65
|
+
_actions: [
|
|
66
|
+
"deprecate",
|
|
67
|
+
"create_draft",
|
|
68
|
+
"toggle_active",
|
|
69
|
+
"delete_score",
|
|
70
|
+
"update_main",
|
|
71
|
+
"execute",
|
|
72
|
+
"delete",
|
|
73
|
+
],
|
|
74
|
+
},
|
|
75
|
+
loading: false,
|
|
76
|
+
mutate: jest.fn(),
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
beforeEach(() => {
|
|
80
|
+
useQualityControl.mockImplementation(defaultMockImplementation);
|
|
81
|
+
mockSearchRelations.mockResolvedValue({
|
|
82
|
+
headers: { "x-total-count": "5" },
|
|
83
|
+
});
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
afterEach(() => {
|
|
87
|
+
jest.clearAllMocks();
|
|
88
|
+
});
|
|
89
|
+
|
|
52
90
|
it("matches the latest snapshot with control quality active", async () => {
|
|
53
91
|
const rendered = render(<QualityControlHeader />);
|
|
54
92
|
await waitForLoad(rendered);
|
|
@@ -78,4 +116,116 @@ describe("<QualityControlHeader />", () => {
|
|
|
78
116
|
|
|
79
117
|
expect(rendered.queryByText(".header")).not.toBeInTheDocument();
|
|
80
118
|
});
|
|
119
|
+
|
|
120
|
+
it("fetches concept and structure link counts", async () => {
|
|
121
|
+
useQualityControl.mockImplementation(() => ({
|
|
122
|
+
data: {
|
|
123
|
+
data: qualityControlData(),
|
|
124
|
+
_actions: [],
|
|
125
|
+
},
|
|
126
|
+
loading: false,
|
|
127
|
+
mutate: jest.fn(),
|
|
128
|
+
}));
|
|
129
|
+
|
|
130
|
+
mockSearchRelations
|
|
131
|
+
.mockResolvedValueOnce({
|
|
132
|
+
headers: { "x-total-count": "10" },
|
|
133
|
+
})
|
|
134
|
+
.mockResolvedValueOnce({
|
|
135
|
+
headers: { "x-total-count": "7" },
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
const rendered = render(<QualityControlHeader />);
|
|
139
|
+
await waitForLoad(rendered);
|
|
140
|
+
|
|
141
|
+
await waitFor(() => {
|
|
142
|
+
expect(mockSearchRelations).toHaveBeenCalledTimes(2);
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
expect(mockSearchRelations).toHaveBeenCalledWith({
|
|
146
|
+
must: {
|
|
147
|
+
target_id: qualityControlData().id,
|
|
148
|
+
target_type: "quality_control",
|
|
149
|
+
source_type: "business_concept",
|
|
150
|
+
},
|
|
151
|
+
size: 0,
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
expect(mockSearchRelations).toHaveBeenCalledWith({
|
|
155
|
+
must: {
|
|
156
|
+
source_id: qualityControlData().id,
|
|
157
|
+
source_type: "quality_control",
|
|
158
|
+
target_type: "data_structure",
|
|
159
|
+
},
|
|
160
|
+
size: 0,
|
|
161
|
+
});
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
it("handles missing x-total-count header gracefully", async () => {
|
|
165
|
+
useQualityControl.mockImplementation(() => ({
|
|
166
|
+
data: {
|
|
167
|
+
data: qualityControlData(),
|
|
168
|
+
_actions: [],
|
|
169
|
+
},
|
|
170
|
+
loading: false,
|
|
171
|
+
mutate: jest.fn(),
|
|
172
|
+
}));
|
|
173
|
+
|
|
174
|
+
mockSearchRelations.mockResolvedValue({
|
|
175
|
+
headers: {},
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
const rendered = render(<QualityControlHeader />);
|
|
179
|
+
await waitForLoad(rendered);
|
|
180
|
+
|
|
181
|
+
await waitFor(() => {
|
|
182
|
+
expect(mockSearchRelations).toHaveBeenCalled();
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
expect(rendered.container).toBeInTheDocument();
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
it("fetches counts with correct payloads and updates state", async () => {
|
|
189
|
+
useQualityControl.mockImplementation(() => ({
|
|
190
|
+
data: {
|
|
191
|
+
data: qualityControlData(),
|
|
192
|
+
_actions: [],
|
|
193
|
+
},
|
|
194
|
+
loading: false,
|
|
195
|
+
mutate: jest.fn(),
|
|
196
|
+
}));
|
|
197
|
+
|
|
198
|
+
mockSearchRelations
|
|
199
|
+
.mockResolvedValueOnce({
|
|
200
|
+
headers: { "x-total-count": "15" },
|
|
201
|
+
})
|
|
202
|
+
.mockResolvedValueOnce({
|
|
203
|
+
headers: { "x-total-count": "23" },
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
const rendered = render(<QualityControlHeader />);
|
|
207
|
+
await waitForLoad(rendered);
|
|
208
|
+
|
|
209
|
+
await waitFor(() => {
|
|
210
|
+
expect(mockSearchRelations).toHaveBeenCalledTimes(2);
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
expect(mockSearchRelations).toHaveBeenNthCalledWith(1, {
|
|
214
|
+
must: {
|
|
215
|
+
target_id: qualityControlData().id,
|
|
216
|
+
target_type: "quality_control",
|
|
217
|
+
source_type: "business_concept",
|
|
218
|
+
},
|
|
219
|
+
size: 0,
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
expect(mockSearchRelations).toHaveBeenNthCalledWith(2, {
|
|
223
|
+
must: {
|
|
224
|
+
source_id: qualityControlData().id,
|
|
225
|
+
source_type: "quality_control",
|
|
226
|
+
target_type: "data_structure",
|
|
227
|
+
},
|
|
228
|
+
size: 0,
|
|
229
|
+
});
|
|
230
|
+
});
|
|
81
231
|
});
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { render, waitForLoad } from "@truedat/test/render";
|
|
2
|
+
import StructureLinkNew from "../StructureLinkNew";
|
|
3
|
+
import QxContext from "../../QxContext";
|
|
4
|
+
|
|
5
|
+
jest.mock("react-intl", () => ({
|
|
6
|
+
...jest.requireActual("react-intl"),
|
|
7
|
+
useIntl: () => ({
|
|
8
|
+
formatMessage: ({ id }) => id,
|
|
9
|
+
}),
|
|
10
|
+
}));
|
|
11
|
+
|
|
12
|
+
jest.mock("@truedat/core/selectors", () => ({
|
|
13
|
+
makeTagOptionsSelector: jest.fn(() => jest.fn(() => [])),
|
|
14
|
+
}));
|
|
15
|
+
|
|
16
|
+
jest.mock("@truedat/core/routes", () => ({
|
|
17
|
+
linkTo: {
|
|
18
|
+
QUALITY_CONTROL_STRUCTURES: jest.fn(
|
|
19
|
+
({ id, version }) =>
|
|
20
|
+
`/qualityControls/${id}/version/${version}/links/structures`
|
|
21
|
+
),
|
|
22
|
+
},
|
|
23
|
+
}));
|
|
24
|
+
|
|
25
|
+
jest.mock("@truedat/lm/components/StructureLinkForm", () =>
|
|
26
|
+
jest.fn(({ sourceId, sourceType, redirectUrl, selectTagOptions }) => (
|
|
27
|
+
<div>
|
|
28
|
+
<div>StructureLinkForm</div>
|
|
29
|
+
<div data-testid="source-id">{sourceId}</div>
|
|
30
|
+
<div data-testid="source-type">{sourceType}</div>
|
|
31
|
+
<div data-testid="redirect-url">{redirectUrl}</div>
|
|
32
|
+
<div data-testid="has-select-tag-options">
|
|
33
|
+
{selectTagOptions ? "true" : "false"}
|
|
34
|
+
</div>
|
|
35
|
+
</div>
|
|
36
|
+
))
|
|
37
|
+
);
|
|
38
|
+
|
|
39
|
+
const qualityControl = {
|
|
40
|
+
id: 10,
|
|
41
|
+
version: 1,
|
|
42
|
+
name: "Test Quality Control",
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
const context = {
|
|
46
|
+
qualityControl,
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
describe("<StructureLinkNew />", () => {
|
|
50
|
+
it("matches the latest snapshot", async () => {
|
|
51
|
+
const rendered = render(
|
|
52
|
+
<QxContext value={context}>
|
|
53
|
+
<StructureLinkNew />
|
|
54
|
+
</QxContext>
|
|
55
|
+
);
|
|
56
|
+
await waitForLoad(rendered);
|
|
57
|
+
expect(rendered.container).toMatchSnapshot();
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
it("renders header and StructureLinkForm", async () => {
|
|
61
|
+
const rendered = render(
|
|
62
|
+
<QxContext value={context}>
|
|
63
|
+
<StructureLinkNew />
|
|
64
|
+
</QxContext>
|
|
65
|
+
);
|
|
66
|
+
await waitForLoad(rendered);
|
|
67
|
+
|
|
68
|
+
expect(
|
|
69
|
+
rendered.getByText("quality_controls.relation.new.structure.header")
|
|
70
|
+
).toBeInTheDocument();
|
|
71
|
+
expect(rendered.getByText("StructureLinkForm")).toBeInTheDocument();
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
it("passes correct props to StructureLinkForm", async () => {
|
|
75
|
+
const rendered = render(
|
|
76
|
+
<QxContext value={context}>
|
|
77
|
+
<StructureLinkNew />
|
|
78
|
+
</QxContext>
|
|
79
|
+
);
|
|
80
|
+
await waitForLoad(rendered);
|
|
81
|
+
|
|
82
|
+
expect(rendered.getByTestId("source-id").textContent).toBe("10");
|
|
83
|
+
expect(rendered.getByTestId("source-type").textContent).toBe(
|
|
84
|
+
"quality_control"
|
|
85
|
+
);
|
|
86
|
+
expect(rendered.getByTestId("redirect-url").textContent).toContain(
|
|
87
|
+
"/qualityControls/10/version/1/links/structures"
|
|
88
|
+
);
|
|
89
|
+
expect(rendered.getByTestId("has-select-tag-options").textContent).toBe(
|
|
90
|
+
"true"
|
|
91
|
+
);
|
|
92
|
+
});
|
|
93
|
+
});
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { render, waitForLoad } from "@truedat/test/render";
|
|
2
|
+
import StructureLinks from "../StructureLinks";
|
|
3
|
+
import QxContext from "../../QxContext";
|
|
4
|
+
|
|
5
|
+
jest.mock("@truedat/lm/components/QualityControlStructures", () =>
|
|
6
|
+
jest.fn(({ qualityControl, actions }) => (
|
|
7
|
+
<div>
|
|
8
|
+
<div>QualityControlStructures</div>
|
|
9
|
+
<div data-testid="quality-control-id">{qualityControl?.id}</div>
|
|
10
|
+
<div data-testid="quality-control-name">{qualityControl?.name}</div>
|
|
11
|
+
<div data-testid="actions-count">{actions?.length || 0}</div>
|
|
12
|
+
</div>
|
|
13
|
+
))
|
|
14
|
+
);
|
|
15
|
+
|
|
16
|
+
const qualityControl = {
|
|
17
|
+
id: 10,
|
|
18
|
+
version: 1,
|
|
19
|
+
name: "Test Quality Control",
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
const actions = ["link_to_data_structure"];
|
|
23
|
+
|
|
24
|
+
const context = {
|
|
25
|
+
qualityControl,
|
|
26
|
+
actions,
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
describe("<StructureLinks />", () => {
|
|
30
|
+
it("matches the latest snapshot", async () => {
|
|
31
|
+
const rendered = render(
|
|
32
|
+
<QxContext value={context}>
|
|
33
|
+
<StructureLinks />
|
|
34
|
+
</QxContext>
|
|
35
|
+
);
|
|
36
|
+
await waitForLoad(rendered);
|
|
37
|
+
expect(rendered.container).toMatchSnapshot();
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
it("renders QualityControlStructures", async () => {
|
|
41
|
+
const rendered = render(
|
|
42
|
+
<QxContext value={context}>
|
|
43
|
+
<StructureLinks />
|
|
44
|
+
</QxContext>
|
|
45
|
+
);
|
|
46
|
+
await waitForLoad(rendered);
|
|
47
|
+
|
|
48
|
+
expect(rendered.getByText("QualityControlStructures")).toBeInTheDocument();
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
it("passes qualityControl and actions to QualityControlStructures", async () => {
|
|
52
|
+
const rendered = render(
|
|
53
|
+
<QxContext value={context}>
|
|
54
|
+
<StructureLinks />
|
|
55
|
+
</QxContext>
|
|
56
|
+
);
|
|
57
|
+
await waitForLoad(rendered);
|
|
58
|
+
|
|
59
|
+
expect(rendered.getByTestId("quality-control-id").textContent).toBe("10");
|
|
60
|
+
expect(rendered.getByTestId("quality-control-name").textContent).toBe(
|
|
61
|
+
"Test Quality Control"
|
|
62
|
+
);
|
|
63
|
+
expect(rendered.getByTestId("actions-count").textContent).toBe("1");
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
it("handles missing actions gracefully", async () => {
|
|
67
|
+
const contextWithoutActions = {
|
|
68
|
+
qualityControl,
|
|
69
|
+
};
|
|
70
|
+
const rendered = render(
|
|
71
|
+
<QxContext value={contextWithoutActions}>
|
|
72
|
+
<StructureLinks />
|
|
73
|
+
</QxContext>
|
|
74
|
+
);
|
|
75
|
+
await waitForLoad(rendered);
|
|
76
|
+
|
|
77
|
+
expect(rendered.getByTestId("actions-count").textContent).toBe("0");
|
|
78
|
+
});
|
|
79
|
+
});
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
|
2
|
+
|
|
3
|
+
exports[`<ConceptLinkNew /> matches the latest snapshot 1`] = `
|
|
4
|
+
<div>
|
|
5
|
+
<h4
|
|
6
|
+
class="ui header"
|
|
7
|
+
>
|
|
8
|
+
quality_controls.relation.new.concept.header
|
|
9
|
+
</h4>
|
|
10
|
+
<div>
|
|
11
|
+
ConceptLinkForm
|
|
12
|
+
</div>
|
|
13
|
+
</div>
|
|
14
|
+
`;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
|
2
|
+
|
|
3
|
+
exports[`<StructureLinkNew /> matches the latest snapshot 1`] = `
|
|
4
|
+
<div>
|
|
5
|
+
<h4
|
|
6
|
+
class="ui header"
|
|
7
|
+
>
|
|
8
|
+
quality_controls.relation.new.structure.header
|
|
9
|
+
</h4>
|
|
10
|
+
<div>
|
|
11
|
+
<div>
|
|
12
|
+
StructureLinkForm
|
|
13
|
+
</div>
|
|
14
|
+
<div
|
|
15
|
+
data-testid="source-id"
|
|
16
|
+
>
|
|
17
|
+
10
|
|
18
|
+
</div>
|
|
19
|
+
<div
|
|
20
|
+
data-testid="source-type"
|
|
21
|
+
>
|
|
22
|
+
quality_control
|
|
23
|
+
</div>
|
|
24
|
+
<div
|
|
25
|
+
data-testid="redirect-url"
|
|
26
|
+
>
|
|
27
|
+
/qualityControls/10/version/1/links/structures
|
|
28
|
+
</div>
|
|
29
|
+
<div
|
|
30
|
+
data-testid="has-select-tag-options"
|
|
31
|
+
>
|
|
32
|
+
true
|
|
33
|
+
</div>
|
|
34
|
+
</div>
|
|
35
|
+
</div>
|
|
36
|
+
`;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
|
2
|
+
|
|
3
|
+
exports[`<StructureLinks /> matches the latest snapshot 1`] = `
|
|
4
|
+
<div>
|
|
5
|
+
<div>
|
|
6
|
+
<div>
|
|
7
|
+
QualityControlStructures
|
|
8
|
+
</div>
|
|
9
|
+
<div
|
|
10
|
+
data-testid="quality-control-id"
|
|
11
|
+
>
|
|
12
|
+
10
|
|
13
|
+
</div>
|
|
14
|
+
<div
|
|
15
|
+
data-testid="quality-control-name"
|
|
16
|
+
>
|
|
17
|
+
Test Quality Control
|
|
18
|
+
</div>
|
|
19
|
+
<div
|
|
20
|
+
data-testid="actions-count"
|
|
21
|
+
>
|
|
22
|
+
1
|
|
23
|
+
</div>
|
|
24
|
+
</div>
|
|
25
|
+
</div>
|
|
26
|
+
`;
|