• 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

66.3
/src/js/components/dashboard/software-distribution.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 React, { useEffect, useMemo } from 'react';
15
import { useDispatch, useSelector } from 'react-redux';
16

17
import { BarChart as BarChartIcon } from '@mui/icons-material';
18

19
import {
20
  defaultReportType,
21
  defaultReports,
22
  deriveReportsData,
23
  getDeviceAttributes,
24
  getDevicesInBounds,
25
  getGroupDevices,
26
  getReportingLimits,
27
  getReportsData
28
} from '../../actions/deviceActions';
29
import { saveUserSettings } from '../../actions/userActions';
30
import { rootfsImageVersion, softwareTitleMap } from '../../constants/releaseConstants';
31
import { isEmpty } from '../../helpers';
32
import {
33
  getAcceptedDevices,
34
  getAttributesList,
35
  getDeviceReports,
36
  getDeviceReportsForUser,
37
  getDevicesById,
38
  getFeatures,
39
  getGroupNames,
40
  getGroupsByIdWithoutUngrouped,
41
  getIsEnterprise
42
} from '../../selectors';
43
import EnterpriseNotification from '../common/enterpriseNotification';
44
import { extractSoftwareInformation } from '../devices/device-details/installedsoftware';
45
import ChartAdditionWidget from './widgets/chart-addition';
46
import DistributionReport from './widgets/distribution';
47
import MapWrapper from './widgets/mapwidget';
48

49
const reportTypes = {
5✔
50
  distribution: DistributionReport
51
};
52

53
const getLayerKey = ({ title, key }, parent) => `${parent.length ? `${parent}.` : parent}${key.length <= title.length ? key : title}`;
23!
54

55
const generateLayer = (softwareLayer, parentKey = '', nestingLevel = 0) => {
5✔
56
  const { children, key, title } = softwareLayer;
23✔
57
  const suffix = title === key ? '.version' : '';
23!
58
  const layerKey = getLayerKey(softwareLayer, parentKey);
23✔
59
  const layerTitle = `${layerKey}${suffix}`;
23✔
60
  let headerItems = [{ title, nestingLevel, value: layerKey }];
23✔
61
  if (softwareTitleMap[layerTitle]) {
23!
62
    headerItems = [
23✔
63
      { subheader: title, nestingLevel, value: `${layerTitle}-subheader` },
64
      { title: softwareTitleMap[layerTitle].title, nestingLevel: nestingLevel + 1, value: layerTitle }
65
    ];
66
  } else if (!isEmpty(children)) {
×
67
    headerItems = [{ subheader: title, nestingLevel, value: `${layerTitle}-subheader` }];
×
68
  }
69
  return Object.values(softwareLayer.children).reduce((accu, childLayer) => {
23✔
70
    const layerData = generateLayer(childLayer, getLayerKey(softwareLayer, parentKey), nestingLevel + 1);
×
71
    accu.push(...layerData);
×
72
    return accu;
×
73
  }, headerItems);
74
};
75

76
const listSoftware = attributes => {
5✔
77
  const enhancedAttributes = attributes.reduce((accu, attribute) => ({ ...accu, [attribute]: attribute }), {});
23✔
78
  const softwareTree = extractSoftwareInformation(enhancedAttributes, false);
23✔
79
  const { rootFs, remainder } = Object.values(softwareTree).reduce(
23✔
80
    (accu, layer) => {
81
      if (layer.key.startsWith('rootfs-image')) {
23!
82
        return { ...accu, rootFs: layer };
23✔
83
      }
84
      accu.remainder.push(layer);
×
85
      return accu;
×
86
    },
87
    { rootFs: undefined, remainder: [] }
88
  );
89

90
  return (rootFs ? [rootFs, ...remainder] : remainder).flatMap(softwareLayer => generateLayer(softwareLayer));
23!
91
};
92

93
export const SoftwareDistribution = () => {
5✔
94
  const dispatch = useDispatch();
1,263✔
95

96
  const reports = useSelector(getDeviceReportsForUser);
1,263✔
97
  const groups = useSelector(getGroupsByIdWithoutUngrouped);
1,263✔
98
  const { hasReporting } = useSelector(getFeatures);
1,263✔
99
  const attributes = useSelector(getAttributesList);
1,263✔
100
  const { total } = useSelector(getAcceptedDevices);
1,263✔
101
  const hasDevices = !!total;
1,263✔
102
  const isEnterprise = useSelector(getIsEnterprise);
1,263✔
103
  const reportsData = useSelector(getDeviceReports);
1,263✔
104
  const groupNames = useSelector(getGroupNames);
1,263✔
105
  const devicesById = useSelector(getDevicesById);
1,263✔
106

107
  useEffect(() => {
1,263✔
108
    dispatch(getDeviceAttributes());
10✔
109
    if (hasReporting) {
10!
110
      dispatch(getReportingLimits());
×
111
    }
112
  }, []);
113

114
  useEffect(() => {
1,263✔
115
    if (hasReporting) {
12!
116
      dispatch(getReportsData());
×
117
      return;
×
118
    }
119
    dispatch(deriveReportsData());
12✔
120
  }, [JSON.stringify(reports)]);
121

122
  const addCurrentSelection = selection => {
1,263✔
123
    const newReports = [...reports, { ...defaultReports[0], ...selection }];
×
124
    dispatch(saveUserSettings({ reports: newReports }));
×
125
  };
126

127
  const onSaveChangedReport = (change, index) => {
1,263✔
128
    let newReports = [...reports];
×
129
    newReports.splice(index, 1, change);
×
130
    dispatch(saveUserSettings({ reports: newReports }));
×
131
  };
132

133
  const removeReport = removedReport => dispatch(saveUserSettings({ reports: reports.filter(report => report !== removedReport) }));
1,263✔
134

135
  const software = useMemo(() => listSoftware(hasReporting ? attributes : [rootfsImageVersion]), [JSON.stringify(attributes), hasReporting]);
1,263!
136

137
  if (!isEnterprise) {
1,263✔
138
    return (
1,250✔
139
      <div className="flexbox centered">
140
        <EnterpriseNotification isEnterprise={isEnterprise} benefit="actionable insights into the devices you are updating with Mender" />
141
      </div>
142
    );
143
  }
144
  const dispatchedGetGroupDevices = (...args) => dispatch(getGroupDevices(...args));
13✔
145
  return hasDevices ? (
13!
146
    <div className="dashboard margin-bottom-large">
147
      <MapWrapper
148
        groups={groups}
149
        groupNames={groupNames}
150
        devicesById={devicesById}
151
        getGroupDevices={dispatchedGetGroupDevices}
152
        getDevicesInBounds={(...args) => dispatch(getDevicesInBounds(...args))}
×
153
      />
154
      {reports.map((report, index) => {
155
        const Component = reportTypes[report.type || defaultReportType];
13!
156
        return (
13✔
157
          <Component
158
            key={`report-${report.group}-${index}`}
159
            data={reportsData[index]}
160
            getGroupDevices={dispatchedGetGroupDevices}
161
            groups={groups}
162
            onClick={() => removeReport(report)}
×
163
            onSave={change => onSaveChangedReport(change, index)}
×
164
            selection={report}
165
            software={software}
166
          />
167
        );
168
      })}
169
      <ChartAdditionWidget groups={groups} onAdditionClick={addCurrentSelection} software={software} />
170
    </div>
171
  ) : (
172
    <div className="dashboard-placeholder margin-top-large">
173
      <BarChartIcon style={{ transform: 'scale(5)' }} />
174
      <p className="margin-top-large">Software distribution charts will appear here once you connected a device. </p>
175
    </div>
176
  );
177
};
178

179
export default SoftwareDistribution;
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