• 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

75.42
/src/js/components/header/header.js
1
// Copyright 2015 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, { useEffect, useRef, useState } from 'react';
15
import { useDispatch, useSelector } from 'react-redux';
16
import { Link } from 'react-router-dom';
17

18
import {
19
  AccountCircle as AccountCircleIcon,
20
  ArrowDropDown as ArrowDropDownIcon,
21
  ArrowDropUp as ArrowDropUpIcon,
22
  ExitToApp as ExitIcon
23
} from '@mui/icons-material';
24
import { Button, IconButton, ListItemSecondaryAction, ListItemText, Menu, MenuItem, Toolbar } from '@mui/material';
25
import { makeStyles } from 'tss-react/mui';
26

27
import moment from 'moment';
28
import Cookies from 'universal-cookie';
29

30
import enterpriseLogo from '../../../assets/img/headerlogo-enterprise.png';
31
import logo from '../../../assets/img/headerlogo.png';
32
import whiteEnterpriseLogo from '../../../assets/img/whiteheaderlogo-enterprise.png';
33
import whiteLogo from '../../../assets/img/whiteheaderlogo.png';
34
import { setFirstLoginAfterSignup, setSearchState } from '../../actions/appActions';
35
import { getAllDeviceCounts } from '../../actions/deviceActions';
36
import { initializeSelf, logoutUser, setHideAnnouncement, toggleHelptips } from '../../actions/userActions';
37
import { getToken } from '../../auth';
38
import { TIMEOUTS } from '../../constants/appConstants';
39
import { decodeSessionToken, isDarkMode } from '../../helpers';
40
import {
41
  getAcceptedDevices,
42
  getCurrentUser,
43
  getDeviceCountsByStatus,
44
  getDeviceLimit,
45
  getFeatures,
46
  getIsEnterprise,
47
  getOrganization,
48
  getShowHelptips,
49
  getUserCapabilities,
50
  getUserSettings
51
} from '../../selectors';
52
import Tracking from '../../tracking';
53
import { useDebounce } from '../../utils/debouncehook';
54
import Search from '../common/search';
55
import Announcement from './announcement';
56
import DemoNotification from './demonotification';
57
import DeploymentNotifications from './deploymentnotifications';
58
import DeviceNotifications from './devicenotifications';
59
import OfferHeader from './offerheader';
60
import TrialNotification from './trialnotification';
61

62
// Change this when a new feature/offer is introduced
63
const currentOffer = {
3✔
64
  name: 'add-ons',
65
  expires: '2021-12-30',
66
  trial: true,
67
  os: true,
68
  professional: true,
69
  enterprise: true
70
};
71

72
const cookies = new Cookies();
3✔
73

74
const useStyles = makeStyles()(theme => ({
54✔
75
  header: {
76
    minHeight: 'unset',
77
    paddingLeft: theme.spacing(4),
78
    paddingRight: theme.spacing(5),
79
    width: '100%',
80
    borderBottom: `1px solid ${theme.palette.grey[100]}`,
81
    display: 'grid'
82
  },
83
  banner: { gridTemplateRows: `1fr ${theme.mixins.toolbar.minHeight}px` },
84
  buttonColor: { color: theme.palette.grey[600] },
85
  dropDown: { height: '100%', marginLeft: theme.spacing(0.5), textTransform: 'none' },
86
  exitIcon: { color: theme.palette.grey[600], fill: theme.palette.grey[600] },
87
  demoTrialAnnouncement: {
88
    fontSize: 14,
89
    height: 'auto'
90
  },
91
  demoAnnouncementIcon: {
92
    height: 16,
93
    color: theme.palette.primary.main,
94
    '&.MuiButton-textPrimary': {
95
      color: theme.palette.primary.main,
96
      height: 'inherit'
97
    }
98
  },
99
  redAnnouncementIcon: {
100
    color: theme.palette.error.dark
101
  }
102
}));
103

104
export const Header = ({ mode }) => {
3✔
105
  const { classes } = useStyles();
462✔
106
  const [anchorEl, setAnchorEl] = useState(null);
462✔
107
  const [loggingOut, setLoggingOut] = useState(false);
462✔
108
  const [gettingUser, setGettingUser] = useState(false);
462✔
109
  const [hasOfferCookie, setHasOfferCookie] = useState(false);
462✔
110
  const sessionId = useDebounce(getToken(), TIMEOUTS.debounceDefault);
462✔
111

112
  const organization = useSelector(getOrganization);
462✔
113
  const { canManageUsers: allowUserManagement } = useSelector(getUserCapabilities);
462✔
114
  const { total: acceptedDevices = 0 } = useSelector(getAcceptedDevices);
462!
115
  const announcement = useSelector(state => state.app.hostedAnnouncement);
1,539✔
116
  const deviceLimit = useSelector(getDeviceLimit);
462✔
117
  const firstLoginAfterSignup = useSelector(state => state.app.firstLoginAfterSignup);
1,539✔
118
  const { trackingConsentGiven: hasTrackingEnabled } = useSelector(getUserSettings);
462✔
119
  const inProgress = useSelector(state => state.deployments.byStatus.inprogress.total);
1,539✔
120
  const isEnterprise = useSelector(getIsEnterprise);
462✔
121
  const { isDemoMode: demo, hasMultitenancy, isHosted } = useSelector(getFeatures);
462✔
122
  const { isSearching, searchTerm, refreshTrigger } = useSelector(state => state.app.searchState);
1,539✔
123
  const multitenancy = hasMultitenancy || isEnterprise || isHosted;
462✔
124
  const showHelptips = useSelector(getShowHelptips);
462✔
125
  const { pending: pendingDevices } = useSelector(getDeviceCountsByStatus);
462✔
126
  const user = useSelector(getCurrentUser);
462✔
127
  const dispatch = useDispatch();
462✔
128
  const deviceTimer = useRef();
462✔
129

130
  useEffect(() => {
462✔
131
    if ((!sessionId || !user?.id || !user.email.length) && !gettingUser && !loggingOut) {
9✔
132
      updateUsername();
2✔
133
      return;
2✔
134
    }
135
    Tracking.setTrackingEnabled(hasTrackingEnabled);
7✔
136
    if (hasTrackingEnabled && user.id && organization.id) {
7!
137
      Tracking.setOrganizationUser(organization, user);
×
138
      if (firstLoginAfterSignup) {
×
139
        Tracking.pageview('/signup/complete');
×
140
        dispatch(setFirstLoginAfterSignup(false));
×
141
      }
142
    }
143
  }, [sessionId, user.id, user.email, gettingUser, loggingOut]);
144

145
  useEffect(() => {
462✔
146
    const showOfferCookie = cookies.get('offer') === currentOffer.name;
6✔
147
    setHasOfferCookie(showOfferCookie);
6✔
148
    clearInterval(deviceTimer.current);
6✔
149
    deviceTimer.current = setInterval(() => dispatch(getAllDeviceCounts()), TIMEOUTS.refreshDefault);
180✔
150
    return () => {
6✔
151
      clearInterval(deviceTimer.current);
6✔
152
    };
153
  }, []);
154

155
  const updateUsername = () => {
462✔
156
    const userId = decodeSessionToken(getToken());
2✔
157
    if (gettingUser || !userId) {
2✔
158
      return;
1✔
159
    }
160
    setGettingUser(true);
1✔
161
    // get current user
162
    return dispatch(initializeSelf()).finally(() => setGettingUser(false));
1✔
163
  };
164

165
  const onLogoutClick = () => {
462✔
166
    setGettingUser(false);
1✔
167
    setLoggingOut(true);
1✔
168
    setAnchorEl(null);
1✔
169
    dispatch(logoutUser());
1✔
170
  };
171

172
  const onSearch = searchTerm => dispatch(setSearchState({ refreshTrigger: !refreshTrigger, searchTerm, page: 1 }));
462✔
173

174
  const setHideOffer = () => {
462✔
175
    cookies.set('offer', currentOffer.name, { path: '/', maxAge: 2629746 });
×
176
    setHasOfferCookie(true);
×
177
  };
178

179
  const showOffer =
180
    isHosted && moment().isBefore(currentOffer.expires) && (organization.trial ? currentOffer.trial : currentOffer[organization.plan]) && !hasOfferCookie;
462!
181

182
  const headerLogo = isDarkMode(mode) ? (isEnterprise ? whiteEnterpriseLogo : whiteLogo) : isEnterprise ? enterpriseLogo : logo;
462!
183

184
  return (
462✔
185
    <Toolbar id="fixedHeader" className={showOffer ? `${classes.header} ${classes.banner}` : classes.header}>
462!
186
      {!!announcement && (
506✔
187
        <Announcement
188
          announcement={announcement}
189
          errorIconClassName={classes.redAnnouncementIcon}
190
          iconClassName={classes.demoAnnouncementIcon}
191
          sectionClassName={classes.demoTrialAnnouncement}
192
          onHide={() => dispatch(setHideAnnouncement(true))}
×
193
        />
194
      )}
195
      {showOffer && <OfferHeader onHide={setHideOffer} />}
462!
196
      <div className="flexbox space-between">
197
        <div className="flexbox center-aligned">
198
          <Link to="/">
199
            <img id="logo" src={headerLogo} />
200
          </Link>
201
          {demo && <DemoNotification iconClassName={classes.demoAnnouncementIcon} sectionClassName={classes.demoTrialAnnouncement} />}
462!
202
          {organization.trial && (
462!
203
            <TrialNotification
204
              expiration={organization.trial_expiration}
205
              iconClassName={classes.demoAnnouncementIcon}
206
              sectionClassName={classes.demoTrialAnnouncement}
207
            />
208
          )}
209
        </div>
210
        <Search isSearching={isSearching} searchTerm={searchTerm} onSearch={onSearch} />
211
        <div className="flexbox center-aligned">
212
          <DeviceNotifications pending={pendingDevices} total={acceptedDevices} limit={deviceLimit} />
213
          <DeploymentNotifications inprogress={inProgress} />
214
          <Button
215
            className={`header-dropdown ${classes.dropDown}`}
216
            onClick={e => setAnchorEl(e.currentTarget)}
1✔
217
            startIcon={<AccountCircleIcon className={classes.buttonColor} />}
218
            endIcon={anchorEl ? <ArrowDropUpIcon /> : <ArrowDropDownIcon />}
462✔
219
          >
220
            {user.email}
221
          </Button>
222
          <Menu
223
            anchorEl={anchorEl}
224
            onClose={() => setAnchorEl(null)}
×
225
            open={Boolean(anchorEl)}
226
            anchorOrigin={{
227
              vertical: 'center',
228
              horizontal: 'center'
229
            }}
230
            transformOrigin={{
231
              vertical: 'bottom',
232
              horizontal: 'center'
233
            }}
234
          >
235
            <MenuItem component={Link} to="/settings">
236
              Settings
237
            </MenuItem>
238
            <MenuItem component={Link} to="/settings/my-profile">
239
              My profile
240
            </MenuItem>
241
            {multitenancy && (
915✔
242
              <MenuItem component={Link} to="/settings/organization-and-billing">
243
                My organization
244
              </MenuItem>
245
            )}
246
            {allowUserManagement && (
727✔
247
              <MenuItem component={Link} to="/settings/user-management">
248
                User management
249
              </MenuItem>
250
            )}
251
            <MenuItem onClick={() => dispatch(toggleHelptips())}>{showHelptips ? 'Hide help tooltips' : 'Show help tooltips'}</MenuItem>
×
252
            <MenuItem component={Link} to="/help/get-started">
253
              Help & support
254
            </MenuItem>
255
            <MenuItem onClick={onLogoutClick}>
256
              <ListItemText primary="Log out" />
257
              <ListItemSecondaryAction>
258
                <IconButton>
259
                  <ExitIcon className={classes.exitIcon} />
260
                </IconButton>
261
              </ListItemSecondaryAction>
262
            </MenuItem>
263
          </Menu>
264
        </div>
265
      </div>
266
    </Toolbar>
267
  );
268
};
269

270
export default Header;
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