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

mendersoftware / gui / 1350829378

27 Jun 2024 01:46PM UTC coverage: 83.494% (-16.5%) from 99.965%
1350829378

Pull #4465

gitlab-ci

mzedel
chore: test fixes

Signed-off-by: Manuel Zedel <manuel.zedel@northern.tech>
Pull Request #4465: MEN-7169 - feat: added multi sorting capabilities to devices view

4506 of 6430 branches covered (70.08%)

81 of 100 new or added lines in 14 files covered. (81.0%)

1661 existing lines in 163 files now uncovered.

8574 of 10269 relevant lines covered (83.49%)

160.6 hits per line

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

91.89
/src/js/components/devices/dialogs/monitordetailsdialog.js
1
// Copyright 2021 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 CopyToClipboard from 'react-copy-to-clipboard';
16

17
import {
18
  ArrowDropDown as ArrowDropDownIcon,
19
  ArrowDropUp as ArrowDropUpIcon,
20
  FileCopy as CopyPasteIcon,
21
  ReportProblemOutlined as WarningIcon
22
} from '@mui/icons-material';
23
import {
24
  Accordion,
25
  AccordionDetails,
26
  AccordionSummary,
27
  Button,
28
  Collapse,
29
  Dialog,
30
  DialogActions,
31
  DialogContent,
32
  DialogTitle,
33
  IconButton,
34
  styled
35
} from '@mui/material';
36

37
import { TIMEOUTS } from '../../../constants/appConstants';
38
import { toggle } from '../../../helpers';
39

40
const CopyButton = ({ text, onCopy }) => (
10✔
UNCOV
41
  <CopyToClipboard text={text} onCopy={onCopy}>
×
42
    <IconButton size="small">
43
      <CopyPasteIcon fontSize="small" />
44
    </IconButton>
45
  </CopyToClipboard>
46
);
47

48
const LogLine = ({ beExplicit, line, prefix }) => {
10✔
49
  const [copied, setCopied] = useState(false);
9✔
50
  const [hovering, setHovering] = useState(false);
9✔
51

52
  const onCopied = () => {
9✔
UNCOV
53
    setCopied(true);
×
UNCOV
54
    setTimeout(() => setCopied(false), TIMEOUTS.threeSeconds);
×
55
  };
56

57
  const toggleHovering = () => setHovering(toggle);
9✔
58

59
  const { line_number, data } = line;
9✔
60

61
  return (
9✔
62
    <React.Fragment key={line_number}>
63
      <div className="log-line margin-right" onMouseLeave={toggleHovering} onMouseOver={setHovering}>
64
        {prefix ? prefix : <div />}
9✔
65
        {line_number !== undefined ? <code className={`align-right ${beExplicit ? 'red' : ''}`}>{line_number}</code> : <div />}
17✔
66
        <code className={beExplicit ? 'red' : ''}>{data}</code>
9✔
67
        {hovering && <CopyButton text={data} onCopy={onCopied} />}
9!
68
      </div>
69
      <Collapse in={copied}>
70
        <div className="margin-left-small green fadeIn">Copied to clipboard.</div>
71
      </Collapse>
72
    </React.Fragment>
73
  );
74
};
75

76
const CustomAccordion = styled(Accordion)({
10✔
77
  root: {
78
    '&:before': {
79
      display: 'none'
80
    },
81
    '&$expanded': {
82
      backgroundColor: 'transparent',
83
      margin: 0
84
    }
85
  },
86
  expanded: {}
87
});
88

89
const LogSection = ({ section = 'previous', lines }) => {
10!
90
  const [expanded, setExpanded] = useState(false);
5✔
91
  const onToggle = () => setExpanded(toggle);
5✔
92
  return (
5✔
93
    !!lines.length && (
8✔
94
      <CustomAccordion square expanded={expanded} onChange={onToggle}>
95
        <AccordionSummary style={{ paddingLeft: 0 }}>
96
          {expanded ? <ArrowDropUpIcon className="margin-right-small" /> : <ArrowDropDownIcon className="margin-right-small" />}
3✔
97
          <div>
98
            show {section} {lines.length} lines
99
          </div>
100
        </AccordionSummary>
101
        <AccordionDetails style={{ paddingLeft: 0, paddingRight: 0 }}>
102
          {lines.map(item => (
103
            <LogLine key={item.line_number} line={item} />
6✔
104
          ))}
105
        </AccordionDetails>
106
      </CustomAccordion>
107
    )
108
  );
109
};
110

111
const LogContent = ({ lines_before = [], lines_after = [], line_matching = '' }) => (
10!
112
  <>
2✔
113
    <LogSection section="previous" lines={lines_before} />
114
    <LogLine beExplicit line={line_matching} prefix={<WarningIcon fontSize="small" />} />
115
    <LogSection section="next" lines={lines_after} />
116
  </>
117
);
118

119
const DescriptionContent = ({ description }) => <LogLine line={{ data: description }} />;
10✔
120

121
const detailTypes = {
10✔
122
  log: {
123
    component: LogContent,
124
    title: 'Log excerpt'
125
  },
126
  description: {
127
    component: DescriptionContent,
128
    title: 'Details'
129
  }
130
};
131

132
const exportLog = (name, lines) => {
10✔
133
  const max = lines.reduce((accu, item) => Math.max(accu, item.line_number), 0);
3✔
134
  const length = `${max}`.length;
1✔
135
  const logData = lines
1✔
136
    .reduce((accu, item) => {
137
      const paddedLineNumber = `${item.line_number}`.padStart(length, '0');
3✔
138
      accu.push(`${paddedLineNumber}   ${item.data}`);
3✔
139
      return accu;
3✔
140
    }, [])
141
    .join('\n');
142
  const uriContent = `data:application/octet-stream,${encodeURIComponent(logData)}`;
1✔
143
  window.open(uriContent, `Mender-Monitor-${name.replace(/ /g, '_')}.log`);
1✔
144
};
145

146
export const MonitorDetailsDialog = ({ alert, onClose }) => {
10✔
147
  const { name, subject = { details: {} } } = alert ?? {};
4✔
148
  const {
149
    details: { lines_before = [], lines_after = [], line_matching = '' }
8✔
150
  } = subject;
4✔
151

152
  const lines = [...lines_before, line_matching, ...lines_after].filter(i => i);
8✔
153

154
  const { component: Component, title } = lines.length ? detailTypes.log : detailTypes.description;
4✔
155
  return (
4✔
156
    <Dialog open={!!alert} maxWidth="md">
157
      <DialogTitle>{`${title} for ${name}`}</DialogTitle>
158
      <DialogContent style={{ minWidth: 600 }}>
159
        <Component {...subject.details} />
160
      </DialogContent>
161
      <DialogActions>
162
        <Button onClick={onClose}>Close</Button>
163
        {!!lines.length && (
6✔
164
          <Button variant="contained" color="primary" onClick={() => exportLog(name, lines)}>
1✔
165
            Export log
166
          </Button>
167
        )}
168
      </DialogActions>
169
    </Dialog>
170
  );
171
};
172

173
export default MonitorDetailsDialog;
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