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

mendersoftware / gui / 897326496

pending completion
897326496

Pull #3752

gitlab-ci

mzedel
chore(e2e): made use of shared timeout & login checking values to remove code duplication

Signed-off-by: Manuel Zedel <manuel.zedel@northern.tech>
Pull Request #3752: chore(e2e-tests): slightly simplified log in test + separated log out test

4395 of 6392 branches covered (68.76%)

8060 of 9780 relevant lines covered (82.41%)

126.17 hits per line

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

77.19
/src/js/components/settings/user-management/selfusermanagement.js
1
// Copyright 2017 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, { useState } from 'react';
15
import { connect } from 'react-redux';
16

17
import { Button, Switch, TextField } from '@mui/material';
18
import { makeStyles } from 'tss-react/mui';
19

20
import { setSnackbar } from '../../../actions/appActions';
21
import { editUser, saveGlobalSettings, saveUserSettings } from '../../../actions/userActions';
22
import { getToken } from '../../../auth';
23
import * as UserConstants from '../../../constants/userConstants';
24
import { toggle } from '../../../helpers';
25
import { getCurrentUser, getIsEnterprise, getUserSettings } from '../../../selectors';
26
import ExpandableAttribute from '../../common/expandable-attribute';
27
import Form from '../../common/forms/form';
28
import PasswordInput from '../../common/forms/passwordinput';
29
import TextInput from '../../common/forms/textinput';
30
import InfoText from '../../common/infotext';
31
import AccessTokenManagement from '../accesstokenmanagement';
32
import { CopyTextToClipboard } from '../organization/organization';
33
import TwoFactorAuthSetup from './twofactorauthsetup';
34
import { getUserSSOState } from './userdefinition';
35

36
const useStyles = makeStyles()(() => ({
5✔
37
  formField: { width: 400, maxWidth: '100%' },
38
  changeButton: { margin: '30px 0 0 15px' },
39
  infoText: { margin: 0, width: '75%' },
40
  jwt: { maxWidth: '70%' },
41
  oauthIcon: { fontSize: '36px', marginRight: 10 },
42
  widthLimit: { maxWidth: 750 }
43
}));
44

45
export const SelfUserManagement = ({
5✔
46
  canHave2FA,
47
  currentUser,
48
  editUser,
49
  hasTracking,
50
  hasTrackingConsent,
51
  isEnterprise,
52
  mode,
53
  saveUserSettings,
54
  setSnackbar
55
}) => {
56
  const [editEmail, setEditEmail] = useState(false);
7✔
57
  const [editPass, setEditPass] = useState(false);
7✔
58
  const [emailFormId, setEmailFormId] = useState(new Date());
7✔
59
  const { classes } = useStyles();
7✔
60

61
  const editSubmit = userData => {
7✔
62
    if (userData.password != userData.password_confirmation) {
×
63
      setSnackbar(`The passwords don't match`);
×
64
    } else {
65
      editUser(UserConstants.OWN_USER_ID, userData).then(() => {
×
66
        setEditEmail(false);
×
67
        setEditPass(false);
×
68
      });
69
    }
70
  };
71

72
  const handleEmail = () => {
7✔
73
    let uniqueId = emailFormId;
2✔
74
    if (editEmail) {
2✔
75
      // changing unique id will reset form values
76
      uniqueId = new Date();
1✔
77
    }
78
    setEditEmail(toggle);
2✔
79
    setEmailFormId(uniqueId);
2✔
80
  };
81

82
  const toggleMode = () => {
7✔
83
    const newMode = mode === 'dark' ? 'light' : 'dark';
×
84
    saveUserSettings({ mode: newMode });
×
85
  };
86

87
  const handlePass = () => setEditPass(toggle);
7✔
88
  const email = currentUser.email;
7✔
89
  const { isOAuth2, provider } = getUserSSOState(currentUser);
7✔
90
  return (
7✔
91
    <div className={`margin-top-small ${classes.widthLimit}`}>
92
      <h2 className="margin-top-small">My profile</h2>
93
      {!editEmail && currentUser.email ? (
20✔
94
        <div className="flexbox space-between">
95
          <TextField className={classes.formField} label="Email" key={email} InputLabelProps={{ shrink: !!email }} disabled defaultValue={email} />
96
          {!isOAuth2 && (
11✔
97
            <Button className={`inline-block ${classes.changeButton}`} color="primary" id="change_email" onClick={handleEmail}>
98
              Change email
99
            </Button>
100
          )}
101
        </div>
102
      ) : (
103
        <Form
104
          onSubmit={editSubmit}
105
          handleCancel={handleEmail}
106
          submitLabel="Save"
107
          showButtons={editEmail}
108
          buttonColor="secondary"
109
          submitButtonId="submit_email"
110
          uniqueId={emailFormId}
111
        >
112
          <TextInput
113
            disabled={false}
114
            focus
115
            hint="Email"
116
            id="email"
117
            InputLabelProps={{ shrink: !!email }}
118
            label="Email"
119
            validations="isLength:1,isEmail"
120
            value={email}
121
          />
122
          <PasswordInput id="current_password" label="Current password *" validations={`isLength:8,isNot:${email}`} required={true} />
123
        </Form>
124
      )}
125
      {!isOAuth2 &&
13✔
126
        (!editPass ? (
6✔
127
          <form className="flexbox space-between">
128
            <TextField className={classes.formField} label="Password" key="password-placeholder" disabled defaultValue="********" type="password" />
129
            <Button className={classes.changeButton} color="primary" id="change_password" onClick={handlePass}>
130
              Change password
131
            </Button>
132
          </form>
133
        ) : (
134
          <>
135
            <h3 className="margin-top margin-bottom-none">Change password</h3>
136
            <Form
137
              onSubmit={editSubmit}
138
              handleCancel={handlePass}
139
              submitLabel="Save"
140
              submitButtonId="submit_pass"
141
              buttonColor="secondary"
142
              showButtons={editPass}
143
            >
144
              <PasswordInput id="current_password" label="Current password *" validations={`isLength:8,isNot:${email}`} required />
145
              <PasswordInput className="edit-pass" id="password" label="Password *" validations={`isLength:8,isNot:${email}`} create generate required />
146
              <PasswordInput id="password_confirmation" label="Confirm password *" validations={`isLength:8,isNot:${email}`} required />
147
            </Form>
148
          </>
149
        ))}
150
      <div className="clickable flexbox space-between margin-top" onClick={toggleMode}>
151
        <p className="help-content">Enable dark theme</p>
152
        <Switch checked={mode === 'dark'} />
153
      </div>
154
      {!isOAuth2 ? (
7✔
155
        canHave2FA && <TwoFactorAuthSetup />
11✔
156
      ) : (
157
        <div className="flexbox margin-top">
158
          <div className={classes.oauthIcon}>{provider.icon}</div>
159
          <div className="info">
160
            You are logging in using your <strong>{provider.name}</strong> account.
161
            <br />
162
            Please connect to {provider.name} to update your login settings.
163
          </div>
164
        </div>
165
      )}
166
      <div className="flexbox space-between margin-top-large">
167
        <div className={classes.jwt}>
168
          <div className="help-content">Session token</div>
169
          <ExpandableAttribute
170
            component="div"
171
            disableGutters
172
            dividerDisabled
173
            secondary={getToken()}
174
            textClasses={{ secondary: 'inventory-text tenant-token-text' }}
175
          />
176
        </div>
177
        <div className="flexbox center-aligned">
178
          <CopyTextToClipboard token={getToken()} />
179
        </div>
180
      </div>
181
      {!isOAuth2 && <AccessTokenManagement />}
13✔
182
      {isEnterprise && hasTracking && (
12!
183
        <div className="margin-top">
184
          <div className="clickable flexbox space-between" onClick={() => saveUserSettings({ trackingConsentGiven: !hasTrackingConsent })}>
×
185
            <p className="help-content">Help us improve Mender</p>
186
            <Switch checked={!!hasTrackingConsent} />
187
          </div>
188
          <InfoText className={classes.infoText}>Enable usage data and errors to be sent to help us improve our service.</InfoText>
189
        </div>
190
      )}
191
    </div>
192
  );
193
};
194

195
const actionCreators = { editUser, saveGlobalSettings, saveUserSettings, setSnackbar };
5✔
196

197
const mapStateToProps = state => {
5✔
198
  const isEnterprise = getIsEnterprise(state);
3✔
199
  return {
3✔
200
    canHave2FA: isEnterprise || state.app.features.isHosted,
5✔
201
    currentUser: getCurrentUser(state),
202
    hasTracking: !!state.app.trackerCode,
203
    hasTrackingConsent: getUserSettings(state).trackingConsentGiven,
204
    mode: getUserSettings(state).mode,
205
    isEnterprise
206
  };
207
};
208

209
export default connect(mapStateToProps, actionCreators)(SelfUserManagement);
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