• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

mendersoftware / gui / 951400782

pending completion
951400782

Pull #3900

gitlab-ci

web-flow
chore: bump @testing-library/jest-dom from 5.16.5 to 5.17.0

Bumps [@testing-library/jest-dom](https://github.com/testing-library/jest-dom) from 5.16.5 to 5.17.0.
- [Release notes](https://github.com/testing-library/jest-dom/releases)
- [Changelog](https://github.com/testing-library/jest-dom/blob/main/CHANGELOG.md)
- [Commits](https://github.com/testing-library/jest-dom/compare/v5.16.5...v5.17.0)

---
updated-dependencies:
- dependency-name: "@testing-library/jest-dom"
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Pull Request #3900: chore: bump @testing-library/jest-dom from 5.16.5 to 5.17.0

4446 of 6414 branches covered (69.32%)

8342 of 10084 relevant lines covered (82.73%)

186.0 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

78.63
/src/js/components/deployments/deployments.js
1
// Copyright 2015 Northern.tech AS
2
//
3
//    Licensed under the Apache License, Version 2.0 (the "License");
4
//    you may not use this file except in compliance with the License.
5
//    You may obtain a copy of the License at
6
//
7
//        http://www.apache.org/licenses/LICENSE-2.0
8
//
9
//    Unless required by applicable law or agreed to in writing, software
10
//    distributed under the License is distributed on an "AS IS" BASIS,
11
//    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
//    See the License for the specific language governing permissions and
13
//    limitations under the License.
14
import React, { useEffect, useRef, useState } from 'react';
15
import { useDispatch, useSelector } from 'react-redux';
16
import { Link, useNavigate } from 'react-router-dom';
17

18
import { Button, Tab, Tabs } from '@mui/material';
19

20
import { setSnackbar } from '../../actions/appActions';
21
import { abortDeployment, setDeploymentsState } from '../../actions/deploymentActions';
22
import { getDynamicGroups, getGroups } from '../../actions/deviceActions';
23
import { advanceOnboarding } from '../../actions/onboardingActions';
24
import { DEPLOYMENT_ROUTES, DEPLOYMENT_STATES, listDefaultsByState } from '../../constants/deploymentConstants';
25
import { ALL_DEVICES } from '../../constants/deviceConstants';
26
import { onboardingSteps } from '../../constants/onboardingConstants';
27
import { getISOStringBoundaries } from '../../helpers';
28
import { getDevicesById, getGroupsByIdWithoutUngrouped, getIsEnterprise, getOnboardingState, getReleasesById, getUserCapabilities } from '../../selectors';
29
import { useLocationParams } from '../../utils/liststatehook';
30
import { getOnboardingComponentFor } from '../../utils/onboardingmanager';
31
import useWindowSize from '../../utils/resizehook';
32
import CreateDeployment from './createdeployment';
33
import Progress from './inprogressdeployments';
34
import Past from './pastdeployments';
35
import Report from './report';
36
import Scheduled from './scheduleddeployments';
37

38
const routes = {
7✔
39
  [DEPLOYMENT_ROUTES.active.key]: {
40
    ...DEPLOYMENT_ROUTES.active,
41
    component: Progress
42
  },
43
  [DEPLOYMENT_ROUTES.scheduled.key]: {
44
    ...DEPLOYMENT_ROUTES.scheduled,
45
    component: Scheduled
46
  },
47
  [DEPLOYMENT_ROUTES.finished.key]: {
48
    ...DEPLOYMENT_ROUTES.finished,
49
    component: Past
50
  }
51
};
52

53
export const defaultRefreshDeploymentsLength = 30000;
7✔
54

55
export const Deployments = () => {
7✔
56
  const groupsById = useSelector(getGroupsByIdWithoutUngrouped);
202✔
57
  const devicesById = useSelector(getDevicesById);
201✔
58
  const isEnterprise = useSelector(getIsEnterprise);
201✔
59
  const onboardingState = useSelector(getOnboardingState);
201✔
60
  const pastCount = useSelector(state => state.deployments.byStatus.finished.total);
490✔
61
  const releases = useSelector(getReleasesById);
201✔
62
  const selectionState = useSelector(state => state.deployments.selectionState);
490✔
63
  const userCapabilities = useSelector(getUserCapabilities);
201✔
64
  const dispatch = useDispatch();
201✔
65

66
  const [deploymentObject, setDeploymentObject] = useState({});
201✔
67
  // eslint-disable-next-line no-unused-vars
68
  const size = useWindowSize();
201✔
69
  const tabsRef = useRef();
201✔
70
  const isInitialized = useRef(false);
201✔
71
  const navigate = useNavigate();
201✔
72
  const { reportType, showCreationDialog: createDialog, showReportDialog: reportDialog, state } = selectionState.general;
201✔
73
  const { canDeploy, canReadReleases } = userCapabilities;
201✔
74

75
  const [date] = useState(getISOStringBoundaries(new Date()));
201✔
76
  const { start: today, end: tonight } = date;
201✔
77

78
  const [locationParams, setLocationParams] = useLocationParams('deployments', { today, tonight, defaults: listDefaultsByState });
201✔
79

80
  useEffect(() => {
201✔
81
    if (!isInitialized.current) {
27✔
82
      return;
6✔
83
    }
84
    setLocationParams({ deploymentObject, pageState: selectionState });
21✔
85
  }, [
86
    selectionState.selectedId,
87
    selectionState.general.state,
88
    selectionState.general.showCreationDialog,
89
    selectionState.general.showReportDialog,
90
    selectionState.general.reportType,
91
    selectionState[DEPLOYMENT_STATES.finished].endDate,
92
    selectionState[DEPLOYMENT_STATES.finished].search,
93
    selectionState[DEPLOYMENT_STATES.finished].startDate,
94
    selectionState[DEPLOYMENT_STATES.finished].page,
95
    selectionState[DEPLOYMENT_STATES.finished].perPage,
96
    selectionState[DEPLOYMENT_STATES.finished].type,
97
    selectionState[DEPLOYMENT_STATES.inprogress].page,
98
    selectionState[DEPLOYMENT_STATES.inprogress].perPage,
99
    selectionState[DEPLOYMENT_STATES.pending].page,
100
    selectionState[DEPLOYMENT_STATES.pending].perPage
101
  ]);
102

103
  useEffect(() => {
201✔
104
    dispatch(getGroups());
6✔
105
    if (isEnterprise) {
6✔
106
      dispatch(getDynamicGroups());
2✔
107
    }
108
    const { deploymentObject = {}, id: selectedId = [], ...remainder } = locationParams;
6!
109
    const { devices: selectedDevices = [], release: releaseName } = deploymentObject;
6✔
110
    const release = releaseName ? { ...(releases[releaseName] ?? { Name: releaseName }) } : undefined;
6!
111
    const devices = selectedDevices.length ? selectedDevices.map(device => ({ ...device, ...devicesById[device.id] })) : [];
6!
112
    setDeploymentObject({ devices, release });
6✔
113
    dispatch(setDeploymentsState({ selectedId: selectedId[0], ...remainder }));
6✔
114
    isInitialized.current = true;
6✔
115
  }, []);
116

117
  const retryDeployment = (deployment, deploymentDeviceIds) => {
201✔
118
    const { artifact_name, name, update_control_map = {} } = deployment;
×
119
    const release = releases[artifact_name];
×
120
    const enterpriseSettings = isEnterprise
×
121
      ? {
122
          phases: [{ batch_size: 100, start_ts: undefined, delay: 0 }],
123
          update_control_map: { states: update_control_map.states || {} }
×
124
        }
125
      : {};
126
    const targetDevicesConfig = name === ALL_DEVICES || groupsById[name] ? { group: name } : { devices: [devicesById[name]] };
×
127
    const deploymentObject = {
×
128
      deploymentDeviceIds,
129
      release,
130
      deploymentDeviceCount: deploymentDeviceIds.length,
131
      ...targetDevicesConfig,
132
      ...enterpriseSettings
133
    };
134
    setDeploymentObject(deploymentObject);
×
135
    dispatch(setDeploymentsState({ general: { showCreationDialog: true, showReportDialog: false } }));
×
136
  };
137

138
  const onScheduleSubmit = () => {
201✔
139
    dispatch(setDeploymentsState({ general: { showCreationDialog: false, showReportDialog: false } }));
2✔
140
    setDeploymentObject({});
2✔
141
    // successfully retrieved new deployment
142
    if (routes.active.key !== state) {
2✔
143
      navigate(routes.active.route);
1✔
144
      changeTab(undefined, routes.active.key);
1✔
145
    }
146
  };
147

148
  const onAbortDeployment = id =>
201✔
149
    dispatch(abortDeployment(id)).then(() => {
×
150
      dispatch(setDeploymentsState({ general: { showCreationDialog: false, showReportDialog: false } }));
×
151
      return Promise.resolve();
×
152
    });
153

154
  const changeTab = (_, tabIndex) => {
201✔
155
    dispatch(setDeploymentsState({ general: { state: tabIndex } }));
5✔
156
    dispatch(setSnackbar(''));
5✔
157
    if (pastCount && !onboardingState.complete) {
5✔
158
      dispatch(advanceOnboarding(onboardingSteps.DEPLOYMENTS_PAST));
1✔
159
    }
160
  };
161

162
  const showReport = (reportType, selectedId) => {
201✔
163
    if (!onboardingState.complete) {
1!
164
      dispatch(advanceOnboarding(onboardingSteps.DEPLOYMENTS_INPROGRESS));
1✔
165
    }
166
    dispatch(setDeploymentsState({ general: { reportType, showCreationDialog: false, showReportDialog: true }, selectedId }));
1✔
167
  };
168

169
  const closeReport = () => dispatch(setDeploymentsState({ general: { reportType: undefined, showReportDialog: false }, selectedId: undefined }));
201✔
170

171
  const onCreationDismiss = () => {
201✔
172
    dispatch(setDeploymentsState({ general: { showCreationDialog: false } }));
1✔
173
    setDeploymentObject({});
1✔
174
  };
175

176
  const onCreationShow = () => dispatch(setDeploymentsState({ general: { showCreationDialog: true } }));
201✔
177

178
  let onboardingComponent = null;
201✔
179
  // the pastCount prop is needed to trigger the rerender as the change in past deployments would otherwise not be noticed on this view
180
  if (pastCount && tabsRef.current && !reportDialog) {
201✔
181
    const tabs = tabsRef.current.getElementsByClassName('MuiTab-root');
157✔
182
    const finishedTab = tabs[tabs.length - 1];
157✔
183
    onboardingComponent = getOnboardingComponentFor(onboardingSteps.DEPLOYMENTS_PAST, onboardingState, {
157✔
184
      anchor: {
185
        left: tabsRef.current.offsetLeft + tabsRef.current.offsetWidth - finishedTab.offsetWidth / 2,
186
        top: tabsRef.current.parentElement.offsetTop + finishedTab.offsetHeight
187
      }
188
    });
189
  }
190

191
  const ComponentToShow = routes[state].component;
201✔
192
  return (
201✔
193
    <>
194
      <div className="margin-left-small margin-top" style={{ maxWidth: '80vw' }}>
195
        <div className="flexbox space-between">
196
          <Tabs value={state} onChange={changeTab} ref={tabsRef}>
197
            {Object.values(routes).map(route => (
198
              <Tab component={Link} key={route.route} label={route.title} to={route.route} value={route.key} />
603✔
199
            ))}
200
          </Tabs>
201
          {canDeploy && canReadReleases && (
603✔
202
            <Button color="secondary" variant="contained" onClick={onCreationShow} style={{ height: '100%' }}>
203
              Create a deployment
204
            </Button>
205
          )}
206
        </div>
207
        <ComponentToShow abort={onAbortDeployment} createClick={onCreationShow} openReport={showReport} isShowingDetails={reportDialog} />
208
      </div>
209
      <Report abort={onAbortDeployment} onClose={closeReport} open={reportDialog} retry={retryDeployment} type={reportType} />
210
      <CreateDeployment
211
        open={createDialog}
212
        onDismiss={onCreationDismiss}
213
        deploymentObject={deploymentObject}
214
        onScheduleSubmit={onScheduleSubmit}
215
        setDeploymentObject={setDeploymentObject}
216
      />
217
      {!reportDialog && onboardingComponent}
400✔
218
    </>
219
  );
220
};
221

222
export default Deployments;
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc