@truedat/dd 5.2.8 → 5.2.9
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/CHANGELOG.md +6 -0
- package/package.json +5 -5
- package/src/api/queries.js +31 -0
- package/src/components/DictionaryRoutes.js +3 -0
- package/src/components/Task.js +149 -0
- package/src/components/Tasks.js +138 -0
- package/src/components/TasksRoutes.js +27 -0
- package/src/components/__tests__/Task.spec.js +44 -0
- package/src/components/__tests__/Tasks.spec.js +120 -0
- package/src/components/__tests__/TasksRoutes.spec.js +56 -0
- package/src/components/__tests__/__snapshots__/DictionaryRoutes.spec.js.snap +4 -0
- package/src/components/__tests__/__snapshots__/Task.spec.js.snap +298 -0
- package/src/components/__tests__/__snapshots__/Tasks.spec.js.snap +301 -0
- package/src/components/__tests__/__snapshots__/TasksRoutes.spec.js.snap +448 -0
- package/src/messages/en.js +17 -0
- package/src/messages/es.js +17 -0
package/CHANGELOG.md
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@truedat/dd",
|
|
3
|
-
"version": "5.2.
|
|
3
|
+
"version": "5.2.9",
|
|
4
4
|
"description": "Truedat Web Data Dictionary",
|
|
5
5
|
"sideEffects": false,
|
|
6
6
|
"jsnext:main": "src/index.js",
|
|
@@ -87,9 +87,9 @@
|
|
|
87
87
|
},
|
|
88
88
|
"dependencies": {
|
|
89
89
|
"@apollo/client": "^3.7.1",
|
|
90
|
-
"@truedat/auth": "5.2.
|
|
91
|
-
"@truedat/core": "5.2.
|
|
92
|
-
"@truedat/df": "5.2.
|
|
90
|
+
"@truedat/auth": "5.2.9",
|
|
91
|
+
"@truedat/core": "5.2.9",
|
|
92
|
+
"@truedat/df": "5.2.9",
|
|
93
93
|
"lodash": "^4.17.21",
|
|
94
94
|
"moment": "^2.29.4",
|
|
95
95
|
"path-to-regexp": "^1.7.0",
|
|
@@ -114,5 +114,5 @@
|
|
|
114
114
|
"react-dom": ">= 16.8.6 < 17",
|
|
115
115
|
"semantic-ui-react": ">= 2.0.3 < 2.2"
|
|
116
116
|
},
|
|
117
|
-
"gitHead": "
|
|
117
|
+
"gitHead": "39162d892066f21b9e894be20abfe38304b62f91"
|
|
118
118
|
}
|
package/src/api/queries.js
CHANGED
|
@@ -446,3 +446,34 @@ export const DATA_STRUCTURE_VERSION_QUERY = gql`
|
|
|
446
446
|
}
|
|
447
447
|
}
|
|
448
448
|
`;
|
|
449
|
+
|
|
450
|
+
export const TASKS_QUERY = gql`
|
|
451
|
+
query Tasks {
|
|
452
|
+
tasks {
|
|
453
|
+
id
|
|
454
|
+
index
|
|
455
|
+
status
|
|
456
|
+
started_at
|
|
457
|
+
last_message_at
|
|
458
|
+
count
|
|
459
|
+
processed
|
|
460
|
+
elapsed
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
`;
|
|
464
|
+
|
|
465
|
+
export const TASK_QUERY = gql`
|
|
466
|
+
query Task($id: String) {
|
|
467
|
+
task(id: $id) {
|
|
468
|
+
id
|
|
469
|
+
index
|
|
470
|
+
status
|
|
471
|
+
started_at
|
|
472
|
+
last_message_at
|
|
473
|
+
count
|
|
474
|
+
processed
|
|
475
|
+
memory_trace
|
|
476
|
+
elapsed
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
`;
|
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
STRUCTURE_TAGS,
|
|
12
12
|
STRUCTURE_TYPES,
|
|
13
13
|
SYSTEMS,
|
|
14
|
+
TASKS,
|
|
14
15
|
} from "@truedat/core/routes";
|
|
15
16
|
import PendingStructureNotes from "./PendingStructureNotes";
|
|
16
17
|
import ProfileExecution from "./ProfileExecution";
|
|
@@ -28,6 +29,7 @@ import StructureTypesRoutes from "./StructureTypesRoutes";
|
|
|
28
29
|
import SystemsRoutes from "./SystemsRoutes";
|
|
29
30
|
import SystemsLoader from "./SystemsLoader";
|
|
30
31
|
import TagsRoutes from "./TagsRoutes";
|
|
32
|
+
import TasksRoutes from "./TasksRoutes";
|
|
31
33
|
|
|
32
34
|
const TemplatesLoader = React.lazy(() =>
|
|
33
35
|
import("@truedat/df/templates/components/TemplatesLoader")
|
|
@@ -124,6 +126,7 @@ export const DictionaryRoutes = () => {
|
|
|
124
126
|
</>
|
|
125
127
|
)}
|
|
126
128
|
/>
|
|
129
|
+
<Route path={TASKS} render={() => <TasksRoutes />} />
|
|
127
130
|
</>
|
|
128
131
|
);
|
|
129
132
|
};
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import _ from "lodash/fp";
|
|
2
|
+
import React from "react";
|
|
3
|
+
import { useParams, Link } from "react-router-dom";
|
|
4
|
+
import { useQuery } from "@apollo/client";
|
|
5
|
+
import { FormattedMessage } from "react-intl";
|
|
6
|
+
import {
|
|
7
|
+
Breadcrumb,
|
|
8
|
+
Button,
|
|
9
|
+
Header,
|
|
10
|
+
Icon,
|
|
11
|
+
Grid,
|
|
12
|
+
Item,
|
|
13
|
+
Segment,
|
|
14
|
+
Progress,
|
|
15
|
+
Divider,
|
|
16
|
+
} from "semantic-ui-react";
|
|
17
|
+
import { Dimmer, Loader } from "semantic-ui-react";
|
|
18
|
+
import Moment from "react-moment";
|
|
19
|
+
import { TASKS } from "@truedat/core/routes";
|
|
20
|
+
import { TASK_QUERY } from "../api/queries";
|
|
21
|
+
|
|
22
|
+
function Task() {
|
|
23
|
+
const { id } = useParams();
|
|
24
|
+
const { loading, data, refetch } = useQuery(TASK_QUERY, {
|
|
25
|
+
fetchPolicy: "cache-and-network",
|
|
26
|
+
variables: { id },
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
const task = _.get("task")(data);
|
|
30
|
+
|
|
31
|
+
const memoryStats = _.flow(
|
|
32
|
+
_.get("memory_trace"),
|
|
33
|
+
_.reduce(
|
|
34
|
+
(stats, [_ts, memory]) => ({
|
|
35
|
+
max: Math.max(stats.max || -Infinity, memory),
|
|
36
|
+
min: Math.min(stats.min || Infinity, memory),
|
|
37
|
+
last: memory,
|
|
38
|
+
}),
|
|
39
|
+
{}
|
|
40
|
+
)
|
|
41
|
+
)(task);
|
|
42
|
+
|
|
43
|
+
const parseMemory = (value) => `${Math.round(value / 1024 / 1024)}MB`;
|
|
44
|
+
|
|
45
|
+
const properties = [
|
|
46
|
+
{ name: "task.index", content: task?.index },
|
|
47
|
+
{ name: "task.status", content: task?.status },
|
|
48
|
+
|
|
49
|
+
{
|
|
50
|
+
name: "task.started_at",
|
|
51
|
+
content: <Moment date={task?.started_at} format="YYYY-MM-DD HH:mm" />,
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
name: "task.last_message_at",
|
|
55
|
+
content: (
|
|
56
|
+
<Moment date={task?.last_message_at} format="YYYY-MM-DD HH:mm" />
|
|
57
|
+
),
|
|
58
|
+
},
|
|
59
|
+
{ name: "task.elapsed", content: `${task?.elapsed / 1000}s` },
|
|
60
|
+
];
|
|
61
|
+
|
|
62
|
+
const propertyElement = (name, content) => (
|
|
63
|
+
<Grid.Column key={name} width={3}>
|
|
64
|
+
<Item>
|
|
65
|
+
<Item.Content>
|
|
66
|
+
<Item.Header as="h5">
|
|
67
|
+
<FormattedMessage id={name} />
|
|
68
|
+
</Item.Header>
|
|
69
|
+
<Item.Description>{content}</Item.Description>
|
|
70
|
+
</Item.Content>
|
|
71
|
+
</Item>
|
|
72
|
+
</Grid.Column>
|
|
73
|
+
);
|
|
74
|
+
|
|
75
|
+
return (
|
|
76
|
+
<>
|
|
77
|
+
<Breadcrumb>
|
|
78
|
+
<Breadcrumb.Section as={Link} to={TASKS} active={false}>
|
|
79
|
+
<FormattedMessage id="tasks.header" />
|
|
80
|
+
</Breadcrumb.Section>
|
|
81
|
+
<>
|
|
82
|
+
<Breadcrumb.Divider icon="right angle" />
|
|
83
|
+
<Breadcrumb.Section active>{task?.index}</Breadcrumb.Section>
|
|
84
|
+
</>
|
|
85
|
+
</Breadcrumb>
|
|
86
|
+
<Segment>
|
|
87
|
+
<Header as="h2">
|
|
88
|
+
<Icon circular name="check" />
|
|
89
|
+
<Header.Content>
|
|
90
|
+
<FormattedMessage id="task.header" />
|
|
91
|
+
<Header.Subheader>
|
|
92
|
+
<FormattedMessage id="task.subheader" />
|
|
93
|
+
</Header.Subheader>
|
|
94
|
+
</Header.Content>
|
|
95
|
+
</Header>
|
|
96
|
+
<Segment basic>
|
|
97
|
+
<Button
|
|
98
|
+
primary
|
|
99
|
+
onClick={() => refetch()}
|
|
100
|
+
content={<FormattedMessage id="task.refresh" />}
|
|
101
|
+
/>
|
|
102
|
+
</Segment>
|
|
103
|
+
<Dimmer.Dimmable dimmed={loading}>
|
|
104
|
+
<Dimmer active={loading} inverted>
|
|
105
|
+
<Loader />
|
|
106
|
+
</Dimmer>
|
|
107
|
+
{task ? (
|
|
108
|
+
<>
|
|
109
|
+
<Grid columns={4} divided>
|
|
110
|
+
<Grid.Row>
|
|
111
|
+
{properties.map(({ name, content }) =>
|
|
112
|
+
propertyElement(name, content)
|
|
113
|
+
)}
|
|
114
|
+
</Grid.Row>
|
|
115
|
+
</Grid>
|
|
116
|
+
<Divider />
|
|
117
|
+
<Progress
|
|
118
|
+
percent={Math.round((task.processed / task.count) * 100)}
|
|
119
|
+
progress
|
|
120
|
+
>
|
|
121
|
+
<FormattedMessage id="task.progress" />
|
|
122
|
+
</Progress>
|
|
123
|
+
<Divider />
|
|
124
|
+
|
|
125
|
+
<Grid columns={4} divided>
|
|
126
|
+
<Grid.Row>
|
|
127
|
+
{propertyElement(
|
|
128
|
+
"task.memory.max",
|
|
129
|
+
parseMemory(memoryStats?.max)
|
|
130
|
+
)}
|
|
131
|
+
{propertyElement(
|
|
132
|
+
"task.memory.min",
|
|
133
|
+
parseMemory(memoryStats?.min)
|
|
134
|
+
)}
|
|
135
|
+
{propertyElement(
|
|
136
|
+
"task.memory.last",
|
|
137
|
+
parseMemory(memoryStats?.last)
|
|
138
|
+
)}
|
|
139
|
+
</Grid.Row>
|
|
140
|
+
</Grid>
|
|
141
|
+
</>
|
|
142
|
+
) : null}
|
|
143
|
+
</Dimmer.Dimmable>
|
|
144
|
+
</Segment>
|
|
145
|
+
</>
|
|
146
|
+
);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
export default Task;
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import _ from "lodash/fp";
|
|
2
|
+
import React from "react";
|
|
3
|
+
import PropTypes from "prop-types";
|
|
4
|
+
import { useHistory } from "react-router-dom";
|
|
5
|
+
import { useQuery } from "@apollo/client";
|
|
6
|
+
import { connect } from "react-redux";
|
|
7
|
+
import { FormattedMessage } from "react-intl";
|
|
8
|
+
import { Button, Header, Icon, Table, Segment } from "semantic-ui-react";
|
|
9
|
+
import { Dimmer, Loader } from "semantic-ui-react";
|
|
10
|
+
import Moment from "react-moment";
|
|
11
|
+
import { reindexAllGrants, reindexAllStructures } from "@truedat/core/routines";
|
|
12
|
+
import { linkTo } from "@truedat/core/routes";
|
|
13
|
+
import { TASKS_QUERY } from "../api/queries";
|
|
14
|
+
|
|
15
|
+
export const Tasks = ({ reindexAllGrants, reindexAllStructures }) => {
|
|
16
|
+
const { loading, data, refetch } = useQuery(TASKS_QUERY, {
|
|
17
|
+
fetchPolicy: "cache-and-network",
|
|
18
|
+
});
|
|
19
|
+
const history = useHistory();
|
|
20
|
+
|
|
21
|
+
const tasks = _.flow(
|
|
22
|
+
_.get("tasks"),
|
|
23
|
+
_.orderBy(["started_at"], ["desc"])
|
|
24
|
+
)(data);
|
|
25
|
+
|
|
26
|
+
return (
|
|
27
|
+
<Segment>
|
|
28
|
+
<Header as="h2">
|
|
29
|
+
<Icon circular name="check" />
|
|
30
|
+
<Header.Content>
|
|
31
|
+
<FormattedMessage id="tasks.header" />
|
|
32
|
+
<Header.Subheader>
|
|
33
|
+
<FormattedMessage id="tasks.subheader" />
|
|
34
|
+
</Header.Subheader>
|
|
35
|
+
</Header.Content>
|
|
36
|
+
</Header>
|
|
37
|
+
<Segment basic>
|
|
38
|
+
<Button
|
|
39
|
+
primary
|
|
40
|
+
onClick={() => refetch()}
|
|
41
|
+
content={<FormattedMessage id="task.refresh" />}
|
|
42
|
+
/>
|
|
43
|
+
<Button
|
|
44
|
+
primary
|
|
45
|
+
floated="right"
|
|
46
|
+
onClick={() => {
|
|
47
|
+
reindexAllStructures();
|
|
48
|
+
refetch();
|
|
49
|
+
}}
|
|
50
|
+
content={<FormattedMessage id="task.reindex_all_structures" />}
|
|
51
|
+
/>
|
|
52
|
+
<Button
|
|
53
|
+
primary
|
|
54
|
+
floated="right"
|
|
55
|
+
onClick={() => {
|
|
56
|
+
reindexAllGrants();
|
|
57
|
+
refetch();
|
|
58
|
+
}}
|
|
59
|
+
content={<FormattedMessage id="task.reindex_all_grants" />}
|
|
60
|
+
/>
|
|
61
|
+
</Segment>
|
|
62
|
+
<Dimmer.Dimmable dimmed={loading}>
|
|
63
|
+
<Dimmer active={loading} inverted>
|
|
64
|
+
<Loader />
|
|
65
|
+
</Dimmer>
|
|
66
|
+
{data ? (
|
|
67
|
+
<Table selectable>
|
|
68
|
+
<Table.Header>
|
|
69
|
+
<Table.Row>
|
|
70
|
+
<Table.HeaderCell
|
|
71
|
+
content={<FormattedMessage id={`task.index`} />}
|
|
72
|
+
/>
|
|
73
|
+
<Table.HeaderCell
|
|
74
|
+
content={<FormattedMessage id={`task.status`} />}
|
|
75
|
+
/>
|
|
76
|
+
<Table.HeaderCell
|
|
77
|
+
content={<FormattedMessage id={`task.started_at`} />}
|
|
78
|
+
/>
|
|
79
|
+
<Table.HeaderCell
|
|
80
|
+
content={<FormattedMessage id={`task.last_message_at`} />}
|
|
81
|
+
/>
|
|
82
|
+
<Table.HeaderCell
|
|
83
|
+
content={<FormattedMessage id={`task.progress`} />}
|
|
84
|
+
/>
|
|
85
|
+
<Table.HeaderCell
|
|
86
|
+
content={<FormattedMessage id={`task.elapsed`} />}
|
|
87
|
+
/>
|
|
88
|
+
</Table.Row>
|
|
89
|
+
</Table.Header>
|
|
90
|
+
<Table.Body>
|
|
91
|
+
{tasks.map((task) => (
|
|
92
|
+
<Table.Row
|
|
93
|
+
key={task.started_at}
|
|
94
|
+
onClick={() => history.push(linkTo.TASK(task))}
|
|
95
|
+
>
|
|
96
|
+
<Table.Cell content={task.index} />
|
|
97
|
+
<Table.Cell content={task.status} />
|
|
98
|
+
|
|
99
|
+
<Table.Cell
|
|
100
|
+
content={
|
|
101
|
+
<Moment
|
|
102
|
+
date={task.started_at}
|
|
103
|
+
format="YYYY-MM-DD HH:mm"
|
|
104
|
+
/>
|
|
105
|
+
}
|
|
106
|
+
/>
|
|
107
|
+
<Table.Cell
|
|
108
|
+
content={
|
|
109
|
+
<Moment
|
|
110
|
+
date={task.last_message_at}
|
|
111
|
+
format="YYYY-MM-DD HH:mm"
|
|
112
|
+
/>
|
|
113
|
+
}
|
|
114
|
+
/>
|
|
115
|
+
<Table.Cell
|
|
116
|
+
content={
|
|
117
|
+
task.processed ? `${task.processed}/${task.count}` : "-"
|
|
118
|
+
}
|
|
119
|
+
/>
|
|
120
|
+
<Table.Cell
|
|
121
|
+
content={task.elapsed ? `${task.elapsed / 1000}s` : "-"}
|
|
122
|
+
/>
|
|
123
|
+
</Table.Row>
|
|
124
|
+
))}
|
|
125
|
+
</Table.Body>
|
|
126
|
+
</Table>
|
|
127
|
+
) : null}
|
|
128
|
+
</Dimmer.Dimmable>
|
|
129
|
+
</Segment>
|
|
130
|
+
);
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
Tasks.propTypes = {
|
|
134
|
+
reindexAllGrants: PropTypes.func,
|
|
135
|
+
reindexAllStructures: PropTypes.func,
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
export default connect(null, { reindexAllGrants, reindexAllStructures })(Tasks);
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { Route } from "react-router-dom";
|
|
2
|
+
import React from "react";
|
|
3
|
+
import { Unauthorized } from "@truedat/core/components";
|
|
4
|
+
import { useAuthorized } from "@truedat/core/hooks";
|
|
5
|
+
import { TASKS, TASK } from "@truedat/core/routes";
|
|
6
|
+
import Tasks from "./Tasks";
|
|
7
|
+
import Task from "./Task";
|
|
8
|
+
|
|
9
|
+
export const TasksRoutes = () => {
|
|
10
|
+
const authorized = useAuthorized();
|
|
11
|
+
return (
|
|
12
|
+
<>
|
|
13
|
+
<Route
|
|
14
|
+
exact
|
|
15
|
+
path={TASK}
|
|
16
|
+
render={() => (authorized ? <Task /> : <Unauthorized />)}
|
|
17
|
+
/>
|
|
18
|
+
<Route
|
|
19
|
+
exact
|
|
20
|
+
path={TASKS}
|
|
21
|
+
render={() => (authorized ? <Tasks /> : <Unauthorized />)}
|
|
22
|
+
/>
|
|
23
|
+
</>
|
|
24
|
+
);
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
export default TasksRoutes;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { render } from "@truedat/test/render";
|
|
3
|
+
import Task from "../Task";
|
|
4
|
+
import { TASK_QUERY } from "../../api/queries";
|
|
5
|
+
import en from "../../messages/en";
|
|
6
|
+
|
|
7
|
+
jest.mock("@truedat/core/hooks", () => ({
|
|
8
|
+
useAuthorized: jest.fn(() => true),
|
|
9
|
+
}));
|
|
10
|
+
|
|
11
|
+
jest.mock("react-router-dom", () => ({
|
|
12
|
+
...jest.requireActual("react-router-dom"),
|
|
13
|
+
useParams: () => ({ id: "123" }),
|
|
14
|
+
}));
|
|
15
|
+
|
|
16
|
+
const task = {
|
|
17
|
+
__typename: "Task",
|
|
18
|
+
id: "123",
|
|
19
|
+
started_at: "2020-01-01T00:00:00.000Z",
|
|
20
|
+
last_message_at: "2020-01-01T00:01:00.000Z",
|
|
21
|
+
index: "grants",
|
|
22
|
+
status: "processing",
|
|
23
|
+
count: 10000,
|
|
24
|
+
processed: 1000,
|
|
25
|
+
memory_trace: [["2020-01-01T00:01:00.000Z", 100000]],
|
|
26
|
+
elapsed: 10,
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
const taskMock = {
|
|
30
|
+
request: { query: TASK_QUERY, variables: { id: "123" } },
|
|
31
|
+
result: { data: { task } },
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
describe("<Task />", () => {
|
|
35
|
+
it("matches the latest snapshot", async () => {
|
|
36
|
+
const renderOpts = {
|
|
37
|
+
messages: { en },
|
|
38
|
+
mocks: [taskMock],
|
|
39
|
+
};
|
|
40
|
+
const { container, findByText } = render(<Task />, renderOpts);
|
|
41
|
+
await findByText("processing");
|
|
42
|
+
expect(container).toMatchSnapshot();
|
|
43
|
+
});
|
|
44
|
+
});
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
/* eslint-disable fp/no-mutation */
|
|
2
|
+
/* eslint-disable fp/no-let */
|
|
3
|
+
import React from "react";
|
|
4
|
+
import { waitFor } from "@testing-library/react";
|
|
5
|
+
import { render } from "@truedat/test/render";
|
|
6
|
+
import userEvent from "@testing-library/user-event";
|
|
7
|
+
import { Tasks } from "../Tasks";
|
|
8
|
+
import { TASKS_QUERY } from "../../api/queries";
|
|
9
|
+
import en from "../../messages/en";
|
|
10
|
+
|
|
11
|
+
const mockHistory = {
|
|
12
|
+
push: jest.fn(),
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
jest.mock("react-router-dom", () => ({
|
|
16
|
+
...jest.requireActual("react-router-dom"),
|
|
17
|
+
useHistory: () => mockHistory,
|
|
18
|
+
}));
|
|
19
|
+
|
|
20
|
+
const task = {
|
|
21
|
+
__typename: "Task",
|
|
22
|
+
id: "123",
|
|
23
|
+
started_at: "2020-01-01T00:00:00.000Z",
|
|
24
|
+
last_message_at: "2020-01-01T00:01:00.000Z",
|
|
25
|
+
index: "grants",
|
|
26
|
+
status: "processing",
|
|
27
|
+
count: 10000,
|
|
28
|
+
processed: 1000,
|
|
29
|
+
memory_trace: [["2020-01-01T00:01:00.000Z", 100000]],
|
|
30
|
+
elapsed: 10,
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
const refetchTask = {
|
|
34
|
+
__typename: "Task",
|
|
35
|
+
id: "123",
|
|
36
|
+
started_at: "2020-01-01T00:00:00.000Z",
|
|
37
|
+
last_message_at: "2020-01-01T00:02:00.000Z",
|
|
38
|
+
index: "grants",
|
|
39
|
+
status: "done",
|
|
40
|
+
count: 10000,
|
|
41
|
+
processed: 10000,
|
|
42
|
+
memory_trace: [["2020-01-01T00:01:00.000Z", 100000]],
|
|
43
|
+
elapsed: 20,
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
let queryCalled = false;
|
|
47
|
+
const tasksMock = {
|
|
48
|
+
request: { query: TASKS_QUERY },
|
|
49
|
+
newData: () => {
|
|
50
|
+
if (queryCalled) {
|
|
51
|
+
return {
|
|
52
|
+
data: { tasks: [refetchTask] },
|
|
53
|
+
};
|
|
54
|
+
} else {
|
|
55
|
+
queryCalled = true;
|
|
56
|
+
return {
|
|
57
|
+
data: { tasks: [task] },
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
},
|
|
61
|
+
};
|
|
62
|
+
const renderOpts = {
|
|
63
|
+
messages: { en },
|
|
64
|
+
mocks: [tasksMock],
|
|
65
|
+
};
|
|
66
|
+
const props = {
|
|
67
|
+
reindexAllGrants: jest.fn(),
|
|
68
|
+
reindexAllStructures: jest.fn(),
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
describe("<Tasks />", () => {
|
|
72
|
+
it("matches the latest snapshot", async () => {
|
|
73
|
+
queryCalled = false;
|
|
74
|
+
const { container, findByText } = render(<Tasks {...props} />, renderOpts);
|
|
75
|
+
await findByText("processing");
|
|
76
|
+
expect(container).toMatchSnapshot();
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
it("refetches button", async () => {
|
|
80
|
+
queryCalled = false;
|
|
81
|
+
const { container, findByText, findByRole } = render(
|
|
82
|
+
<Tasks {...props} />,
|
|
83
|
+
renderOpts
|
|
84
|
+
);
|
|
85
|
+
await findByText("processing");
|
|
86
|
+
|
|
87
|
+
userEvent.click(await findByRole("button", { name: /Refresh/i }));
|
|
88
|
+
await findByText("done");
|
|
89
|
+
expect(container).toMatchSnapshot();
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
it("triggers Reindex Grants and Structures", async () => {
|
|
93
|
+
queryCalled = false;
|
|
94
|
+
const { findByText, findByRole } = render(<Tasks {...props} />, renderOpts);
|
|
95
|
+
await findByText("processing");
|
|
96
|
+
|
|
97
|
+
userEvent.click(
|
|
98
|
+
await findByRole("button", { name: /Reindex all structures/i })
|
|
99
|
+
);
|
|
100
|
+
await waitFor(() =>
|
|
101
|
+
expect(props.reindexAllStructures).toHaveBeenCalledTimes(1)
|
|
102
|
+
);
|
|
103
|
+
|
|
104
|
+
userEvent.click(
|
|
105
|
+
await findByRole("button", { name: /Reindex all grants/i })
|
|
106
|
+
);
|
|
107
|
+
await waitFor(() =>
|
|
108
|
+
expect(props.reindexAllGrants).toHaveBeenCalledTimes(1)
|
|
109
|
+
);
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
it("navigates to task detail", async () => {
|
|
113
|
+
queryCalled = false;
|
|
114
|
+
const { findByText } = render(<Tasks {...props} />, renderOpts);
|
|
115
|
+
await findByText("processing");
|
|
116
|
+
|
|
117
|
+
userEvent.click(await findByText(/processing/i));
|
|
118
|
+
expect(mockHistory.push.mock.calls[0][0]).toBe("/tasks/123");
|
|
119
|
+
});
|
|
120
|
+
});
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { render } from "@truedat/test/render";
|
|
3
|
+
import TasksRoutes from "../TasksRoutes";
|
|
4
|
+
import { TASK_QUERY, TASKS_QUERY } from "../../api/queries";
|
|
5
|
+
import en from "../../messages/en";
|
|
6
|
+
|
|
7
|
+
jest.mock("@truedat/core/hooks", () => ({
|
|
8
|
+
useAuthorized: jest.fn(() => true),
|
|
9
|
+
}));
|
|
10
|
+
|
|
11
|
+
const task = {
|
|
12
|
+
__typename: "Task",
|
|
13
|
+
id: "123",
|
|
14
|
+
started_at: "2020-01-01T00:00:00.000Z",
|
|
15
|
+
last_message_at: "2020-01-01T00:01:00.000Z",
|
|
16
|
+
index: "grants",
|
|
17
|
+
status: "processing",
|
|
18
|
+
count: 10000,
|
|
19
|
+
processed: 1000,
|
|
20
|
+
memory_trace: [["2020-01-01T00:01:00.000Z", 100000]],
|
|
21
|
+
elapsed: 10,
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
const tasksMock = {
|
|
25
|
+
request: { query: TASKS_QUERY },
|
|
26
|
+
result: { data: { tasks: [task] } },
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
const taskMock = {
|
|
30
|
+
request: { query: TASK_QUERY, variables: { id: "123" } },
|
|
31
|
+
result: { data: { task } },
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
describe("<TasksRoutes />", () => {
|
|
35
|
+
it("matches the latest snapshot (list route)", async () => {
|
|
36
|
+
const renderOpts = {
|
|
37
|
+
messages: { en },
|
|
38
|
+
routes: ["/tasks"],
|
|
39
|
+
mocks: [tasksMock],
|
|
40
|
+
};
|
|
41
|
+
const { container, findByText } = render(<TasksRoutes />, renderOpts);
|
|
42
|
+
await findByText("processing");
|
|
43
|
+
expect(container).toMatchSnapshot();
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it("matches the latest snapshot (view route)", async () => {
|
|
47
|
+
const renderOpts = {
|
|
48
|
+
messages: { en },
|
|
49
|
+
routes: ["/tasks/123"],
|
|
50
|
+
mocks: [taskMock],
|
|
51
|
+
};
|
|
52
|
+
const { container, findByText } = render(<TasksRoutes />, renderOpts);
|
|
53
|
+
await findByText("processing");
|
|
54
|
+
expect(container).toMatchSnapshot();
|
|
55
|
+
});
|
|
56
|
+
});
|