• 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

60.82
/src/js/components/deployments/scheduleddeployments.js
1
// Copyright 2020 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, { useCallback, useEffect, useRef, useState } from 'react';
15
import { Calendar, momentLocalizer } from 'react-big-calendar';
16
import 'react-big-calendar/lib/css/react-big-calendar.css';
17
import { useDispatch, useSelector } from 'react-redux';
18

19
import { CalendarToday as CalendarTodayIcon, List as ListIcon, Refresh as RefreshIcon } from '@mui/icons-material';
20
import { Button } from '@mui/material';
21
import { makeStyles } from 'tss-react/mui';
22

23
import moment from 'moment';
24

25
import { setSnackbar } from '../../actions/appActions';
26
import { getDeploymentsByStatus, setDeploymentsState } from '../../actions/deploymentActions';
27
import { DEPLOYMENT_STATES } from '../../constants/deploymentConstants';
28
import {
29
  getDeploymentsByStatus as getDeploymentsByStatusSelector,
30
  getDeploymentsSelectionState,
31
  getDevicesById,
32
  getIdAttribute,
33
  getMappedDeploymentSelection,
34
  getTenantCapabilities,
35
  getUserCapabilities
36
} from '../../selectors';
37
import { clearAllRetryTimers, clearRetryTimer, setRetryTimer } from '../../utils/retrytimer';
38
import EnterpriseNotification from '../common/enterpriseNotification';
39
import { DeploymentDeviceCount, DeploymentEndTime, DeploymentPhases, DeploymentStartTime } from './deploymentitem';
40
import { defaultRefreshDeploymentsLength as refreshDeploymentsLength } from './deployments';
41
import DeploymentsList, { defaultHeaders } from './deploymentslist';
42

43
const useStyles = makeStyles()(theme => ({
7✔
44
  inactive: { color: theme.palette.text.disabled },
45
  refreshIcon: { fill: theme.palette.grey[400], width: 111, height: 111 },
46
  tabSelect: { textTransform: 'none' }
47
}));
48

49
const localizer = momentLocalizer(moment);
7✔
50

51
const headers = [
7✔
52
  ...defaultHeaders.slice(0, 2),
53
  { title: 'Start time', renderer: DeploymentStartTime, props: { direction: 'up' } },
54
  { title: `End time`, renderer: DeploymentEndTime },
55
  { title: '# devices', class: 'align-right column-defined', renderer: DeploymentDeviceCount },
56
  { title: 'Phases', renderer: DeploymentPhases }
57
];
58

59
const tabs = {
7✔
60
  list: {
61
    icon: <ListIcon />,
62
    index: 'list',
63
    title: 'List view'
64
  },
65
  calendar: {
66
    icon: <CalendarTodayIcon />,
67
    index: 'calendar',
68
    title: 'Calendar'
69
  }
70
};
71

72
const type = DEPLOYMENT_STATES.scheduled;
7✔
73

74
export const Scheduled = ({ abort, createClick, openReport, ...remainder }) => {
7✔
75
  const [calendarEvents, setCalendarEvents] = useState([]);
3✔
76
  const [tabIndex, setTabIndex] = useState(tabs.list.index);
3✔
77
  const timer = useRef();
3✔
78
  const { canConfigure, canDeploy } = useSelector(getUserCapabilities);
3✔
79
  const {
80
    scheduled: { total: count }
81
  } = useSelector(getDeploymentsByStatusSelector);
3✔
82
  const { attribute: idAttribute } = useSelector(getIdAttribute);
3✔
83
  const devices = useSelector(getDevicesById);
3✔
84
  // TODO: isEnterprise is misleading here, but is passed down to the DeploymentListItem, this should be renamed
85
  const { canDelta: isEnterprise } = useSelector(getTenantCapabilities);
3✔
86
  const { scheduled: scheduledState } = useSelector(getDeploymentsSelectionState);
3✔
87
  const items = useSelector(state => getMappedDeploymentSelection(state, type));
8✔
88
  const dispatch = useDispatch();
3✔
89
  const dispatchedSetSnackbar = (...args) => dispatch(setSnackbar(...args));
3✔
90
  const { classes } = useStyles();
3✔
91

92
  const { page, perPage } = scheduledState;
3✔
93

94
  useEffect(() => {
3✔
95
    if (!isEnterprise) {
2!
96
      return;
×
97
    }
98
    refreshDeployments();
2✔
99
    return () => {
2✔
100
      clearAllRetryTimers(dispatchedSetSnackbar);
2✔
101
    };
102
  }, [isEnterprise]);
103

104
  useEffect(() => {
3✔
105
    if (!isEnterprise) {
2!
106
      return;
×
107
    }
108
    clearInterval(timer.current);
2✔
109
    timer.current = setInterval(refreshDeployments, refreshDeploymentsLength);
2✔
110
    return () => {
2✔
111
      clearInterval(timer.current);
2✔
112
    };
113
  }, [isEnterprise, page, perPage]);
114

115
  useEffect(() => {
3✔
116
    if (tabIndex !== tabs.calendar.index) {
2!
117
      return;
2✔
118
    }
119
    const calendarEvents = items.map(deployment => {
×
120
      const start = new Date(deployment.start_ts || deployment.phases ? deployment.phases[0].start_ts : deployment.created);
×
121
      let endDate = start;
×
122
      if (deployment.phases && deployment.phases.length && deployment.phases[deployment.phases.length - 1].end_ts) {
×
123
        endDate = new Date(deployment.phases[deployment.phases.length - 1].end_ts);
×
124
      } else if (deployment.filter_id || deployment.filter) {
×
125
        // calendar doesn't support never ending events so we arbitrarly set one year
126
        endDate = moment(start).add(1, 'year');
×
127
      }
128
      return {
×
129
        allDay: !(deployment.filter_id || deployment.filter),
×
130
        id: deployment.id,
131
        title: `${deployment.name} ${deployment.artifact_name}`,
132
        start,
133
        end: endDate
134
      };
135
    });
136
    setCalendarEvents(calendarEvents);
×
137
  }, [tabIndex]);
138

139
  const refreshDeployments = useCallback(() => {
3✔
140
    return dispatch(getDeploymentsByStatus(DEPLOYMENT_STATES.scheduled, page, perPage))
2✔
141
      .then(deploymentsAction => {
142
        clearRetryTimer(type, dispatchedSetSnackbar);
1✔
143
        const { total, deploymentIds } = deploymentsAction[deploymentsAction.length - 1];
1✔
144
        if (total && !deploymentIds.length) {
1!
145
          return refreshDeployments();
×
146
        }
147
      })
148
      .catch(err => setRetryTimer(err, 'deployments', `Couldn't load deployments.`, refreshDeploymentsLength, dispatchedSetSnackbar));
×
149
  }, [page, perPage]);
150

151
  const abortDeployment = id => abort(id).then(refreshDeployments);
3✔
152

153
  const props = {
3✔
154
    ...remainder,
155
    canDeploy,
156
    canConfigure,
157
    count,
158
    devices,
159
    idAttribute,
160
    isEnterprise,
161
    items,
162
    openReport,
163
    page
164
  };
165
  return (
3✔
166
    <div className="fadeIn margin-left">
167
      {items.length ? (
3✔
168
        <>
169
          <div className="margin-large margin-left-small">
170
            {Object.entries(tabs).map(([currentIndex, tab]) => (
171
              <Button
4✔
172
                className={`${classes.tabSelect} ${currentIndex !== tabIndex ? classes.inactive : ''}`}
4✔
173
                color="primary"
174
                key={currentIndex}
175
                startIcon={tab.icon}
176
                onClick={() => setTabIndex(currentIndex)}
×
177
              >
178
                {tab.title}
179
              </Button>
180
            ))}
181
          </div>
182
          {tabIndex === tabs.list.index && (
4✔
183
            <DeploymentsList
184
              {...props}
185
              abort={abortDeployment}
186
              headers={headers}
187
              type={type}
188
              onChangeRowsPerPage={perPage => dispatch(setDeploymentsState({ [DEPLOYMENT_STATES.scheduled]: { page: 1, perPage } }))}
×
189
              onChangePage={page => dispatch(setDeploymentsState({ [DEPLOYMENT_STATES.scheduled]: { page } }))}
×
190
            />
191
          )}
192
          {tabIndex === tabs.calendar.index && (
2!
193
            <Calendar
194
              localizer={localizer}
195
              className="margin-left margin-bottom"
196
              events={calendarEvents}
197
              startAccessor="start"
198
              endAccessor="end"
199
              style={{ height: 700 }}
200
              onSelectEvent={calendarEvent => openReport(type, calendarEvent.id)}
×
201
            />
202
          )}
203
        </>
204
      ) : (
205
        <div className="dashboard-placeholder margin-top">
206
          {isEnterprise ? (
1!
207
            <>
208
              <p>Scheduled deployments will appear here. </p>
209
              {canDeploy && (
2✔
210
                <p>
211
                  <a onClick={createClick}>Create a deployment</a> to get started
212
                </p>
213
              )}
214
            </>
215
          ) : (
216
            <div className="flexbox centered">
217
              <EnterpriseNotification isEnterprise={isEnterprise} benefit="scheduled deployments to steer the distribution of your updates." />
218
            </div>
219
          )}
220
          <RefreshIcon className={`flip-horizontal ${classes.refreshIcon}`} />
221
        </div>
222
      )}
223
    </div>
224
  );
225
};
226

227
export default Scheduled;
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