orc-shared 1.5.0-dev.8 → 1.5.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 (60) hide show
  1. package/dist/actions/navigation.js +16 -1
  2. package/dist/actions/requestsApi.js +1 -0
  3. package/dist/actions/tasks.js +190 -0
  4. package/dist/buildStore.js +3 -1
  5. package/dist/components/AppFrame/Sidebar.js +4 -8
  6. package/dist/components/CategoryList.js +6 -0
  7. package/dist/components/MaterialUI/DataDisplay/PredefinedElements/DiscountedPrice.js +1 -0
  8. package/dist/components/MaterialUI/DataDisplay/PredefinedElements/InformationItem.js +13 -5
  9. package/dist/components/MaterialUI/DataDisplay/TooltippedElements/MultipleLinesText.js +4 -2
  10. package/dist/components/MaterialUI/Inputs/PredefinedElements/SearchControl.js +43 -46
  11. package/dist/components/MaterialUI/hocs/withDeferredTooltip.js +3 -2
  12. package/dist/components/Navigation/index.js +0 -1
  13. package/dist/components/Routing/withWaypointing.js +1 -1
  14. package/dist/components/TaskDetailsModal.js +193 -0
  15. package/dist/constants.js +17 -2
  16. package/dist/content/iconsSheet.svg +22 -0
  17. package/dist/reducers/navigation.js +16 -0
  18. package/dist/reducers/request.js +4 -0
  19. package/dist/reducers/tasks.js +99 -0
  20. package/dist/selectors/authentication.js +17 -1
  21. package/dist/selectors/tasks.js +66 -0
  22. package/dist/sharedMessages.js +17 -1
  23. package/dist/utils/propertyHelper.js +35 -0
  24. package/dist/whyDidYouRerender.js +1 -0
  25. package/package.json +5 -5
  26. package/src/actions/navigation.js +7 -0
  27. package/src/actions/navigation.test.js +12 -0
  28. package/src/actions/tasks.js +77 -0
  29. package/src/actions/tasks.test.js +169 -0
  30. package/src/buildStore.js +2 -0
  31. package/src/components/AppFrame/About.test.js +3 -3
  32. package/src/components/AppFrame/Sidebar.js +4 -3
  33. package/src/components/MaterialUI/DataDisplay/PredefinedElements/InformationItem.js +15 -3
  34. package/src/components/MaterialUI/DataDisplay/TooltippedElements/MultipleLinesText.js +2 -1
  35. package/src/components/MaterialUI/Inputs/PredefinedElements/SearchControl.js +39 -27
  36. package/src/components/MaterialUI/Inputs/PredefinedElements/SearchControl.test.js +39 -34
  37. package/src/components/MaterialUI/hocs/withDeferredTooltip.js +2 -1
  38. package/src/components/MaterialUI/hocs/withDeferredTooltip.test.js +52 -0
  39. package/src/components/TaskDetailsModal.js +132 -0
  40. package/src/components/TaskDetailsModal.test.js +317 -0
  41. package/src/components/Text.test.js +44 -59
  42. package/src/constants.js +14 -1
  43. package/src/content/iconsSheet.svg +22 -0
  44. package/src/hooks/useLabelMessage.test.js +16 -10
  45. package/src/reducers/navigation.js +24 -0
  46. package/src/reducers/navigation.test.js +38 -0
  47. package/src/reducers/request.js +4 -0
  48. package/src/reducers/request.test.js +11 -0
  49. package/src/reducers/tasks.js +56 -0
  50. package/src/reducers/tasks.test.js +404 -0
  51. package/src/selectors/authentication.js +13 -0
  52. package/src/selectors/authentication.test.js +322 -0
  53. package/src/selectors/tasks.js +16 -0
  54. package/src/selectors/tasks.test.js +60 -0
  55. package/src/sharedMessages.js +17 -1
  56. package/src/translations/en-US.json +16 -12
  57. package/src/translations/fr-CA.json +16 -12
  58. package/src/utils/propertyHelper.js +38 -0
  59. package/src/utils/propertyHelper.test.js +160 -0
  60. package/src/utils/timezoneHelper.test.js +4 -2
@@ -53,10 +53,11 @@ describe("useStyles", () => {
53
53
  });
54
54
 
55
55
  describe("SearchControl Component", () => {
56
- const stateSetter = sinon.spy().named("focus");
57
- const useStateMock = initState => [initState, stateSetter];
58
-
56
+ beforeEach(() => {
57
+ jest.useFakeTimers();
58
+ });
59
59
  afterEach(() => {
60
+ jest.useRealTimers();
60
61
  jest.clearAllMocks();
61
62
  });
62
63
 
@@ -232,8 +233,7 @@ describe("SearchControl Component", () => {
232
233
  expect(searchInput.length, "to be", 1);
233
234
 
234
235
  searchInput.instance().value = "abc";
235
- searchInput.simulate("keydown", { key: "Tab" });
236
- searchInput.simulate("keydown", { key: "Enter" });
236
+ mountedComponent.find("form").simulate("submit", { preventDefault: () => {} });
237
237
 
238
238
  expect(onSearchEvent, "to have calls satisfying", [{ args: ["aValue", "abc"] }]);
239
239
  });
@@ -371,7 +371,7 @@ describe("SearchControl Component", () => {
371
371
  expect(onSearchEvent, "to have calls satisfying", [{ args: ["anotherValue", "abcdef123"] }]);
372
372
  });
373
373
 
374
- it("Search Control should render with the 2nd value", () => {
374
+ it("Search Control should clear the search when changing the option", () => {
375
375
  const options = [
376
376
  { value: "aValue", label: "aLabel" },
377
377
  { value: "anotherValue", label: "anotherLabel" },
@@ -385,8 +385,8 @@ describe("SearchControl Component", () => {
385
385
  placeholder="placeHolderTest"
386
386
  defaultValue={"abcdef123"}
387
387
  searchOptions={options}
388
- searchOption={"anotherValue"}
389
388
  onSearch={onSearchEvent}
389
+ focusSearchOnSearchOptionChange={true}
390
390
  />
391
391
  </TestWrapper>
392
392
  );
@@ -395,48 +395,53 @@ describe("SearchControl Component", () => {
395
395
 
396
396
  const selectMui = mountedComponent.find(SelectMUI);
397
397
 
398
- expect(selectMui.props().value, "to equal", "anotherValue");
399
- });
398
+ const event = {
399
+ target: {
400
+ value: "anotherValue",
401
+ },
402
+ };
400
403
 
401
- it("focusing text input should set focus on container", () => {
402
- jest.spyOn(React, "useState").mockImplementation(useStateMock);
404
+ onSearchEvent.resetHistory();
405
+
406
+ selectMui.invoke("onChange")(event);
407
+
408
+ expect(onSearchEvent.callCount, "to equal", 1);
409
+ expect(onSearchEvent, "to have calls satisfying", [{ args: ["anotherValue", ""] }]);
410
+
411
+ const allInputs = mountedComponent.find("input");
412
+ const searchInput = allInputs.find("[placeholder='placeHolderTest']");
413
+ expect(searchInput.length, "to be", 1);
403
414
 
415
+ expect(searchInput.instance().value, "to be", "abcdef123");
416
+ jest.runOnlyPendingTimers();
417
+ expect(searchInput.instance().value, "to be", "");
418
+ });
419
+
420
+ it("Search Control should render with the 2nd value", () => {
404
421
  const options = [
405
422
  { value: "aValue", label: "aLabel" },
406
423
  { value: "anotherValue", label: "anotherLabel" },
407
424
  ];
408
425
 
426
+ const onSearchEvent = sinon.spy().named("search");
427
+
409
428
  const component = (
410
429
  <TestWrapper stylesProvider muiThemeProvider={{ theme }}>
411
- <SearchControl placeholder="placeHolderTest" searchOptions={options} />
430
+ <SearchControl
431
+ placeholder="placeHolderTest"
432
+ defaultValue={"abcdef123"}
433
+ searchOptions={options}
434
+ searchOption={"anotherValue"}
435
+ onSearch={onSearchEvent}
436
+ />
412
437
  </TestWrapper>
413
438
  );
414
439
 
415
440
  const mountedComponent = mount(component);
416
441
 
417
- const allInputs = mountedComponent.find("input");
418
- const searchInput = allInputs.find("[placeholder='placeHolderTest']");
419
- expect(searchInput.length, "to be", 1);
420
-
421
- let searchEditParent = mountedComponent.find('[data-qa="searchInput"]');
422
- expect(searchEditParent.length, "to be", 1);
423
-
424
- expect(searchEditParent.props()["data-qa-is-focused"], "to be", false);
425
-
426
- const event = {
427
- preventDefault: () => {},
428
- stopPropagation: () => {},
429
- };
430
-
431
- searchInput.invoke("onFocus")(event);
432
-
433
- searchEditParent = mountedComponent.find('[data-qa="searchInput"]');
434
- expect(searchEditParent.props()["data-qa-is-focused"], "to be", true);
435
-
436
- searchInput.invoke("onBlur")(event);
442
+ const selectMui = mountedComponent.find(SelectMUI);
437
443
 
438
- searchEditParent = mountedComponent.find('[data-qa="searchInput"]');
439
- expect(searchEditParent.props()["data-qa-is-focused"], "to be", false);
444
+ expect(selectMui.props().value, "to equal", "anotherValue");
440
445
  });
441
446
 
442
447
  it("Renders Search Control component without errors when disabled", () => {
@@ -8,7 +8,7 @@ import { isString, isObject, isStringNullOrWhitespace, isReactComponent } from "
8
8
 
9
9
  const withDeferredTooltip =
10
10
  Comp =>
11
- ({ titleValue, alwaysDisplay, ...props }) => {
11
+ ({ titleValue, alwaysDisplay, tooltipClasses, ...props }) => {
12
12
  const [shouldBeTooltipped, setShouldBeTooltipped] = useState(false);
13
13
 
14
14
  const defaultComponent = <Comp onMouseEnter={event => makeComponentTooltipped(event)} {...props} />;
@@ -32,6 +32,7 @@ const withDeferredTooltip =
32
32
  return (
33
33
  <MuiTooltip
34
34
  arrow
35
+ classes={tooltipClasses ?? null}
35
36
  title={titleValue}
36
37
  disableHoverListener={false}
37
38
  disableFocusListener={true}
@@ -155,4 +155,56 @@ describe("withDeferredTooltip", () => {
155
155
 
156
156
  expect(wrapper.prop("onMouseEnter"), "to be", undefined);
157
157
  });
158
+
159
+ it("Always displays passed title in tooltip with tooltip classes when specified", () => {
160
+ const Wrapper = props => <ComponentToBeTooltipped {...props} />;
161
+
162
+ const TooltippedCompponent = withDeferredTooltip(Wrapper);
163
+
164
+ const mountedTooltippedComponent = shallow(
165
+ <TooltippedCompponent alwaysDisplay titleValue="test" tooltipClasses={{ classTest: "testMyMojo" }} />,
166
+ );
167
+
168
+ const event = {
169
+ target: {
170
+ offsetWidth: 100,
171
+ scrollWidth: 100,
172
+ },
173
+ };
174
+
175
+ mountedTooltippedComponent.find(Wrapper).invoke("onMouseEnter")(event);
176
+
177
+ let expected = (
178
+ <MuiTooltip title="test" classes={{ classTest: "testMyMojo" }}>
179
+ <Wrapper />
180
+ </MuiTooltip>
181
+ );
182
+
183
+ expect(mountedTooltippedComponent.containsMatchingElement(expected), "to be true");
184
+ });
185
+
186
+ it("Always displays passed title in tooltip without tooltip classes when not specified", () => {
187
+ const Wrapper = props => <ComponentToBeTooltipped {...props} />;
188
+
189
+ const TooltippedCompponent = withDeferredTooltip(Wrapper);
190
+
191
+ const mountedTooltippedComponent = shallow(<TooltippedCompponent alwaysDisplay titleValue="test" />);
192
+
193
+ const event = {
194
+ target: {
195
+ offsetWidth: 100,
196
+ scrollWidth: 100,
197
+ },
198
+ };
199
+
200
+ mountedTooltippedComponent.find(Wrapper).invoke("onMouseEnter")(event);
201
+
202
+ let expected = (
203
+ <MuiTooltip title="test" classes={null}>
204
+ <Wrapper />
205
+ </MuiTooltip>
206
+ );
207
+
208
+ expect(mountedTooltippedComponent.containsMatchingElement(expected), "to be true");
209
+ });
158
210
  });
@@ -0,0 +1,132 @@
1
+ import React, { useEffect } from "react";
2
+ import { useDispatch, useSelector } from "react-redux";
3
+ import Button from "@material-ui/core/Button";
4
+ import { FormattedMessage } from "react-intl";
5
+ import { makeStyles } from "@material-ui/core/styles";
6
+ import { taskStatuses } from "../constants";
7
+ import useSelectorAndUnwrap from "../hooks/useSelectorAndUnwrap";
8
+ import InformationItem from "./MaterialUI/DataDisplay/PredefinedElements/InformationItem";
9
+ import { taskInfo, taskLogs } from "../selectors/tasks";
10
+ import { compareObjectProperty } from "../utils/propertyHelper";
11
+ import useLoader from "../hooks/useLoader";
12
+ import { clearTaskLog, getTaskInfo, getTaskLog } from "../actions/tasks";
13
+ import ModalProps from "./MaterialUI/DataDisplay/modalProps";
14
+ import Modal from "./MaterialUI/DataDisplay/Modal";
15
+ import { namedLookupLocalizedSelector } from "../selectors/metadata";
16
+ import sharedMessages from "../sharedMessages";
17
+
18
+ export const useStyles = makeStyles(theme => ({
19
+ actionPanel: {
20
+ display: "flex",
21
+ marginLeft: "auto",
22
+ flex: "1 1 0",
23
+ justifyContent: "flex-end",
24
+ },
25
+ taskContainer: {
26
+ display: "flex",
27
+ width: "100%",
28
+ flexDirection: "column",
29
+ "&>div": {
30
+ "&:last-child": {
31
+ flex: 1,
32
+ display: "flex",
33
+ flexDirection: "column",
34
+ "&>textarea": {
35
+ overflowY: "scroll",
36
+ resize: "none",
37
+ flex: 1,
38
+ },
39
+ },
40
+ },
41
+ },
42
+ }));
43
+
44
+ const isTaskCompleted = status => {
45
+ switch (status) {
46
+ case taskStatuses.faulted:
47
+ case taskStatuses.ranToCompletion:
48
+ case taskStatuses.canceled:
49
+ case taskStatuses.ignored:
50
+ return true;
51
+ default:
52
+ return false;
53
+ }
54
+ };
55
+
56
+ const TaskDetailsModal = ({ taskId, open, closeModal }) => {
57
+ const classes = useStyles();
58
+ const dispatch = useDispatch();
59
+
60
+ const taskDetails = useSelectorAndUnwrap(taskInfo(taskId));
61
+ const logs = useSelectorAndUnwrap(taskLogs(taskId));
62
+ const logsText = logs
63
+ .sort((a, b) => compareObjectProperty(a, b, "executionTime"))
64
+ .reduce((accumulator, currentValue) => {
65
+ if (currentValue.message === null) {
66
+ return accumulator;
67
+ }
68
+ if (accumulator === "") {
69
+ return currentValue.message.trim();
70
+ }
71
+ return accumulator + "\n" + currentValue.message.trim();
72
+ }, "");
73
+
74
+ useLoader(getTaskInfo(taskId), () => taskDetails !== null);
75
+
76
+ const internalCloseModal = () => {
77
+ dispatch(clearTaskLog(taskId));
78
+ closeModal();
79
+ };
80
+
81
+ const taskStatus = taskDetails?.status;
82
+
83
+ useEffect(() => {
84
+ const timer = setInterval(() => {
85
+ if (!isTaskCompleted(taskStatus)) {
86
+ dispatch(getTaskInfo(taskId));
87
+ dispatch(getTaskLog(taskId, false));
88
+ }
89
+ }, 10000);
90
+ return () => clearInterval(timer);
91
+ }, [dispatch, taskId, taskStatus]);
92
+
93
+ const localizedStatus = useSelector(namedLookupLocalizedSelector("order", "TaskStatus", taskStatus));
94
+ const modalProps = new ModalProps();
95
+ const titleComponent = <FormattedMessage {...sharedMessages.taskInProgressModalTitle} />;
96
+
97
+ modalProps.set(ModalProps.propNames.title, titleComponent);
98
+ modalProps.set(ModalProps.propNames.open, open);
99
+ modalProps.set(ModalProps.propNames.type, "wide");
100
+ modalProps.set(ModalProps.propNames.backdropClickCallback, internalCloseModal);
101
+
102
+ const actionPanel = (
103
+ <div className={classes.actionPanel}>
104
+ <Button
105
+ key={sharedMessages.close.id}
106
+ data-qa={sharedMessages.close.id}
107
+ variant="contained"
108
+ color="primary"
109
+ disableElevation={true}
110
+ onClick={e => internalCloseModal(e)}
111
+ >
112
+ <FormattedMessage {...sharedMessages.close} />
113
+ </Button>
114
+ </div>
115
+ );
116
+
117
+ modalProps.set(ModalProps.propNames.actionPanel, actionPanel);
118
+
119
+ const taskContent = (
120
+ <div className={classes.taskContainer}>
121
+ <InformationItem label={sharedMessages.taskId}>{taskId}</InformationItem>
122
+ <InformationItem label={sharedMessages.taskStatus}>{localizedStatus}</InformationItem>
123
+ <InformationItem label={sharedMessages.taskLogs}>
124
+ <textarea readOnly value={logsText} />
125
+ </InformationItem>
126
+ </div>
127
+ );
128
+
129
+ return <Modal message={taskContent} modalProps={modalProps} />;
130
+ };
131
+
132
+ export default TaskDetailsModal;
@@ -0,0 +1,317 @@
1
+ import React from "react";
2
+ import Immutable from "immutable";
3
+ import { mount } from "enzyme";
4
+ import TaskDetailsModal from "./TaskDetailsModal";
5
+ import Modal from "./MaterialUI/DataDisplay/Modal";
6
+ import { defaultCulture } from "../selectors/locale";
7
+ import sinon from "sinon";
8
+ import { createMuiTheme, extractMessages, TestWrapper } from "../utils/testUtils";
9
+ import sharedMessages from "../sharedMessages";
10
+ import ModalProps from "./MaterialUI/DataDisplay/modalProps";
11
+ import { Ignore } from "unexpected-reaction";
12
+ import InformationItem from "./MaterialUI/DataDisplay/PredefinedElements/InformationItem";
13
+ import { clearTaskLog, getTaskInfo, getTaskLog } from "../actions/tasks";
14
+ import { taskStatuses } from "../constants";
15
+
16
+ const messages = extractMessages(sharedMessages);
17
+
18
+ jest.mock("../utils/buildUrl", () => {
19
+ const modExport = {};
20
+ modExport.loadConfig = () => Promise.resolve({});
21
+ modExport.buildUrl = (path = [], params = "") => "URL: " + path.join("/") + " " + JSON.stringify(params);
22
+ return modExport;
23
+ });
24
+
25
+ function removeBailout(obj) {
26
+ delete obj["@@redux-api-middleware/RSAA"].bailout;
27
+ return obj;
28
+ }
29
+
30
+ describe("TaskDetailsModal", () => {
31
+ let state, dispatch, store, clearIntervalSpy;
32
+ const theme = createMuiTheme();
33
+
34
+ beforeEach(() => {
35
+ //window.bypassDebounce = true;
36
+
37
+ dispatch = sinon.spy().named("dispatch");
38
+ jest.useFakeTimers();
39
+ clearIntervalSpy = sinon.spy(global, "clearInterval");
40
+
41
+ state = Immutable.fromJS({
42
+ requests: {
43
+ logout: false,
44
+ },
45
+ metadata: {
46
+ lookups: {},
47
+ },
48
+ tasks: {
49
+ taskInfos: {},
50
+ tasks: [],
51
+ logs: {},
52
+ },
53
+ locale: {
54
+ defaultCulture: defaultCulture,
55
+ },
56
+ });
57
+
58
+ store = {
59
+ getState: () => state,
60
+ subscribe: () => {},
61
+ dispatch: dispatch,
62
+ };
63
+ });
64
+ afterEach(() => {
65
+ jest.useRealTimers();
66
+ jest.restoreAllMocks();
67
+ clearIntervalSpy.restore();
68
+ });
69
+
70
+ it("renders a modal for task", () => {
71
+ const modalProps = new ModalProps();
72
+
73
+ modalProps.set(ModalProps.propNames.title, "Task In Progress");
74
+ modalProps.set(ModalProps.propNames.open, true);
75
+ modalProps.set(ModalProps.propNames.type, "wide");
76
+ modalProps.set(ModalProps.propNames.actionPanel, <Ignore />);
77
+
78
+ const expectedContent = (
79
+ <div>
80
+ <InformationItem label={sharedMessages.taskId}>1234</InformationItem>
81
+ <InformationItem label={sharedMessages.taskStatus}>
82
+ <Ignore />
83
+ </InformationItem>
84
+ <InformationItem label={sharedMessages.taskLogs}>
85
+ <textarea />
86
+ </InformationItem>
87
+ </div>
88
+ );
89
+
90
+ expect(
91
+ <TestWrapper
92
+ provider={{ store }}
93
+ memoryRouter
94
+ stylesProvider
95
+ muiThemeProvider={{ theme }}
96
+ intlProvider={{ messages }}
97
+ >
98
+ <TaskDetailsModal taskId="1234" open={true} closeModal={() => {}} />
99
+ </TestWrapper>,
100
+ "when mounted",
101
+ "to satisfy",
102
+ <TestWrapper
103
+ provider={{ store }}
104
+ memoryRouter
105
+ stylesProvider
106
+ muiThemeProvider={{ theme }}
107
+ intlProvider={{ messages }}
108
+ >
109
+ <Modal message={expectedContent} modalProps={modalProps} />
110
+ </TestWrapper>,
111
+ ).then(() => expect(console.error, "was not called"));
112
+ });
113
+
114
+ it("renders a modal for task with status and logs", () => {
115
+ state = state.setIn(
116
+ ["tasks", "logs", "1234"],
117
+ [
118
+ { executionTime: "", message: null },
119
+ { executionTime: "4", message: "msg\r\n" },
120
+ { executionTime: "3", message: "another" },
121
+ ],
122
+ );
123
+ state = state.setIn(["tasks", "taskInfos", "1234"], { status: taskStatuses.running });
124
+
125
+ const modalProps = new ModalProps();
126
+
127
+ modalProps.set(ModalProps.propNames.title, "Task In Progress");
128
+ modalProps.set(ModalProps.propNames.open, true);
129
+ modalProps.set(ModalProps.propNames.type, "wide");
130
+ modalProps.set(ModalProps.propNames.actionPanel, <Ignore />);
131
+
132
+ const expectedContent = (
133
+ <div>
134
+ <InformationItem label={sharedMessages.taskId}>1234</InformationItem>
135
+ <InformationItem label={sharedMessages.taskStatus}>[Running]</InformationItem>
136
+ <InformationItem label={sharedMessages.taskLogs}>
137
+ <textarea readOnly value={"another\nmsg"} />
138
+ </InformationItem>
139
+ </div>
140
+ );
141
+
142
+ expect(
143
+ <TestWrapper
144
+ provider={{ store }}
145
+ memoryRouter
146
+ stylesProvider
147
+ muiThemeProvider={{ theme }}
148
+ intlProvider={{ messages }}
149
+ >
150
+ <TaskDetailsModal taskId="1234" open={true} closeModal={() => {}} />
151
+ </TestWrapper>,
152
+ "when mounted",
153
+ "to satisfy",
154
+ <TestWrapper
155
+ provider={{ store }}
156
+ memoryRouter
157
+ stylesProvider
158
+ muiThemeProvider={{ theme }}
159
+ intlProvider={{ messages }}
160
+ >
161
+ <Modal message={expectedContent} modalProps={modalProps} />
162
+ </TestWrapper>,
163
+ ).then(() => expect(console.error, "was not called"));
164
+ });
165
+
166
+ it("close the dialog", () => {
167
+ const closeSpy = sinon.spy().named("close");
168
+
169
+ const content = (
170
+ <TestWrapper
171
+ provider={{ store }}
172
+ memoryRouter
173
+ stylesProvider
174
+ muiThemeProvider={{ theme }}
175
+ intlProvider={{ messages }}
176
+ >
177
+ <TaskDetailsModal taskId="1234" open={true} closeModal={closeSpy} />
178
+ </TestWrapper>
179
+ );
180
+
181
+ const mountedComponent = mount(content);
182
+ store.dispatch.resetHistory();
183
+
184
+ const input = mountedComponent.find("[data-qa='" + sharedMessages.close.id + "']").at(0);
185
+ input.simulate("click");
186
+
187
+ expect(closeSpy, "was called");
188
+ expect(store.dispatch, "to have calls satisfying", [
189
+ {
190
+ args: [clearTaskLog("1234")],
191
+ },
192
+ ]);
193
+ });
194
+
195
+ it("clear timer on unmount", () => {
196
+ const closeSpy = sinon.spy().named("close");
197
+ const content = (
198
+ <TestWrapper
199
+ provider={{ store }}
200
+ memoryRouter
201
+ stylesProvider
202
+ muiThemeProvider={{ theme }}
203
+ intlProvider={{ messages }}
204
+ >
205
+ <TaskDetailsModal taskId="1234" open={true} closeModal={closeSpy} />
206
+ </TestWrapper>
207
+ );
208
+
209
+ const mountedComponent = mount(content);
210
+ clearIntervalSpy.resetHistory();
211
+ mountedComponent.unmount();
212
+
213
+ expect(clearIntervalSpy, "was called");
214
+ });
215
+
216
+ it("taskInfo is called to load data", () => {
217
+ const content = (
218
+ <TestWrapper
219
+ provider={{ store }}
220
+ memoryRouter
221
+ stylesProvider
222
+ muiThemeProvider={{ theme }}
223
+ intlProvider={{ messages }}
224
+ >
225
+ <TaskDetailsModal taskId="1234" open={true} closeModal={() => {}} />
226
+ </TestWrapper>
227
+ );
228
+
229
+ mount(content);
230
+
231
+ expect(store.dispatch, "to have calls satisfying", [
232
+ {
233
+ args: [removeBailout(getTaskInfo("1234"))],
234
+ },
235
+ ]);
236
+ expect(store.dispatch, "not to have calls satisfying", [
237
+ {
238
+ args: [removeBailout(getTaskLog("1234"))],
239
+ },
240
+ ]);
241
+ });
242
+
243
+ it("taskLogs is called to load data", () => {
244
+ const content = (
245
+ <TestWrapper
246
+ provider={{ store }}
247
+ memoryRouter
248
+ stylesProvider
249
+ muiThemeProvider={{ theme }}
250
+ intlProvider={{ messages }}
251
+ >
252
+ <TaskDetailsModal taskId="1234" open={true} closeModal={() => {}} />
253
+ </TestWrapper>
254
+ );
255
+
256
+ mount(content);
257
+
258
+ expect(store.dispatch, "to have calls satisfying", [
259
+ {
260
+ args: [removeBailout(getTaskInfo("1234"))],
261
+ },
262
+ ]);
263
+
264
+ store.dispatch.resetHistory();
265
+
266
+ jest.runOnlyPendingTimers();
267
+
268
+ expect(store.dispatch, "to have calls satisfying", [
269
+ {
270
+ args: [removeBailout(getTaskInfo("1234"))],
271
+ },
272
+ {
273
+ args: [removeBailout(getTaskLog("1234", false))],
274
+ },
275
+ ]);
276
+ });
277
+
278
+ it.each([taskStatuses.faulted, taskStatuses.ranToCompletion, taskStatuses.canceled, taskStatuses.ignored])(
279
+ "taskLogs is not called because task is completed (%s)",
280
+ newStatus => {
281
+ state = state.setIn(["tasks", "taskInfos", "1234"], { status: newStatus });
282
+
283
+ const content = (
284
+ <TestWrapper
285
+ provider={{ store }}
286
+ memoryRouter
287
+ stylesProvider
288
+ muiThemeProvider={{ theme }}
289
+ intlProvider={{ messages }}
290
+ >
291
+ <TaskDetailsModal taskId="1234" open={true} closeModal={() => {}} />
292
+ </TestWrapper>
293
+ );
294
+
295
+ mount(content);
296
+
297
+ expect(store.dispatch, "not to have calls satisfying", [
298
+ {
299
+ args: [removeBailout(getTaskInfo("1234"))],
300
+ },
301
+ ]);
302
+
303
+ store.dispatch.resetHistory();
304
+
305
+ jest.runOnlyPendingTimers();
306
+
307
+ expect(store.dispatch, "not to have calls satisfying", [
308
+ {
309
+ args: [removeBailout(getTaskInfo("1234"))],
310
+ },
311
+ {
312
+ args: [removeBailout(getTaskLog("1234"))],
313
+ },
314
+ ]);
315
+ },
316
+ );
317
+ });