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