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

mendersoftware / gui / 944676341

pending completion
944676341

Pull #3875

gitlab-ci

mzedel
chore: aligned snapshots with updated design

Signed-off-by: Manuel Zedel <manuel.zedel@northern.tech>
Pull Request #3875: MEN-5414

4469 of 6446 branches covered (69.33%)

230 of 266 new or added lines in 43 files covered. (86.47%)

1712 existing lines in 161 files now uncovered.

8406 of 10170 relevant lines covered (82.65%)

196.7 hits per line

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

86.67
/src/js/actions/onboardingActions.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 Cookies from 'universal-cookie';
15

16
import { DEVICE_STATES } from '../constants/deviceConstants';
17
import {
18
  SET_DEMO_ARTIFACT_PORT,
19
  SET_ONBOARDING_APPROACH,
20
  SET_ONBOARDING_ARTIFACT_INCLUDED,
21
  SET_ONBOARDING_COMPLETE,
22
  SET_ONBOARDING_DEVICE_TYPE,
23
  SET_ONBOARDING_PROGRESS,
24
  SET_SHOW_CREATE_ARTIFACT,
25
  SET_SHOW_ONBOARDING_HELP,
26
  SET_SHOW_ONBOARDING_HELP_DIALOG,
27
  onboardingSteps as onboardingStepNames
28
} from '../constants/onboardingConstants';
29
import { SET_SHOW_HELP } from '../constants/userConstants';
30
import { getDemoDeviceAddress } from '../helpers';
31
import { getOnboardingState as getCurrentOnboardingState, getUserCapabilities } from '../selectors';
32
import Tracking from '../tracking';
33
import { applyOnboardingFallbacks, onboardingSteps } from '../utils/onboardingmanager';
34
import { saveUserSettings } from './userActions';
35

36
const cookies = new Cookies();
187✔
37

38
const deductOnboardingState = ({ devicesById, devicesByStatus, onboardingState, pastDeployments, releases, userCapabilities, userId }) => {
187✔
39
  const { canDeploy, canManageDevices, canReadDeployments, canReadDevices, canReadReleases, canUploadReleases } = userCapabilities;
8✔
40
  const userCookie = cookies.get(`${userId}-onboarded`);
8✔
41
  const acceptedDevices = devicesByStatus[DEVICE_STATES.accepted].deviceIds;
8✔
42
  const pendingDevices = devicesByStatus[DEVICE_STATES.pending].deviceIds;
8✔
43
  let deviceType = onboardingState.deviceType ?? [];
8✔
44
  deviceType =
8✔
45
    !deviceType.length && acceptedDevices.length && devicesById[acceptedDevices[0]].hasOwnProperty('attributes')
32!
46
      ? devicesById[acceptedDevices[0]].attributes.device_type
47
      : deviceType;
48
  const progress = applyOnboardingFallbacks(onboardingState.progress || determineProgress(acceptedDevices, pendingDevices, releases, pastDeployments));
8✔
49
  return {
8✔
50
    complete: !!(
51
      Boolean(userCookie) ||
90!
52
      onboardingState.complete ||
53
      (acceptedDevices.length > 1 && pendingDevices.length > 0 && releases.length > 1 && pastDeployments.length > 1) ||
54
      (acceptedDevices.length >= 1 && releases.length >= 2 && pastDeployments.length > 2) ||
55
      (acceptedDevices.length >= 1 && pendingDevices.length > 0 && releases.length >= 2 && pastDeployments.length >= 2) ||
56
      Object.keys(onboardingSteps).findIndex(step => step === progress) >= Object.keys(onboardingSteps).length - 1 ||
54✔
57
      onboardingState.disable ||
58
      ![canDeploy, canManageDevices, canReadDeployments, canReadDevices, canReadReleases, canUploadReleases].every(i => i)
36✔
59
    ),
60
    showTips: onboardingState.showTips != null ? onboardingState.showTips : true,
8!
61
    deviceType,
62
    approach: onboardingState.approach || (deviceType.some(type => type.startsWith('qemu')) ? 'virtual' : 'physical'),
8!
63
    artifactIncluded: onboardingState.artifactIncluded,
64
    progress
65
  };
66
};
67

68
export const getOnboardingState = () => (dispatch, getState) => {
187✔
69
  const store = getState();
8✔
70
  let onboardingState = getCurrentOnboardingState(store);
8✔
71
  if (!onboardingState.complete) {
8!
72
    const userId = getState().users.currentUser;
8✔
73
    onboardingState = deductOnboardingState({
8✔
74
      devicesById: store.devices.byId,
75
      devicesByStatus: store.devices.byStatus,
76
      onboardingState,
77
      pastDeployments: store.deployments.byStatus.finished.deploymentIds,
78
      releases: Object.values(store.releases.byId),
79
      userCapabilities: getUserCapabilities(store),
80
      userId
81
    });
82
  }
83
  onboardingState.progress = onboardingState.progress || onboardingStepNames.ONBOARDING_START;
8!
84
  const demoDeviceAddress = `http://${getDemoDeviceAddress(Object.values(store.devices.byId), onboardingState.approach)}`;
8✔
85
  onboardingState.address = store.onboarding.demoArtifactPort ? `${demoDeviceAddress}:${store.onboarding.demoArtifactPort}` : demoDeviceAddress;
8!
86
  const progress = Object.keys(onboardingSteps).findIndex(step => step === onboardingStepNames.ARTIFACT_CREATION_DIALOG);
176✔
87
  const currentProgress = Object.keys(onboardingSteps).findIndex(step => step === onboardingState.progress);
112✔
88
  onboardingState.showCreateArtifactDialog = Math.abs(currentProgress - progress) <= 1;
8✔
89
  if (onboardingState.showCreateArtifactDialog && !onboardingState.complete && onboardingState.showTips && store.users.showHelptips) {
8!
UNCOV
90
    dispatch(setShowCreateArtifactDialog(true));
×
UNCOV
91
    onboardingState.progress = onboardingStepNames.ARTIFACT_CREATION_DIALOG;
×
92
    // although it would be more appropriate to do this in the app component, this happens here because in the app component we would need to track
93
    // redirects, if we want to still allow navigation across the UI while the dialog is visible
UNCOV
94
    if (!window.location.pathname.includes('/ui/releases')) {
×
UNCOV
95
      window.location.replace('/ui/releases');
×
96
    }
97
  }
98
  return Promise.resolve(dispatch(setOnboardingState(onboardingState)));
8✔
99
};
100

101
export const setShowOnboardingHelp =
102
  (show, update = true) =>
187✔
103
  dispatch => {
14✔
104
    let tasks = [dispatch({ type: SET_SHOW_ONBOARDING_HELP, show })];
14✔
105
    if (update) {
14✔
106
      tasks.push(dispatch(saveUserSettings({ onboarding: { showTips: show }, showHelptips: show })));
3✔
107
      tasks.push(dispatch({ type: SET_SHOW_HELP, show }));
3✔
108
    }
109
    return Promise.all(tasks);
14✔
110
  };
111

112
const setOnboardingProgress = value => dispatch => dispatch({ type: SET_ONBOARDING_PROGRESS, value });
187✔
113

114
export const setOnboardingDeviceType =
115
  (value, update = true) =>
187✔
116
  dispatch => {
11✔
117
    let tasks = [dispatch({ type: SET_ONBOARDING_DEVICE_TYPE, value })];
11✔
118
    if (update) {
11✔
119
      tasks.push(dispatch(saveUserSettings({ onboarding: { deviceType: value } })));
3✔
120
    }
121
    return Promise.all(tasks);
11✔
122
  };
123

124
export const setOnboardingApproach =
125
  (value, update = true) =>
187✔
126
  dispatch => {
15✔
127
    let tasks = [dispatch({ type: SET_ONBOARDING_APPROACH, value })];
15✔
128
    if (update) {
15✔
129
      tasks.push(dispatch(saveUserSettings({ onboarding: { approach: value } })));
7✔
130
    }
131
    return Promise.all(tasks);
15✔
132
  };
133

134
const setOnboardingArtifactIncluded = value => dispatch => dispatch({ type: SET_ONBOARDING_ARTIFACT_INCLUDED, value });
187✔
135

136
export const setShowCreateArtifactDialog = show => dispatch => dispatch({ type: SET_SHOW_CREATE_ARTIFACT, show });
187✔
137

138
export const setShowDismissOnboardingTipsDialog = show => dispatch => dispatch({ type: SET_SHOW_ONBOARDING_HELP_DIALOG, show });
187✔
139

140
export const setDemoArtifactPort = port => dispatch => dispatch({ type: SET_DEMO_ARTIFACT_PORT, value: port });
187✔
141

142
export const setOnboardingComplete = val => dispatch => {
187✔
143
  let tasks = [Promise.resolve(dispatch({ type: SET_ONBOARDING_COMPLETE, complete: val }))];
21✔
144
  if (val) {
21✔
145
    tasks.push(Promise.resolve(dispatch({ type: SET_SHOW_ONBOARDING_HELP, show: false })));
6✔
146
    tasks.push(Promise.resolve(dispatch(advanceOnboarding(onboardingStepNames.ONBOARDING_FINISHED))));
6✔
147
  }
148
  return Promise.all(tasks);
21✔
149
};
150

151
export const setOnboardingCanceled = () => dispatch =>
187✔
152
  Promise.all([
3✔
153
    Promise.resolve(dispatch(setShowOnboardingHelp(false, false))),
154
    Promise.resolve(dispatch(setShowDismissOnboardingTipsDialog(false))),
155
    Promise.resolve(dispatch({ type: SET_ONBOARDING_COMPLETE, complete: true }))
156
  ])
157
    // using ONBOARDING_FINISHED_NOTIFICATION to ensure we get the intended onboarding state set after
158
    // _advancing_ the onboarding progress
159
    .then(() => dispatch(advanceOnboarding(onboardingStepNames.ONBOARDING_FINISHED_NOTIFICATION)))
3✔
160
    // since we can't advance after ONBOARDING_CANCELED, track the step manually here
161
    .then(() => Tracking.event({ category: 'onboarding', action: onboardingSteps.ONBOARDING_CANCELED }));
3✔
162

163
const setOnboardingState = state => dispatch =>
187✔
164
  Promise.all([
8✔
165
    dispatch(setOnboardingComplete(state.complete)),
166
    dispatch(setOnboardingDeviceType(state.deviceType, false)),
167
    dispatch(setOnboardingApproach(state.approach, false)),
168
    dispatch(setOnboardingArtifactIncluded(state.artifactIncluded)),
169
    dispatch(setShowOnboardingHelp(state.showTips, false)),
170
    dispatch(setOnboardingProgress(state.progress)),
171
    dispatch(setShowCreateArtifactDialog(state.showCreateArtifactDialog && !state.complete && state.showTips)),
8!
172
    dispatch(saveUserSettings({ onboarding: state }))
173
  ]);
174

175
export const advanceOnboarding = stepId => (dispatch, getState) => {
187✔
176
  const steps = Object.keys(onboardingSteps);
34✔
177
  const progress = steps.findIndex(step => step === getState().onboarding.progress);
868✔
178
  const stepIndex = steps.findIndex(step => step === stepId);
575✔
179
  // if there is no progress set yet, the onboarding state deduction hasn't happened
180
  // and the subsequent settings persistence would overwrite what we stored
181
  if (progress > stepIndex || getState().onboarding.progress === null) {
34✔
182
    return;
12✔
183
  }
184
  const madeProgress = steps[stepIndex + 1];
22✔
185
  const state = { ...getCurrentOnboardingState(getState()), progress: madeProgress };
22✔
186
  state.complete = stepIndex + 1 >= Object.keys(onboardingSteps).findIndex(step => step === onboardingStepNames.ONBOARDING_FINISHED) ? true : state.complete;
660✔
187
  Tracking.event({ category: 'onboarding', action: stepId });
22✔
188
  return Promise.all([dispatch(setOnboardingProgress(madeProgress)), dispatch(saveUserSettings({ onboarding: state }))]);
22✔
189
};
190

191
const determineProgress = (acceptedDevices, pendingDevices, releases, pastDeployments) => {
187✔
192
  const steps = Object.keys(onboardingSteps);
8✔
193
  let progress = -1;
8✔
194
  progress = pendingDevices.length > 1 ? steps.findIndex(step => step === onboardingStepNames.DEVICES_PENDING_ACCEPTING_ONBOARDING) : progress;
8!
195
  progress = acceptedDevices.length >= 1 ? steps.findIndex(step => step === onboardingStepNames.APPLICATION_UPDATE_REMINDER_TIP) : progress;
72!
196
  progress =
8✔
197
    acceptedDevices.length > 1 && releases.length > 1 ? steps.findIndex(step => step === onboardingStepNames.APPLICATION_UPDATE_REMINDER_TIP) : progress;
18✔
198
  progress =
8✔
199
    acceptedDevices.length > 1 && releases.length > 1 && pastDeployments.length > 1
26✔
200
      ? steps.findIndex(step => step === onboardingStepNames.DEPLOYMENTS_PAST_COMPLETED)
40✔
201
      : progress;
202
  progress =
8✔
203
    acceptedDevices.length >= 1 && releases.length >= 2 && pastDeployments.length > 1
26✔
204
      ? steps.findIndex(step => step === onboardingStepNames.ARTIFACT_MODIFIED_ONBOARDING)
58✔
205
      : progress;
206
  progress =
8✔
207
    acceptedDevices.length >= 1 && releases.length >= 2 && pastDeployments.length > 2
26!
UNCOV
208
      ? steps.findIndex(step => step === onboardingStepNames.ONBOARDING_FINISHED)
×
209
      : progress;
210
  return steps[progress];
8✔
211
};
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