feat: prevent download large files
This commit is contained in:
committed by
leangseu-edx
parent
7e9eab24b0
commit
74423bf359
@@ -2,11 +2,13 @@ import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import {
|
||||
Card, Collapsible, Icon, DataTable,
|
||||
Card, Collapsible, Icon, DataTable, Button,
|
||||
} from '@edx/paragon';
|
||||
import { ArrowDropDown, ArrowDropUp } from '@edx/paragon/icons';
|
||||
import { ArrowDropDown, ArrowDropUp, WarningFilled } from '@edx/paragon/icons';
|
||||
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
|
||||
|
||||
import { downloadAllLimit, downloadSingleLimit } from 'data/constants/files';
|
||||
|
||||
import FileNameCell from './components/FileNameCell';
|
||||
import FileExtensionCell from './components/FileExtensionCell';
|
||||
import FilePopoverCell from './components/FilePopoverCell';
|
||||
@@ -19,7 +21,17 @@ import messages from './messages';
|
||||
*/
|
||||
export class SubmissionFiles extends React.Component {
|
||||
get title() {
|
||||
return `Submission Files (${this.props.files.length})`;
|
||||
return `${this.props.intl.formatMessage(messages.submissionFiles)} (${this.props.files.length})`;
|
||||
}
|
||||
|
||||
get canDownload() {
|
||||
let totalFileSize = 0;
|
||||
const exceedFileSize = this.props.files.some(file => {
|
||||
totalFileSize += file.size;
|
||||
return file.size > downloadSingleLimit;
|
||||
});
|
||||
|
||||
return !exceedFileSize && totalFileSize < downloadAllLimit;
|
||||
}
|
||||
|
||||
render() {
|
||||
@@ -70,7 +82,15 @@ export class SubmissionFiles extends React.Component {
|
||||
</Collapsible.Body>
|
||||
</Collapsible.Advanced>
|
||||
<Card.Footer className="text-right">
|
||||
<FileDownload files={files} />
|
||||
{
|
||||
this.canDownload ? <FileDownload files={files} /> : (
|
||||
<div>
|
||||
<Icon className="d-inline-block align-middle" src={WarningFilled} />
|
||||
<span className="exceed-download-text"> {intl.formatMessage(messages.exceedFileSize)} </span>
|
||||
<Button disabled>{intl.formatMessage(messages.downloadFiles)}</Button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
</Card.Footer>
|
||||
</>
|
||||
) : (
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
import React from 'react';
|
||||
import { shallow } from 'enzyme';
|
||||
|
||||
import { downloadAllLimit, downloadSingleLimit } from 'data/constants/files';
|
||||
|
||||
import { formatMessage } from 'testUtils';
|
||||
import { SubmissionFiles } from './SubmissionFiles';
|
||||
import messages from './messages';
|
||||
|
||||
jest.mock('./components/FileNameCell', () => jest.fn().mockName('FileNameCell'));
|
||||
jest.mock('./components/FileExtensionCell', () => jest.fn().mockName('FileExtensionCell'));
|
||||
@@ -16,25 +19,34 @@ describe('SubmissionFiles', () => {
|
||||
name: 'some file name.jpg',
|
||||
description: 'description for the file',
|
||||
downloadURL: '/valid-url-wink-wink',
|
||||
size: 0,
|
||||
},
|
||||
{
|
||||
name: 'file number 2.jpg',
|
||||
description: 'description for this file',
|
||||
downloadURL: '/url-2',
|
||||
size: 0,
|
||||
},
|
||||
],
|
||||
};
|
||||
let el;
|
||||
beforeAll(() => {
|
||||
el = shallow(<SubmissionFiles intl={{ formatMessage }} />);
|
||||
beforeEach(() => {
|
||||
el = shallow(<SubmissionFiles intl={{ formatMessage }} {...props} />);
|
||||
});
|
||||
|
||||
describe('snapshot', () => {
|
||||
test('files does not exist', () => {
|
||||
test('files existed for props', () => {
|
||||
expect(el).toMatchSnapshot();
|
||||
});
|
||||
test('files exited for props', () => {
|
||||
el.setProps({ ...props });
|
||||
|
||||
test('files does not exist', () => {
|
||||
el.setProps({ files: [] });
|
||||
|
||||
expect(el).toMatchSnapshot();
|
||||
});
|
||||
test('files size exceed', () => {
|
||||
const files = props.files.map(file => ({ ...file, size: downloadSingleLimit + 1 }));
|
||||
el.setProps({ files });
|
||||
expect(el).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
@@ -43,12 +55,47 @@ describe('SubmissionFiles', () => {
|
||||
test('title', () => {
|
||||
const titleEl = el.find('.submission-files-title>h3');
|
||||
expect(titleEl.text()).toEqual(
|
||||
`Submission Files (${props.files.length})`,
|
||||
`${formatMessage(messages.submissionFiles)} (${props.files.length})`,
|
||||
);
|
||||
expect(el.instance().title).toEqual(
|
||||
`Submission Files (${props.files.length})`,
|
||||
`${formatMessage(messages.submissionFiles)} (${props.files.length})`,
|
||||
);
|
||||
});
|
||||
|
||||
describe('canDownload', () => {
|
||||
test('normal file size', () => {
|
||||
expect(el.instance().canDownload).toEqual(true);
|
||||
});
|
||||
|
||||
test('one of the file exceed the limit', () => {
|
||||
const oneFileExceed = [{ ...props.files[0], size: downloadSingleLimit + 1 }, props.files[1]];
|
||||
|
||||
oneFileExceed.forEach(file => expect(file.size < downloadAllLimit).toEqual(true));
|
||||
|
||||
el.setProps({ files: oneFileExceed });
|
||||
expect(el.instance().canDownload).toEqual(false);
|
||||
|
||||
const warningEl = el.find('span.exceed-download-text');
|
||||
expect(warningEl.text().trim()).toEqual(formatMessage(messages.exceedFileSize));
|
||||
});
|
||||
|
||||
test('total file size exceed the limit', () => {
|
||||
const length = 20;
|
||||
const totalFilesExceed = new Array(length).fill({
|
||||
name: 'some file name.jpg',
|
||||
description: 'description for the file',
|
||||
downloadURL: '/valid-url-wink-wink',
|
||||
size: (downloadAllLimit + 1) / length,
|
||||
});
|
||||
totalFilesExceed.forEach(file => {
|
||||
expect(file.size < downloadAllLimit).toEqual(true);
|
||||
expect(file.size < downloadSingleLimit).toEqual(true);
|
||||
});
|
||||
|
||||
el.setProps({ files: totalFilesExceed });
|
||||
expect(el.instance().canDownload).toEqual(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -14,7 +14,7 @@ exports[`SubmissionFiles component snapshot files does not exist 1`] = `
|
||||
</Card>
|
||||
`;
|
||||
|
||||
exports[`SubmissionFiles component snapshot files exited for props 1`] = `
|
||||
exports[`SubmissionFiles component snapshot files existed for props 1`] = `
|
||||
<Card
|
||||
className="submission-files"
|
||||
>
|
||||
@@ -75,11 +75,13 @@ exports[`SubmissionFiles component snapshot files exited for props 1`] = `
|
||||
"description": "description for the file",
|
||||
"downloadURL": "/valid-url-wink-wink",
|
||||
"name": "some file name.jpg",
|
||||
"size": 0,
|
||||
},
|
||||
Object {
|
||||
"description": "description for this file",
|
||||
"downloadURL": "/url-2",
|
||||
"name": "file number 2.jpg",
|
||||
"size": 0,
|
||||
},
|
||||
]
|
||||
}
|
||||
@@ -100,11 +102,13 @@ exports[`SubmissionFiles component snapshot files exited for props 1`] = `
|
||||
"description": "description for the file",
|
||||
"downloadURL": "/valid-url-wink-wink",
|
||||
"name": "some file name.jpg",
|
||||
"size": 0,
|
||||
},
|
||||
Object {
|
||||
"description": "description for this file",
|
||||
"downloadURL": "/url-2",
|
||||
"name": "file number 2.jpg",
|
||||
"size": 0,
|
||||
},
|
||||
]
|
||||
}
|
||||
@@ -112,3 +116,105 @@ exports[`SubmissionFiles component snapshot files exited for props 1`] = `
|
||||
</Card.Footer>
|
||||
</Card>
|
||||
`;
|
||||
|
||||
exports[`SubmissionFiles component snapshot files size exceed 1`] = `
|
||||
<Card
|
||||
className="submission-files"
|
||||
>
|
||||
<Collapsible.Advanced
|
||||
defaultOpen={true}
|
||||
>
|
||||
<Collapsible.Trigger
|
||||
className="submission-files-title"
|
||||
>
|
||||
<h3>
|
||||
Submission Files (2)
|
||||
</h3>
|
||||
<Collapsible.Visible
|
||||
whenClosed={true}
|
||||
>
|
||||
<Icon
|
||||
src={[MockFunction icons.ArrowDropDown]}
|
||||
/>
|
||||
</Collapsible.Visible>
|
||||
<Collapsible.Visible
|
||||
whenOpen={true}
|
||||
>
|
||||
<Icon
|
||||
src={[MockFunction icons.ArrowDropUp]}
|
||||
/>
|
||||
</Collapsible.Visible>
|
||||
</Collapsible.Trigger>
|
||||
<Collapsible.Body
|
||||
className="submission-files-body"
|
||||
>
|
||||
<div
|
||||
className="submission-files-table"
|
||||
>
|
||||
<DataTable
|
||||
columns={
|
||||
Array [
|
||||
Object {
|
||||
"Cell": [MockFunction FileNameCell],
|
||||
"Header": "Name",
|
||||
"accessor": "name",
|
||||
},
|
||||
Object {
|
||||
"Cell": [MockFunction FileExtensionCell],
|
||||
"Header": "File Extension",
|
||||
"accessor": "name",
|
||||
"id": "extension",
|
||||
},
|
||||
Object {
|
||||
"Cell": [MockFunction FilePopoverCell],
|
||||
"Header": "File Metadata",
|
||||
"accessor": "",
|
||||
},
|
||||
]
|
||||
}
|
||||
data={
|
||||
Array [
|
||||
Object {
|
||||
"description": "description for the file",
|
||||
"downloadURL": "/valid-url-wink-wink",
|
||||
"name": "some file name.jpg",
|
||||
"size": 1610612737,
|
||||
},
|
||||
Object {
|
||||
"description": "description for this file",
|
||||
"downloadURL": "/url-2",
|
||||
"name": "file number 2.jpg",
|
||||
"size": 1610612737,
|
||||
},
|
||||
]
|
||||
}
|
||||
itemCount={2}
|
||||
>
|
||||
<DataTable.Table />
|
||||
</DataTable>
|
||||
</div>
|
||||
</Collapsible.Body>
|
||||
</Collapsible.Advanced>
|
||||
<Card.Footer
|
||||
className="text-right"
|
||||
>
|
||||
<div>
|
||||
<Icon
|
||||
className="d-inline-block align-middle"
|
||||
/>
|
||||
<span
|
||||
className="exceed-download-text"
|
||||
>
|
||||
|
||||
Exceeded the allow download size
|
||||
|
||||
</span>
|
||||
<Button
|
||||
disabled={true}
|
||||
>
|
||||
Download files
|
||||
</Button>
|
||||
</div>
|
||||
</Card.Footer>
|
||||
</Card>
|
||||
`;
|
||||
|
||||
@@ -36,6 +36,16 @@ const messages = defineMessages({
|
||||
defaultMessage: 'Retry download',
|
||||
description: 'Download files failed state label',
|
||||
},
|
||||
submissionFiles: {
|
||||
id: 'ora-grading.ResponseDisplay.SubmissionFiles.submissionFile',
|
||||
defaultMessage: 'Submission Files',
|
||||
description: 'Total submission files',
|
||||
},
|
||||
exceedFileSize: {
|
||||
id: 'ora-grading.ResponseDisplay.SubmissionFiles.fileSizeExceed',
|
||||
defaultMessage: 'Exceeded the allow download size',
|
||||
description: 'Exceed the allow download size error message',
|
||||
},
|
||||
});
|
||||
|
||||
export default messages;
|
||||
|
||||
@@ -14,4 +14,7 @@ export const FileTypes = StrictDict({
|
||||
svg: 'svg',
|
||||
});
|
||||
|
||||
export const downloadSingleLimit = 1610612736; // 1.5GB
|
||||
export const downloadAllLimit = 10737418240; // 10GB
|
||||
|
||||
export default FileTypes;
|
||||
|
||||
Reference in New Issue
Block a user