|
1 | 1 | import { tsx } from '@dojo/framework/core/vdom'; |
2 | 2 | import { assertion, renderer, wrap } from '@dojo/framework/testing/renderer'; |
3 | 3 | 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'; |
5 | 6 | import FileUploadInput from '../../../file-upload-input'; |
6 | 7 | import Icon from '../../../icon'; |
7 | 8 |
|
| 9 | +import bundle from '../../nls/FileUploader'; |
8 | 10 | import * as css from '../../../theme/default/file-uploader.m.css'; |
9 | 11 | import * as fileUploadInputCss from '../../../theme/default/file-upload-input.m.css'; |
10 | 12 | import * as fileUploadInputFixedCss from '../../../file-upload-input/styles/file-upload-input.m.css'; |
11 | 13 |
|
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; |
13 | 17 |
|
14 | 18 | describe('FileUploader', function() { |
15 | 19 | const WrappedRoot = wrap('div'); |
16 | 20 | const WrappedFileUploadInput = wrap(FileUploadInput); |
| 21 | + const WrappedButton = wrap('button'); |
17 | 22 |
|
18 | 23 | const baseRootProperties = { |
19 | 24 | key: 'root', |
@@ -51,6 +56,65 @@ describe('FileUploader', function() { |
51 | 56 | ); |
52 | 57 | }); |
53 | 58 |
|
| 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 | + |
54 | 118 | it('renders', function() { |
55 | 119 | const r = renderer(function() { |
56 | 120 | return <FileUploader onValue={noop} />; |
@@ -102,60 +166,191 @@ describe('FileUploader', function() { |
102 | 166 | r.expect(baseAssertion.setChildren(WrappedFileUploadInput, () => [{ label, content: '' }])); |
103 | 167 | }); |
104 | 168 |
|
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 | + }; |
128 | 203 | } |
129 | | - ]; |
| 204 | + } |
130 | 205 |
|
| 206 | + const files = getTestFiles(); |
131 | 207 | 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 | + ); |
133 | 211 | }); |
| 212 | + const multipleAssertion = baseAssertion.setProperty( |
| 213 | + WrappedFileUploadInput, |
| 214 | + 'multiple', |
| 215 | + true |
| 216 | + ); |
134 | 217 |
|
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 | + ]) |
157 | 311 | ); |
| 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); |
158 | 321 |
|
159 | 322 | 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'); |
160 | 355 | }); |
161 | 356 | }); |
0 commit comments