• 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

40.38
/src/js/components/devices/troubleshoot/filetransfer.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, { useEffect, useState } from 'react';
15

16
import { FileCopy as CopyPasteIcon } from '@mui/icons-material';
17
import { Button, IconButton, Tab, Tabs, TextField, Tooltip } from '@mui/material';
18
import { makeStyles } from 'tss-react/mui';
19

20
import FileUpload from '../../common/forms/fileupload';
21
import InfoText from '../../common/infotext';
22

23
const tabs = ['upload', 'download'];
13✔
24

25
const maxWidth = 400;
13✔
26

27
const useStyles = makeStyles()(theme => ({
13✔
28
  column: { maxWidth },
29
  inputWrapper: { display: 'grid', gridTemplateColumns: `${maxWidth}px max-content` },
30
  tab: {
31
    alignItems: 'flex-start'
32
  },
33
  fileDestination: { marginTop: theme.spacing(2) }
34
}));
35

36
export const FileTransfer = ({ deviceId, downloadPath, file, onDownload, onUpload, setFile, setDownloadPath, setSnackbar, setUploadPath, uploadPath }) => {
13✔
37
  const { classes } = useStyles();
1✔
38
  const [currentTab, setCurrentTab] = useState(tabs[0]);
1✔
39
  const [isValidDestination, setIsValidDestination] = useState(true);
1✔
40

41
  useEffect(() => {
1✔
42
    let destination = currentTab === 'download' ? downloadPath : uploadPath;
1!
43
    const isValid = destination.length ? /^(?:\/|[a-z]+:\/\/)/.test(destination) : true;
1!
44
    setIsValidDestination(isValid);
1✔
45
  }, [currentTab, downloadPath, uploadPath]);
46

47
  const onPasteDownloadClick = async () => {
1✔
48
    const path = await navigator.clipboard.readText();
×
49
    setDownloadPath(path);
×
50
  };
51

52
  const onPasteUploadClick = async () => {
1✔
53
    const path = await navigator.clipboard.readText();
×
54
    setUploadPath(path);
×
55
  };
56

57
  const onFileSelect = selectedFile => {
1✔
58
    let path;
59
    if (selectedFile) {
×
60
      path = `${uploadPath}/${selectedFile.name}`;
×
61
    } else {
62
      path = file && uploadPath.includes(file.name) ? uploadPath.substring(0, uploadPath.lastIndexOf('/')) : uploadPath;
×
63
    }
64
    setUploadPath(path);
×
65
    setFile(selectedFile);
×
66
  };
67

68
  return (
1✔
69
    <div className="tab-container with-sub-panels" style={{ minHeight: '95%' }}>
70
      <Tabs orientation="vertical" className="leftFixed" onChange={(e, item) => setCurrentTab(item)} value={currentTab}>
×
71
        {tabs.map(item => (
72
          <Tab className={`${classes.tab} capitalized`} key={item} label={item} value={item} />
2✔
73
        ))}
74
      </Tabs>
75
      <div className="rightFluid padding-right">
76
        {currentTab === 'upload' ? (
1!
77
          <>
78
            <InfoText className={classes.column}>Upload a file to the device</InfoText>
79
            <FileUpload
80
              enableContentReading={false}
81
              fileNameSelection={file?.name}
82
              onFileChange={() => undefined}
×
83
              onFileSelect={onFileSelect}
84
              placeholder={
85
                <>
86
                  Drag here or <a>browse</a> to upload a file
87
                </>
88
              }
89
              setSnackbar={setSnackbar}
90
              style={{ maxWidth }}
91
            />
92
            <div className={classes.inputWrapper}>
93
              <TextField
94
                autoFocus={true}
95
                error={!isValidDestination}
96
                fullWidth
97
                helperText={!isValidDestination && <div className="warning">Destination has to be an absolute path</div>}
1!
98
                inputProps={{ style: { marginTop: 16 } }}
99
                InputLabelProps={{ shrink: true }}
100
                label="Destination directory on the device where the file will be transferred"
101
                onChange={e => setUploadPath(e.target.value)}
×
102
                placeholder="Example: /opt/installed-by-single-file"
103
                value={uploadPath}
104
              />
105
              <Tooltip title="Paste" placement="top">
106
                <IconButton style={{ alignSelf: 'flex-end' }} onClick={onPasteUploadClick} size="large">
107
                  <CopyPasteIcon />
108
                </IconButton>
109
              </Tooltip>
110
            </div>
111
            <div className={`flexbox margin-top ${classes.column}`} style={{ justifyContent: 'flex-end' }}>
112
              <Button
113
                variant="contained"
114
                color="primary"
115
                disabled={!(file && uploadPath && isValidDestination)}
1!
116
                onClick={() => onUpload(deviceId, uploadPath, file)}
×
117
              >
118
                Upload
119
              </Button>
120
            </div>
121
          </>
122
        ) : (
123
          <>
124
            <InfoText>Download a file from the device</InfoText>
125
            <div className={classes.inputWrapper}>
126
              <TextField
127
                autoFocus={true}
128
                className={classes.column}
129
                error={!isValidDestination}
130
                fullWidth
131
                helperText={!isValidDestination && <div className="warning">Destination has to be an absolute path</div>}
×
132
                inputProps={{ className: classes.fileDestination }}
133
                InputLabelProps={{ shrink: true }}
134
                label="Path to the file on the device"
135
                onChange={e => setDownloadPath(e.target.value)}
×
136
                placeholder="Example: /home/mender/"
137
                value={downloadPath}
138
              />
139
              <Tooltip title="Paste" placement="top">
140
                <IconButton style={{ alignSelf: 'flex-end' }} onClick={onPasteDownloadClick} size="large">
141
                  <CopyPasteIcon />
142
                </IconButton>
143
              </Tooltip>
144
            </div>
145
            <div className={`flexbox margin-top ${classes.column}`} style={{ justifyContent: 'flex-end' }}>
146
              <Button
147
                variant="contained"
148
                color="primary"
149
                disabled={!(downloadPath && isValidDestination)}
×
150
                onClick={() => onDownload(downloadPath)}
×
151
                style={{ alignSelf: 'flex-end' }}
152
              >
153
                Download
154
              </Button>
155
            </div>
156
          </>
157
        )}
158
      </div>
159
    </div>
160
  );
161
};
162

163
export default FileTransfer;
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