Skip to content

Commit aa1ff8e

Browse files
msssktomdye
authored andcommitted
FileUploader: update tests
1 parent 21d4132 commit aa1ff8e

File tree

3 files changed

+253
-52
lines changed

3 files changed

+253
-52
lines changed

src/file-upload-input/tests/unit/FileUploadInput.spec.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import * as css from '../../../theme/default/file-upload-input.m.css';
1313
import * as fixedCss from '../../styles/file-upload-input.m.css';
1414
import * as labelCss from '../../../theme/default/label.m.css';
1515

16-
const { after, afterEach, it, describe } = intern.getInterface('bdd');
16+
const { after, afterEach, describe, it } = intern.getInterface('bdd');
1717
const { assert } = intern.getPlugin('chai');
1818
const { messages } = bundle;
1919

src/file-uploader/index.tsx

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,15 +49,21 @@ export interface FileUploaderProperties extends FileUploadInputProperties {
4949
export type FileWithValidation = File & ValidationInfo;
5050

5151
const factorNames = ['', 'B', 'KB', 'MB', 'GB', 'TB', 'PB'];
52-
function formatBytes(byteCount: number) {
52+
export function formatBytes(byteCount: number) {
5353
if (isNaN(byteCount)) {
5454
return '';
5555
}
5656

5757
let formattedValue = '';
5858
for (let i = 1; i < factorNames.length; i++) {
5959
if (byteCount < Math.pow(1024, i) || i === factorNames.length - 1) {
60-
formattedValue = `${(byteCount / Math.pow(1024, i - 1)).toFixed(2)} ${factorNames[i]}`;
60+
formattedValue = `${(byteCount / Math.pow(1024, i - 1)).toFixed(i > 1 ? 2 : 0)} ${
61+
factorNames[i]
62+
}`;
63+
// values below the next factor up but greater than 1023.99 will round up to 1024.00 - push them down
64+
if (formattedValue.startsWith('1024.00') && i < factorNames.length - 1) {
65+
formattedValue = `1023.99 ${factorNames[i]}`;
66+
}
6167
break;
6268
}
6369
}

src/file-uploader/tests/unit/FileUploader.spec.tsx

Lines changed: 244 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,24 @@
11
import { tsx } from '@dojo/framework/core/vdom';
22
import { assertion, renderer, wrap } from '@dojo/framework/testing/renderer';
33
import { noop } from '@dojo/widgets/common/tests/support/test-helpers';
4-
import FileUploader from '../../index';
4+
import * as sinon from 'sinon';
5+
import FileUploader, { formatBytes } from '../../index';
56
import FileUploadInput from '../../../file-upload-input';
67
import Icon from '../../../icon';
78

9+
import bundle from '../../nls/FileUploader';
810
import * as css from '../../../theme/default/file-uploader.m.css';
911
import * as fileUploadInputCss from '../../../theme/default/file-upload-input.m.css';
1012
import * as fileUploadInputFixedCss from '../../../file-upload-input/styles/file-upload-input.m.css';
1113

12-
const { after, afterEach, it, describe } = intern.getInterface('bdd');
14+
const { describe, it } = intern.getInterface('bdd');
15+
const { assert } = intern.getPlugin('chai');
16+
const { messages } = bundle;
1317

1418
describe('FileUploader', function() {
1519
const WrappedRoot = wrap('div');
1620
const WrappedFileUploadInput = wrap(FileUploadInput);
21+
const WrappedButton = wrap('button');
1722

1823
const baseRootProperties = {
1924
key: 'root',
@@ -51,6 +56,65 @@ describe('FileUploader', function() {
5156
);
5257
});
5358

59+
function getTestFiles() {
60+
return [
61+
{
62+
name: 'file1.jpg',
63+
size: 55383,
64+
formattedSize: '54.08 KB',
65+
type: 'image/jpeg'
66+
},
67+
{
68+
name: 'file2.png',
69+
size: 180240,
70+
formattedSize: '176.02 KB',
71+
type: 'image/png',
72+
valid: false,
73+
message: 'File is too big'
74+
},
75+
{
76+
name: 'file3.png',
77+
size: 4001220,
78+
formattedSize: '3.82 MB',
79+
type: 'image/png',
80+
valid: true,
81+
message: 'File is great'
82+
}
83+
];
84+
}
85+
86+
function getRenderedFiles(files: ReturnType<typeof getTestFiles>) {
87+
return (
88+
<div key="fileList">
89+
{files.map(function(file, index) {
90+
return (
91+
<div
92+
classes={[css.fileItem, file.valid === false ? css.invalid : false]}
93+
key={file.name}
94+
>
95+
<div classes={[css.fileInfo]}>
96+
<div classes={[css.fileItemName]}>{file.name}</div>
97+
<div classes={[css.fileItemSize]}>{formatBytes(file.size)}</div>
98+
{index === 0 ? (
99+
<WrappedButton onclick={noop} classes={[css.closeButton]}>
100+
<Icon altText="Remove" type="closeIcon" />
101+
</WrappedButton>
102+
) : (
103+
<button onclick={noop} classes={[css.closeButton]}>
104+
<Icon altText="Remove" type="closeIcon" />
105+
</button>
106+
)}
107+
</div>
108+
{file.message && (
109+
<div classes={[css.validationMessage]}>{file.message}</div>
110+
)}
111+
</div>
112+
);
113+
})}
114+
</div>
115+
);
116+
}
117+
54118
it('renders', function() {
55119
const r = renderer(function() {
56120
return <FileUploader onValue={noop} />;
@@ -102,60 +166,191 @@ describe('FileUploader', function() {
102166
r.expect(baseAssertion.setChildren(WrappedFileUploadInput, () => [{ label, content: '' }]));
103167
});
104168

105-
it('renders files', function() {
106-
const files = [
107-
{
108-
name: 'file1.jpg',
109-
size: 55383,
110-
formattedSize: '54.08 KB',
111-
type: 'image/jpeg'
112-
},
113-
{
114-
name: 'file2.png',
115-
size: 180240,
116-
formattedSize: '176.02 KB',
117-
type: 'image/png',
118-
valid: false,
119-
message: 'File is too big'
120-
},
121-
{
122-
name: 'file3.png',
123-
size: 4001220,
124-
formattedSize: '3.82 MB',
125-
type: 'image/png',
126-
valid: true,
127-
message: 'File is great'
169+
it('renders files from property', function() {
170+
const files = getTestFiles();
171+
const r = renderer(function() {
172+
return <FileUploader files={files as any} onValue={noop} />;
173+
});
174+
const content = getRenderedFiles(files);
175+
176+
r.expect(baseAssertion.setChildren(WrappedFileUploadInput, () => [{ content }]));
177+
});
178+
179+
it('renders files from FileUploadInput', function() {
180+
const files = getTestFiles();
181+
const r = renderer(function() {
182+
return <FileUploader onValue={noop} />;
183+
});
184+
const content = getRenderedFiles([files[0]]);
185+
186+
r.expect(baseAssertion);
187+
r.property(WrappedFileUploadInput, 'onValue', files as any);
188+
r.expect(baseAssertion.setChildren(WrappedFileUploadInput, () => [{ content }]));
189+
});
190+
191+
it('renders and validates multiple files from FileUploadInput', function() {
192+
// This is implemented to produce the same output that getRenderedFiles(getTestFiles()) creates
193+
function customValidator(file: ReturnType<typeof getTestFiles>[number]) {
194+
if (file.name === 'file2.png') {
195+
return {
196+
valid: false,
197+
message: 'File is too big'
198+
};
199+
} else if (file.name === 'file3.png') {
200+
return {
201+
message: 'File is great'
202+
};
128203
}
129-
];
204+
}
130205

206+
const files = getTestFiles();
131207
const r = renderer(function() {
132-
return <FileUploader files={files as any} onValue={noop} />;
208+
return (
209+
<FileUploader customValidator={customValidator as any} multiple onValue={noop} />
210+
);
133211
});
212+
const multipleAssertion = baseAssertion.setProperty(
213+
WrappedFileUploadInput,
214+
'multiple',
215+
true
216+
);
134217

135-
const content = (
136-
<div key="fileList">
137-
{files.map(function(file) {
138-
return (
139-
<div
140-
classes={[css.fileItem, file.valid === false ? css.invalid : false]}
141-
key={file.name}
142-
>
143-
<div classes={[css.fileInfo]}>
144-
<div classes={[css.fileItemName]}>{file.name}</div>
145-
<div classes={[css.fileItemSize]}>{file.formattedSize}</div>
146-
<button onclick={noop} classes={[css.closeButton]}>
147-
<Icon altText="Remove" type="closeIcon" />
148-
</button>
149-
</div>
150-
{file.message && (
151-
<div classes={[css.validationMessage]}>{file.message}</div>
152-
)}
153-
</div>
154-
);
155-
})}
156-
</div>
218+
r.expect(multipleAssertion);
219+
r.property(WrappedFileUploadInput, 'onValue', files as any);
220+
221+
const content = getRenderedFiles(files);
222+
r.expect(multipleAssertion.setChildren(WrappedFileUploadInput, () => [{ content }]));
223+
});
224+
225+
it('renders added files cumulatively when multiple=true', function() {
226+
const r = renderer(function() {
227+
return <FileUploader multiple onValue={noop} />;
228+
});
229+
const multipleAssertion = baseAssertion.setProperty(
230+
WrappedFileUploadInput,
231+
'multiple',
232+
true
233+
);
234+
235+
r.expect(multipleAssertion);
236+
const files = [{ name: 'file1', size: 100 }];
237+
r.property(WrappedFileUploadInput, 'onValue', files as any);
238+
r.expect(
239+
multipleAssertion.setChildren(WrappedFileUploadInput, () => [
240+
{ content: getRenderedFiles(files) }
241+
])
242+
);
243+
244+
const moreFiles = [{ name: 'file2', size: 200 }];
245+
r.property(WrappedFileUploadInput, 'onValue', moreFiles as any);
246+
r.expect(
247+
multipleAssertion.setChildren(WrappedFileUploadInput, () => [
248+
{ content: getRenderedFiles([...files, ...moreFiles]) }
249+
])
250+
);
251+
});
252+
253+
it('renders only a single file when multiple is not true', function() {
254+
const r = renderer(function() {
255+
return <FileUploader onValue={noop} />;
256+
});
257+
258+
r.expect(baseAssertion);
259+
const files = [{ name: 'file1', size: 100 }, { name: 'file2', size: 200 }];
260+
r.property(WrappedFileUploadInput, 'onValue', files as any);
261+
r.expect(
262+
baseAssertion.setChildren(WrappedFileUploadInput, () => [
263+
{ content: getRenderedFiles([files[0]]) }
264+
])
265+
);
266+
const moreFiles = [{ name: 'file3', size: 300 }, { name: 'file4', size: 400 }];
267+
r.property(WrappedFileUploadInput, 'onValue', moreFiles as any);
268+
r.expect(
269+
baseAssertion.setChildren(WrappedFileUploadInput, () => [
270+
{ content: getRenderedFiles([moreFiles[0]]) }
271+
])
272+
);
273+
});
274+
275+
it('validates files on maxSize', function() {
276+
const maxSize = 500;
277+
const onValue = sinon.spy();
278+
const r = renderer(function() {
279+
return <FileUploader maxSize={maxSize} multiple onValue={onValue} />;
280+
});
281+
const multipleAssertion = baseAssertion.setProperty(
282+
WrappedFileUploadInput,
283+
'multiple',
284+
true
285+
);
286+
287+
r.expect(multipleAssertion);
288+
289+
const files = [
290+
{ name: 'file1', size: 100 },
291+
{ name: 'file2', size: 0 },
292+
{ name: 'file3', size: 499 },
293+
{ name: 'file4', size: 500 },
294+
{ name: 'file5', size: 501 },
295+
{ name: 'file6', size: Math.pow(10, 10) }
296+
];
297+
const expectedFiles = (files as any).map(function(file: any) {
298+
const expectedFile = { ...file, message: '', valid: true };
299+
if (file.size > maxSize) {
300+
expectedFile.valid = false;
301+
expectedFile.message = messages.invalidFileSize;
302+
}
303+
304+
return expectedFile;
305+
});
306+
r.property(WrappedFileUploadInput, 'onValue', files as any);
307+
r.expect(
308+
multipleAssertion.setChildren(WrappedFileUploadInput, () => [
309+
{ content: getRenderedFiles(expectedFiles) }
310+
])
157311
);
312+
assert.deepEqual(onValue.firstCall.args[0], expectedFiles);
313+
});
314+
315+
it('removes files', function() {
316+
const files = getTestFiles();
317+
const r = renderer(function() {
318+
return <FileUploader files={files as any} onValue={noop} />;
319+
});
320+
const content = getRenderedFiles(files);
158321

159322
r.expect(baseAssertion.setChildren(WrappedFileUploadInput, () => [{ content }]));
323+
// TODO: the click event is not firing
324+
// r.property(WrappedButton, 'onclick');
325+
// r.expect(baseAssertion.setChildren(WrappedFileUploadInput, () => [{ content: getRenderedFiles(files.slice(1)) }]));
326+
});
327+
328+
it('formats bytes up to PB', function() {
329+
assert.strictEqual(formatBytes(0), '0 B');
330+
assert.strictEqual(formatBytes(1), '1 B');
331+
assert.strictEqual(formatBytes(1023), '1023 B');
332+
assert.strictEqual(formatBytes(1024), '1.00 KB');
333+
assert.strictEqual(formatBytes(1025), '1.00 KB');
334+
assert.strictEqual(formatBytes(1034), '1.01 KB');
335+
assert.strictEqual(formatBytes(Math.pow(1024, 2) - 1), '1023.99 KB');
336+
assert.strictEqual(formatBytes(Math.pow(1024, 2)), '1.00 MB');
337+
assert.strictEqual(formatBytes(Math.pow(1024, 2) + 1), '1.00 MB');
338+
assert.strictEqual(formatBytes(Math.pow(1024, 2) + 10485), '1.01 MB');
339+
assert.strictEqual(formatBytes(Math.pow(1024, 3) - 1), '1023.99 MB');
340+
assert.strictEqual(formatBytes(Math.pow(1024, 3)), '1.00 GB');
341+
assert.strictEqual(formatBytes(Math.pow(1024, 3) + 1), '1.00 GB');
342+
assert.strictEqual(formatBytes(Math.pow(1024, 3) + 10737418), '1.01 GB');
343+
assert.strictEqual(formatBytes(Math.pow(1024, 4) - 1), '1023.99 GB');
344+
assert.strictEqual(formatBytes(Math.pow(1024, 4)), '1.00 TB');
345+
assert.strictEqual(formatBytes(Math.pow(1024, 4) + 1), '1.00 TB');
346+
assert.strictEqual(formatBytes(Math.pow(1024, 4) + 10995116277), '1.01 TB');
347+
assert.strictEqual(formatBytes(Math.pow(1024, 5) - 1), '1023.99 TB');
348+
assert.strictEqual(formatBytes(Math.pow(1024, 5)), '1.00 PB');
349+
assert.strictEqual(formatBytes(Math.pow(1024, 5) + 1), '1.00 PB');
350+
assert.strictEqual(formatBytes(Math.pow(1024, 5) + 11258999068426), '1.01 PB');
351+
// with no tier above PB there should be no rounding down just below 1024 PB
352+
assert.strictEqual(formatBytes(Math.pow(1024, 6) - 1), '1024.00 PB');
353+
// still PB
354+
assert.strictEqual(formatBytes(Math.pow(1024, 7)), '1048576.00 PB');
160355
});
161356
});

0 commit comments

Comments
 (0)