1'use strict'; 2 3import { FileSystem as FS, Asset } from 'expo'; 4 5export const name = 'FileSystem'; 6 7export function test(t) { 8 t.describe('FileSystem', () => { 9 const throws = async run => { 10 let error = null; 11 try { 12 await run(); 13 } catch (e) { 14 // Uncomment to log error message. 15 // const func = run.toString().match(/[A-Za-z]+\(/)[0].slice(0, -1); 16 // console.log(`${func}: ${e.message}`); 17 error = e; 18 } 19 t.expect(error).toBeTruthy(); 20 }; 21 22 /*t.it( 23 'delete(idempotent) -> !exists -> download(md5, uri) -> exists ' + '-> delete -> !exists', 24 async () => { 25 const localUri = FS.documentDirectory + 'download1.png'; 26 27 const assertExists = async expectedToExist => { 28 let { exists } = await FS.getInfoAsync(localUri); 29 if (expectedToExist) { 30 t.expect(exists).toBeTruthy(); 31 } else { 32 t.expect(exists).not.toBeTruthy(); 33 } 34 }; 35 36 await FS.deleteAsync(localUri, { idempotent: true }); 37 await assertExists(false); 38 39 const { 40 md5, 41 uri, 42 headers, 43 } = await FS.downloadAsync( 44 'https://s3-us-west-1.amazonaws.com/test-suite-data/avatar2.png', 45 localUri, 46 { md5: true } 47 ); 48 t.expect(md5).toBe('1e02045c10b8f1145edc7c8375998f87'); 49 await assertExists(true); 50 t.expect(headers['Content-Type']).toBe('image/png'); 51 52 await FS.deleteAsync(localUri); 53 await assertExists(false); 54 }, 55 9000 56 );*/ 57 58 t.it('Can read/write Base64', async () => { 59 const asset = await Asset.fromModule(require('../assets/icons/app.png')); 60 await asset.downloadAsync(); 61 62 for (let startingPosition = 0; startingPosition < 3; startingPosition++) { 63 const options = { 64 encoding: FS.EncodingTypes.Base64, 65 position: startingPosition, 66 length: startingPosition + 1, 67 }; 68 69 const b64 = await FS.readAsStringAsync(asset.localUri, options); 70 t.expect(b64).toBeDefined(); 71 t.expect(typeof b64).toBe('string'); 72 t.expect(b64.length % 4).toBe(0); 73 74 const localUri = FS.documentDirectory + 'b64.png'; 75 76 await FS.writeAsStringAsync(localUri, b64, { encoding: FS.EncodingTypes.Base64 }); 77 78 t.expect(await FS.readAsStringAsync(localUri, { encoding: FS.EncodingTypes.Base64 })).toBe( 79 b64 80 ); 81 } 82 }); 83 84 t.it('delete(idempotent) -> delete[error]', async () => { 85 const localUri = FS.documentDirectory + 'willDelete.png'; 86 87 await FS.deleteAsync(localUri, { idempotent: true }); 88 89 let error; 90 try { 91 await FS.deleteAsync(localUri); 92 } catch (e) { 93 error = e; 94 } 95 t.expect(error.message).toMatch(/not.*found/); 96 }); 97 98 /*t.it( 99 'download(md5, uri) -> read -> delete -> !exists -> read[error]', 100 async () => { 101 const localUri = FS.documentDirectory + 'download1.txt'; 102 103 const { 104 md5, 105 uri, 106 } = await FS.downloadAsync( 107 'https://s3-us-west-1.amazonaws.com/test-suite-data/text-file.txt', 108 localUri, 109 { md5: true } 110 ); 111 t.expect(md5).toBe('86d73d2f11e507365f7ea8e7ec3cc4cb'); 112 113 const string = await FS.readAsStringAsync(localUri); 114 t.expect(string).toBe('hello, world\nthis is a test file\n'); 115 116 await FS.deleteAsync(localUri, { idempotent: true }); 117 118 let error; 119 try { 120 await FS.readAsStringAsync(localUri); 121 } catch (e) { 122 error = e; 123 } 124 t.expect(error).toBeTruthy(); 125 }, 126 9000 127 );*/ 128 129 t.it('delete(idempotent) -> !exists -> write -> read -> write -> read', async () => { 130 const localUri = FS.documentDirectory + 'write1.txt'; 131 132 await FS.deleteAsync(localUri, { idempotent: true }); 133 134 const { exists } = await FS.getInfoAsync(localUri); 135 t.expect(exists).not.toBeTruthy(); 136 137 const writeAndVerify = async expected => { 138 await FS.writeAsStringAsync(localUri, expected); 139 const string = await FS.readAsStringAsync(localUri); 140 t.expect(string).toBe(expected); 141 }; 142 143 await writeAndVerify('hello, world'); 144 await writeAndVerify('hello, world!!!!!!'); 145 }); 146 147 t.it('delete(new) -> 2 * [write -> move -> !exists(orig) -> read(new)]', async () => { 148 const from = FS.documentDirectory + 'from.txt'; 149 const to = FS.documentDirectory + 'to.txt'; 150 const contents = ['contents 1', 'contents 2']; 151 152 await FS.deleteAsync(to, { idempotent: true }); 153 154 // Move twice to make sure we can overwrite 155 for (let i = 0; i < 2; ++i) { 156 await FS.writeAsStringAsync(from, contents[i]); 157 158 await FS.moveAsync({ from, to }); 159 160 const { exists } = await FS.getInfoAsync(from); 161 t.expect(exists).not.toBeTruthy(); 162 163 t.expect(await FS.readAsStringAsync(to)).toBe(contents[i]); 164 } 165 }); 166 167 t.it('delete(new) -> 2 * [write -> copy -> exists(orig) -> read(new)]', async () => { 168 const from = FS.documentDirectory + 'from.txt'; 169 const to = FS.documentDirectory + 'to.txt'; 170 const contents = ['contents 1', 'contents 2']; 171 172 await FS.deleteAsync(to, { idempotent: true }); 173 174 // Copy twice to make sure we can overwrite 175 for (let i = 0; i < 2; ++i) { 176 await FS.writeAsStringAsync(from, contents[i]); 177 178 await FS.copyAsync({ from, to }); 179 180 const { exists } = await FS.getInfoAsync(from); 181 t.expect(exists).toBeTruthy(); 182 183 t.expect(await FS.readAsStringAsync(to)).toBe(contents[i]); 184 } 185 }); 186 187 t.it( 188 'delete(dir) -> write(dir/file)[error] -> mkdir(dir) ->' + 189 'mkdir(dir)[error] -> write(dir/file) -> read', 190 async () => { 191 let error; 192 const path = FS.documentDirectory + 'dir/file'; 193 const dir = FS.documentDirectory + 'dir'; 194 const contents = 'hello, world'; 195 196 await FS.deleteAsync(dir, { idempotent: true }); 197 198 error = null; 199 try { 200 await FS.writeAsStringAsync(path, contents); 201 } catch (e) { 202 error = e; 203 } 204 t.expect(error).toBeTruthy(); 205 206 await FS.makeDirectoryAsync(dir); 207 208 error = null; 209 try { 210 await FS.makeDirectoryAsync(dir); 211 } catch (e) { 212 error = e; 213 } 214 t.expect(error).toBeTruthy(); 215 216 await FS.writeAsStringAsync(path, contents); 217 218 t.expect(await FS.readAsStringAsync(path)).toBe(contents); 219 } 220 ); 221 222 t.it( 223 'delete(dir) -> write(dir/dir2/file)[error] -> ' + 224 'mkdir(dir/dir2, intermediates) -> ' + 225 'mkdir(dir/dir2, intermediates) -> write(dir/dir2/file) -> read', 226 async () => { 227 let error; 228 const path = FS.documentDirectory + 'dir/dir2/file'; 229 const dir = FS.documentDirectory + 'dir/dir2'; 230 const contents = 'hello, world'; 231 232 await FS.deleteAsync(dir, { idempotent: true }); 233 234 error = null; 235 try { 236 await FS.writeAsStringAsync(path, contents); 237 } catch (e) { 238 error = e; 239 } 240 t.expect(error).toBeTruthy(); 241 242 await FS.makeDirectoryAsync(dir, { 243 intermediates: true, 244 }); 245 246 error = null; 247 try { 248 await FS.makeDirectoryAsync(dir); 249 } catch (e) { 250 error = e; 251 } 252 t.expect(error).toBeTruthy(); 253 254 await FS.writeAsStringAsync(path, contents); 255 256 t.expect(await FS.readAsStringAsync(path)).toBe(contents); 257 } 258 ); 259 260 t.it( 261 'delete(dir, idempotent) -> make tree -> check contents ' + 262 '-> check directory listings' + 263 '-> move -> check directory listings' + 264 '-> copy -> check directory listings', 265 async () => { 266 let error; 267 const dir = FS.documentDirectory + 'dir'; 268 269 await FS.deleteAsync(dir, { idempotent: true }); 270 271 await FS.makeDirectoryAsync(dir + '/child1', { 272 intermediates: true, 273 }); 274 await FS.makeDirectoryAsync(dir + '/child2', { 275 intermediates: true, 276 }); 277 278 await FS.writeAsStringAsync(dir + '/file1', 'contents1'); 279 await FS.writeAsStringAsync(dir + '/file2', 'contents2'); 280 281 await FS.writeAsStringAsync(dir + '/child1/file3', 'contents3'); 282 283 await FS.writeAsStringAsync(dir + '/child2/file4', 'contents4'); 284 await FS.writeAsStringAsync(dir + '/child2/file5', 'contents5'); 285 286 const checkContents = async (path, contents) => 287 t.expect(await FS.readAsStringAsync(path)).toBe(contents); 288 289 await checkContents(dir + '/file1', 'contents1'); 290 await checkContents(dir + '/file2', 'contents2'); 291 await checkContents(dir + '/child1/file3', 'contents3'); 292 await checkContents(dir + '/child2/file4', 'contents4'); 293 await checkContents(dir + '/child2/file5', 'contents5'); 294 295 const checkDirectory = async (path, expected) => { 296 const list = await FS.readDirectoryAsync(path); 297 t.expect(list.sort()).toEqual(expected.sort()); 298 }; 299 300 const checkRoot = async root => { 301 await checkDirectory(root, ['file1', 'file2', 'child1', 'child2']); 302 await checkDirectory(root + '/child1', ['file3']); 303 await checkDirectory(root + '/child2', ['file4', 'file5']); 304 305 error = null; 306 try { 307 await checkDirectory(root + '/file1', ['nope']); 308 } catch (e) { 309 error = e; 310 } 311 t.expect(error).toBeTruthy(); 312 }; 313 314 await checkRoot(dir); 315 316 await FS.deleteAsync(FS.documentDirectory + 'moved', { 317 idempotent: true, 318 }); 319 await FS.moveAsync({ from: dir, to: FS.documentDirectory + 'moved' }); 320 await checkRoot(FS.documentDirectory + 'moved'); 321 await FS.copyAsync({ 322 from: FS.documentDirectory + 'moved', 323 to: FS.documentDirectory + 'copied', 324 }); 325 await checkRoot(FS.documentDirectory + 'copied'); 326 } 327 ); 328 329 t.it( 330 'delete(idempotent) -> download(md5) -> getInfo(size)', 331 async () => { 332 const localUri = FS.documentDirectory + 'download1.png'; 333 334 await FS.deleteAsync(localUri, { idempotent: true }); 335 336 const { md5 } = await FS.downloadAsync( 337 'https://s3-us-west-1.amazonaws.com/test-suite-data/avatar2.png', 338 localUri, 339 { md5: true } 340 ); 341 t.expect(md5).toBe('1e02045c10b8f1145edc7c8375998f87'); 342 343 const { size, modificationTime } = await FS.getInfoAsync(localUri); 344 t.expect(size).toBe(3230); 345 const nowTime = 0.001 * new Date().getTime(); 346 t.expect(nowTime - modificationTime).toBeLessThan(3600); 347 348 await FS.deleteAsync(localUri); 349 }, 350 30000 351 ); 352 353 t.it('throws out-of-scope exceptions', async () => { 354 const p = FS.documentDirectory; 355 356 await throws(() => FS.getInfoAsync(p + '../hello/world')); 357 await throws(() => FS.readAsStringAsync(p + '../hello/world')); 358 await throws(() => FS.writeAsStringAsync(p + '../hello/world', '')); 359 await throws(() => FS.deleteAsync(p + '../hello/world')); 360 await throws(() => FS.deleteAsync(p)); 361 await throws(() => FS.deleteAsync(FS.cacheDirectory)); 362 await throws(() => FS.moveAsync({ from: p + '../a/b', to: 'c' })); 363 await throws(() => FS.moveAsync({ from: 'c', to: p + '../a/b' })); 364 await throws(() => FS.copyAsync({ from: p + '../a/b', to: 'c' })); 365 await throws(() => FS.copyAsync({ from: 'c', to: p + '../a/b' })); 366 await throws(() => FS.makeDirectoryAsync(p + '../hello/world')); 367 await throws(() => FS.readDirectoryAsync(p + '../hello/world')); 368 await throws(() => FS.downloadAsync('http://www.google.com', p + '../hello/world')); 369 await throws(() => FS.readDirectoryAsync(p + '../')); 370 await throws(() => FS.downloadAsync('http://www.google.com', p + '../hello/world')); 371 }); 372 373 t.it('missing parameters', async () => { 374 const p = FS.documentDirectory + 'test'; 375 376 await throws(() => FS.moveAsync({ from: p })); 377 await throws(() => FS.moveAsync({ to: p })); 378 await throws(() => FS.copyAsync({ from: p })); 379 await throws(() => FS.copyAsync({ to: p })); 380 }); 381 382 t.it('can read root directories', async () => { 383 await FS.readDirectoryAsync(FS.documentDirectory); 384 await FS.readDirectoryAsync(FS.cacheDirectory); 385 }); 386 387 /*t.it('can copy from `CameraRoll`, verify hash, other methods restricted', async () => { 388 await Promise.all( 389 (await CameraRoll.getPhotos({ 390 first: 1, 391 })).edges.map(async ({ node: { image: { uri: cameraRollUri } } }) => { 392 const destinationUri = FS.documentDirectory + 'photo.jpg'; 393 394 await throws(() => FS.readAsStringAsync(cameraRollUri)); 395 await throws(() => FS.writeAsStringAsync(cameraRollUri)); 396 await throws(() => FS.deleteAsync(cameraRollUri)); 397 await throws(() => FS.moveAsync({ from: cameraRollUri, to: destinationUri })); 398 await throws(() => FS.copyAsync({ from: destinationUri, to: cameraRollUri })); 399 await throws(() => FS.makeDirectoryAsync(cameraRollUri)); 400 await throws(() => FS.readDirectoryAsync(cameraRollUri)); 401 await throws(() => FS.downloadAsync('http://www.google.com', cameraRollUri)); 402 403 await FS.copyAsync({ from: cameraRollUri, to: destinationUri }); 404 405 const origInfo = await FS.getInfoAsync(cameraRollUri, { 406 size: true, 407 md5: true, 408 }); 409 const copyInfo = await FS.getInfoAsync(destinationUri, { 410 size: true, 411 md5: true, 412 }); 413 414 t.expect(origInfo.md5).toEqual(copyInfo.md5); 415 t.expect(origInfo.size).toEqual(copyInfo.size); 416 }) 417 ); 418 });*/ 419 420 t.it( 421 'download(network failure)', 422 async () => { 423 const localUri = FS.documentDirectory + 'download1.png'; 424 425 const assertExists = async expectedToExist => { 426 let { exists } = await FS.getInfoAsync(localUri); 427 if (expectedToExist) { 428 t.expect(exists).toBeTruthy(); 429 } else { 430 t.expect(exists).not.toBeTruthy(); 431 } 432 }; 433 434 await FS.deleteAsync(localUri, { idempotent: true }); 435 await assertExists(false); 436 437 let error; 438 try { 439 const { md5, uri } = await FS.downloadAsync( 440 'https://nonexistent-subdomain.expo.io', 441 localUri, 442 { md5: true } 443 ); 444 } catch (e) { 445 error = e; 446 } 447 t.expect(error).toBeTruthy(); 448 await assertExists(false); 449 await FS.deleteAsync(localUri, { idempotent: true }); 450 }, 451 30000 452 ); 453 454 t.it( 455 'download(404)', 456 async () => { 457 const localUri = FS.documentDirectory + 'download1.png'; 458 459 const assertExists = async expectedToExist => { 460 let { exists } = await FS.getInfoAsync(localUri); 461 if (expectedToExist) { 462 t.expect(exists).toBeTruthy(); 463 } else { 464 t.expect(exists).not.toBeTruthy(); 465 } 466 }; 467 468 await FS.deleteAsync(localUri, { idempotent: true }); 469 await assertExists(false); 470 471 const { md5, uri, status } = await FS.downloadAsync('https://expo.io/404', localUri, { 472 md5: true, 473 }); 474 await assertExists(true); 475 t.expect(status).toBe(404); 476 477 await FS.deleteAsync(localUri); 478 await assertExists(false); 479 }, 480 30000 481 ); 482 }); 483} 484