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

mendersoftware / mender-server / 10423

11 Nov 2025 04:53PM UTC coverage: 74.435% (-0.1%) from 74.562%
10423

push

gitlab-ci

web-flow
Merge pull request #1071 from mendersoftware/dependabot/npm_and_yarn/frontend/main/development-dependencies-92732187be

3868 of 5393 branches covered (71.72%)

Branch coverage included in aggregate %.

5 of 5 new or added lines in 2 files covered. (100.0%)

176 existing lines in 95 files now uncovered.

64605 of 86597 relevant lines covered (74.6%)

7.74 hits per line

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

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

2✔
16
import { Autocomplete, Checkbox, Collapse, FormControl, FormControlLabel, FormGroup, TextField } from '@mui/material';
2✔
17
import { makeStyles } from 'tss-react/mui';
2✔
18

2✔
19
import { DOCSTIPS, DocsTooltip } from '@northern.tech/common-ui/DocsLink';
2✔
20
import EnterpriseNotification from '@northern.tech/common-ui/EnterpriseNotification';
2✔
21
import { InfoHintContainer } from '@northern.tech/common-ui/InfoHint';
2✔
22
import { BENEFITS, TIMEOUTS } from '@northern.tech/store/constants';
2✔
23
import { useDebounce } from '@northern.tech/utils/debouncehook';
2✔
24
import { toggle } from '@northern.tech/utils/helpers';
2✔
25

2✔
26
import { HELPTOOLTIPS } from '../../helptips/HelpTooltips';
2✔
27
import { MenderHelpTooltip } from '../../helptips/MenderTooltip';
2✔
28
import RolloutSteps from './RolloutSteps';
2✔
29

2✔
30
const useStyles = makeStyles()(() => ({
17✔
31
  defaultBox: { marginTop: 0, marginBottom: -15 },
2✔
32
  heading: { marginBottom: 0 },
2✔
33
  retryInput: { maxWidth: 150, minWidth: 130 },
2✔
34
  wrapper: { minHeight: 300 }
2✔
35
}));
2✔
36

2✔
37
export const ForceDeploy = ({ deploymentObject, setDeploymentSettings }) => {
7✔
38
  const [forceDeploy, setForceDeploy] = useState(deploymentObject.forceDeploy ?? false);
120✔
39
  const { classes } = useStyles();
120✔
40

2✔
41
  useEffect(() => {
120✔
42
    setDeploymentSettings({ forceDeploy });
7✔
43
  }, [forceDeploy, setDeploymentSettings]);
2✔
44

2✔
45
  return (
120✔
46
    <div>
2✔
47
      <FormControlLabel
2✔
48
        className={classes.heading}
2✔
UNCOV
49
        control={<Checkbox color="primary" checked={forceDeploy} onChange={() => setForceDeploy(toggle)} size="small" />}
2✔
50
        label={
2✔
51
          <div className="flexbox center-aligned">
2✔
52
            <b className="margin-right-small">Force update</b> (optional)
2✔
53
            <MenderHelpTooltip
2✔
54
              id={HELPTOOLTIPS.forceDeployment.id}
2✔
55
              disableFocusListener={false}
2✔
56
              disableHoverListener={false}
2✔
57
              disableTouchListener={false}
2✔
58
              style={{ marginLeft: 15 }}
2✔
59
            />
2✔
60
          </div>
2✔
61
        }
2✔
62
      />
2✔
63
    </div>
2✔
64
  );
2✔
65
};
2✔
66

2✔
67
export const RolloutOptions = ({ deploymentObject, isEnterprise, setDeploymentSettings }) => {
7✔
68
  const { phases = [], release = {} } = deploymentObject;
120✔
69
  const { classes } = useStyles();
120✔
70

2✔
71
  const { states = {} } = deploymentObject.update_control_map || {};
120✔
72
  const [isPaused, setIsPaused] = useState(!!Object.keys(states).length);
120✔
73

2✔
74
  const onStepChangeClick = step => {
120✔
75
    const { action } = step;
2✔
76
    setDeploymentSettings({ update_control_map: { states: { ...states, [step.state]: { action } } } });
2✔
77
  };
2✔
78

2✔
79
  const onIsPausedClick = () => setIsPaused(toggle);
120✔
80

2✔
81
  return (
120✔
82
    <>
2✔
83
      <FormControlLabel
2✔
84
        className={classes.heading}
2✔
85
        control={<Checkbox color="primary" checked={isPaused} disabled={!isEnterprise} onChange={onIsPausedClick} size="small" />}
2✔
86
        label={
2✔
87
          <div className="flexbox center-aligned">
2✔
88
            <b className="margin-right-small">Add pauses between update steps</b> (optional)
2✔
89
            <InfoHintContainer>
2✔
90
              <EnterpriseNotification id={BENEFITS.pausedDeployments.id} />
2✔
91
              <DocsTooltip id={DOCSTIPS.pausedDeployments.id} />
2✔
92
            </InfoHintContainer>
2✔
93
          </div>
2✔
94
        }
2✔
95
      />
2✔
96
      <Collapse in={isPaused} className={classes.wrapper}>
2✔
97
        <RolloutSteps disabled={phases.length > 1 || !isEnterprise} onStepChange={onStepChangeClick} release={release} steps={states} />
2✔
98
      </Collapse>
2✔
99
    </>
2✔
100
  );
2✔
101
};
2✔
102

2✔
103
export const Retries = ({
7✔
104
  canRetry,
2✔
105
  commonClasses,
2✔
106
  deploymentObject,
2✔
107
  hasNewRetryDefault = false,
2✔
108
  onSaveRetriesSetting,
2✔
109
  previousRetries,
2✔
110
  setDeploymentSettings
2✔
111
}) => {
2✔
112
  const { retries } = deploymentObject;
123✔
113
  const { classes } = useStyles();
123✔
114

2✔
115
  const [currentAttempts, setCurrentAttempts] = useState(Number(retries ?? previousRetries ?? 0) + 1);
123!
116
  const debouncedAttempts = useDebounce(currentAttempts, TIMEOUTS.debounceShort);
123✔
117

2✔
118
  useEffect(() => {
123✔
119
    setDeploymentSettings({ retries: Number(debouncedAttempts) - 1 });
8✔
120
  }, [debouncedAttempts, setDeploymentSettings]);
2✔
121

2✔
122
  const formatValue = value => {
123✔
123
    const newValue = Math.max(0, Math.min(value, 100));
416✔
124
    return newValue ? `${newValue}` : '';
416!
125
  };
2✔
126

2✔
127
  const onInputChange = (e, value, reason) => {
123✔
128
    if (reason === 'clear') {
3!
129
      return setDeploymentSettings({ retries: 0 });
2✔
130
    } else if ((reason === 'reset' && !e) || reason === 'blur') {
3!
131
      return;
2✔
132
    }
2✔
133
    setCurrentAttempts(formatValue(value));
3✔
134
  };
2✔
135

2✔
136
  const onSaveRetriesSettingClick = (_, checked) => onSaveRetriesSetting(checked);
123✔
137

2✔
138
  return (
123✔
139
    <>
2✔
140
      <div className="flexbox center-aligned margin-top-small margin-bottom-small">
2✔
141
        <b className={canRetry ? '' : commonClasses.disabled}>Select the number of times each device will attempt to apply the update</b>
2✔
142
        <InfoHintContainer>
2✔
143
          <EnterpriseNotification id={BENEFITS.retryDeployments.id} />
2✔
144
          <DocsTooltip id={DOCSTIPS.phasedDeployments.id} />
2✔
145
        </InfoHintContainer>
2✔
146
      </div>
2✔
147
      <FormControl disabled={!canRetry}>
2✔
148
        <FormGroup row>
2✔
149
          <Autocomplete
2✔
150
            autoSelect
2✔
151
            autoHighlight
2✔
152
            className={`margin-right ${classes.retryInput}`}
2✔
153
            freeSolo
2✔
154
            disabled={!canRetry}
2✔
155
            getOptionLabel={formatValue}
2✔
156
            handleHomeEndKeys
2✔
157
            id="deployment-retries-selection"
2✔
158
            options={[1, 2, 3, 4]}
2✔
159
            onInputChange={onInputChange}
2✔
160
            renderInput={params => (
2✔
161
              <TextField
127✔
162
                {...params}
2✔
163
                className={classes.retryInput}
2✔
164
                slotProps={{
2✔
165
                  htmlInput: { ...params.inputProps, value: formatValue(params.inputProps.value) },
2✔
166
                  input: { ...params.InputProps }
2✔
167
                }}
2✔
168
                type="number"
2✔
169
              />
2✔
170
            )}
2✔
171
            value={currentAttempts}
2✔
172
          />
2✔
173
          <FormControlLabel
2✔
174
            className={classes.defaultBox}
2✔
175
            control={<Checkbox checked={hasNewRetryDefault} onChange={onSaveRetriesSettingClick} />}
2✔
176
            label="Save as default"
2✔
177
          />
2✔
178
        </FormGroup>
2✔
179
      </FormControl>
2✔
180
    </>
2✔
181
  );
2✔
182
};
2✔
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