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

mendersoftware / gui / 1081664682

22 Nov 2023 02:11PM UTC coverage: 82.798% (-17.2%) from 99.964%
1081664682

Pull #4214

gitlab-ci

tranchitella
fix: Fixed the infinite page redirects when the back button is pressed

Remove the location and navigate from the useLocationParams.setValue callback
dependencies as they change the set function that is presented in other
useEffect dependencies. This happens when the back button is clicked, which
leads to the location changing infinitely.

Changelog: Title
Ticket: MEN-6847
Ticket: MEN-6796

Signed-off-by: Ihor Aleksandrychiev <ihor.aleksandrychiev@northern.tech>
Signed-off-by: Fabio Tranchitella <fabio.tranchitella@northern.tech>
Pull Request #4214: fix: Fixed the infinite page redirects when the back button is pressed

4319 of 6292 branches covered (0.0%)

8332 of 10063 relevant lines covered (82.8%)

191.0 hits per line

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

98.68
/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_COMPLETE,
21
  SET_ONBOARDING_DEVICE_TYPE,
22
  SET_ONBOARDING_PROGRESS,
23
  SET_ONBOARDING_STATE,
24
  SET_SHOW_ONBOARDING_HELP,
25
  SET_SHOW_ONBOARDING_HELP_DIALOG,
26
  onboardingSteps as onboardingStepNames
27
} from '../constants/onboardingConstants';
28
import { getDemoDeviceAddress } from '../helpers';
29
import { getOnboardingState as getCurrentOnboardingState, getUserCapabilities } from '../selectors';
30
import Tracking from '../tracking';
31
import { applyOnboardingFallbacks, onboardingSteps } from '../utils/onboardingmanager';
32
import { saveUserSettings } from './userActions';
33

34
const cookies = new Cookies();
183✔
35

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

66
export const getOnboardingState = () => (dispatch, getState) => {
183✔
67
  const store = getState();
6✔
68
  let onboardingState = getCurrentOnboardingState(store);
6✔
69
  if (!onboardingState.complete) {
6!
70
    const userId = getState().users.currentUser;
6✔
71
    onboardingState = deductOnboardingState({
6✔
72
      devicesById: store.devices.byId,
73
      devicesByStatus: store.devices.byStatus,
74
      onboardingState,
75
      pastDeployments: store.deployments.byStatus.finished.deploymentIds,
76
      releases: Object.values(store.releases.byId),
77
      userCapabilities: getUserCapabilities(store),
78
      userId
79
    });
80
  }
81
  onboardingState.progress = onboardingState.progress || onboardingStepNames.DASHBOARD_ONBOARDING_START;
6!
82
  const demoDeviceAddress = `http://${getDemoDeviceAddress(Object.values(store.devices.byId), onboardingState.approach)}`;
6✔
83
  onboardingState.address = store.onboarding.demoArtifactPort ? `${demoDeviceAddress}:${store.onboarding.demoArtifactPort}` : demoDeviceAddress;
6!
84
  return Promise.resolve(dispatch(setOnboardingState(onboardingState)));
6✔
85
};
86

87
export const setShowOnboardingHelp =
88
  (show, update = true) =>
183✔
89
  dispatch => {
6✔
90
    let tasks = [dispatch({ type: SET_SHOW_ONBOARDING_HELP, show })];
6✔
91
    if (update) {
6✔
92
      tasks.push(dispatch(saveUserSettings({ onboarding: { showTips: show } })));
3✔
93
    }
94
    return Promise.all(tasks);
6✔
95
  };
96

97
const setOnboardingProgress = value => dispatch => dispatch({ type: SET_ONBOARDING_PROGRESS, value });
183✔
98

99
export const setOnboardingDeviceType =
100
  (value, update = true) =>
183✔
101
  dispatch => {
3✔
102
    let tasks = [dispatch({ type: SET_ONBOARDING_DEVICE_TYPE, value })];
3✔
103
    if (update) {
3!
104
      tasks.push(dispatch(saveUserSettings({ onboarding: { deviceType: value } })));
3✔
105
    }
106
    return Promise.all(tasks);
3✔
107
  };
108

109
export const setOnboardingApproach =
110
  (value, update = true) =>
183✔
111
  dispatch => {
7✔
112
    let tasks = [dispatch({ type: SET_ONBOARDING_APPROACH, value })];
7✔
113
    if (update) {
7!
114
      tasks.push(dispatch(saveUserSettings({ onboarding: { approach: value } })));
7✔
115
    }
116
    return Promise.all(tasks);
7✔
117
  };
118

119
export const setShowDismissOnboardingTipsDialog = show => dispatch => dispatch({ type: SET_SHOW_ONBOARDING_HELP_DIALOG, show });
183✔
120

121
export const setDemoArtifactPort = port => dispatch => dispatch({ type: SET_DEMO_ARTIFACT_PORT, value: port });
183✔
122

123
export const setOnboardingComplete = val => dispatch => {
183✔
124
  let tasks = [Promise.resolve(dispatch({ type: SET_ONBOARDING_COMPLETE, complete: val }))];
16✔
125
  if (val) {
16✔
126
    tasks.push(Promise.resolve(dispatch({ type: SET_SHOW_ONBOARDING_HELP, show: false })));
4✔
127
    tasks.push(Promise.resolve(dispatch(advanceOnboarding(onboardingStepNames.DEPLOYMENTS_PAST_COMPLETED))));
4✔
128
  }
129
  return Promise.all(tasks);
16✔
130
};
131

132
export const setOnboardingCanceled = () => dispatch =>
183✔
133
  Promise.all([
3✔
134
    Promise.resolve(dispatch(setShowOnboardingHelp(false, false))),
135
    Promise.resolve(dispatch(setShowDismissOnboardingTipsDialog(false))),
136
    Promise.resolve(dispatch({ type: SET_ONBOARDING_COMPLETE, complete: true }))
137
  ])
138
    // using DEPLOYMENTS_PAST_COMPLETED to ensure we get the intended onboarding state set after
139
    // _advancing_ the onboarding progress
140
    .then(() => dispatch(advanceOnboarding(onboardingStepNames.DEPLOYMENTS_PAST_COMPLETED_FAILURE)))
3✔
141
    // since we can't advance after ONBOARDING_CANCELED, track the step manually here
142
    .then(() => Tracking.event({ category: 'onboarding', action: onboardingSteps.ONBOARDING_CANCELED }));
3✔
143

144
const setOnboardingState = state => dispatch =>
183✔
145
  Promise.all([
6✔
146
    dispatch(setOnboardingComplete(state.complete)),
147
    dispatch({ type: SET_ONBOARDING_STATE, value: state }),
148
    dispatch(saveUserSettings({ onboarding: state }))
149
  ]);
150

151
export const advanceOnboarding = stepId => (dispatch, getState) => {
183✔
152
  const steps = Object.keys(onboardingSteps);
44✔
153
  const progress = steps.findIndex(step => step === getState().onboarding.progress);
622✔
154
  const stepIndex = steps.findIndex(step => step === stepId);
431✔
155
  // if there is no progress set yet, the onboarding state deduction hasn't happened
156
  // and the subsequent settings persistence would overwrite what we stored
157
  if (progress > stepIndex || getState().onboarding.progress === null) {
44✔
158
    return;
21✔
159
  }
160
  const madeProgress = steps[stepIndex + 1];
23✔
161
  const state = { ...getCurrentOnboardingState(getState()), progress: madeProgress };
23✔
162
  state.complete =
23✔
163
    stepIndex + 1 >= Object.keys(onboardingSteps).findIndex(step => step === onboardingStepNames.DEPLOYMENTS_PAST_COMPLETED) ? true : state.complete;
345✔
164
  Tracking.event({ category: 'onboarding', action: stepId });
23✔
165
  return Promise.all([dispatch(setOnboardingProgress(madeProgress)), dispatch(saveUserSettings({ onboarding: state }))]);
23✔
166
};
167

168
const determineProgress = (acceptedDevices, pendingDevices, releases, pastDeployments) => {
183✔
169
  const steps = Object.keys(onboardingSteps);
6✔
170
  let progress = -1;
6✔
171
  progress = pendingDevices.length > 1 ? steps.findIndex(step => step === onboardingStepNames.DEVICES_PENDING_ACCEPTING_ONBOARDING) : progress;
6!
172
  progress = acceptedDevices.length >= 1 ? steps.findIndex(step => step === onboardingStepNames.DEVICES_ACCEPTED_ONBOARDING) : progress;
42!
173
  progress =
6✔
174
    acceptedDevices.length > 1 && releases.length > 1 && pastDeployments.length > 1
18!
175
      ? steps.findIndex(step => step === onboardingStepNames.DEPLOYMENTS_PAST_COMPLETED)
×
176
      : progress;
177
  return steps[progress];
6✔
178
};
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