xref: /expo/apps/test-suite/tests/FileSystem.js (revision d95ebc1a)
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        error = null;
255        try {
256          await FS.makeDirectoryAsync(dir, {
257            intermediates: true,
258          });
259        } catch (e) {
260          error = e;
261        }
262        t.expect(error).toBe(null);
263
264        await FS.writeAsStringAsync(path, contents);
265
266        t.expect(await FS.readAsStringAsync(path)).toBe(contents);
267      }
268    );
269
270    t.it(
271      'delete(dir, idempotent) -> make tree -> check contents ' +
272        '-> check directory listings' +
273        '-> move -> check directory listings' +
274        '-> copy -> check directory listings',
275      async () => {
276        let error;
277        const dir = FS.documentDirectory + 'dir';
278
279        await FS.deleteAsync(dir, { idempotent: true });
280
281        await FS.makeDirectoryAsync(dir + '/child1', {
282          intermediates: true,
283        });
284        await FS.makeDirectoryAsync(dir + '/child2', {
285          intermediates: true,
286        });
287
288        await FS.writeAsStringAsync(dir + '/file1', 'contents1');
289        await FS.writeAsStringAsync(dir + '/file2', 'contents2');
290
291        await FS.writeAsStringAsync(dir + '/child1/file3', 'contents3');
292
293        await FS.writeAsStringAsync(dir + '/child2/file4', 'contents4');
294        await FS.writeAsStringAsync(dir + '/child2/file5', 'contents5');
295
296        const checkContents = async (path, contents) =>
297          t.expect(await FS.readAsStringAsync(path)).toBe(contents);
298
299        await checkContents(dir + '/file1', 'contents1');
300        await checkContents(dir + '/file2', 'contents2');
301        await checkContents(dir + '/child1/file3', 'contents3');
302        await checkContents(dir + '/child2/file4', 'contents4');
303        await checkContents(dir + '/child2/file5', 'contents5');
304
305        const checkDirectory = async (path, expected) => {
306          const list = await FS.readDirectoryAsync(path);
307          t.expect(list.sort()).toEqual(expected.sort());
308        };
309
310        const checkRoot = async root => {
311          await checkDirectory(root, ['file1', 'file2', 'child1', 'child2']);
312          await checkDirectory(root + '/child1', ['file3']);
313          await checkDirectory(root + '/child2', ['file4', 'file5']);
314
315          error = null;
316          try {
317            await checkDirectory(root + '/file1', ['nope']);
318          } catch (e) {
319            error = e;
320          }
321          t.expect(error).toBeTruthy();
322        };
323
324        await checkRoot(dir);
325
326        await FS.deleteAsync(FS.documentDirectory + 'moved', {
327          idempotent: true,
328        });
329        await FS.moveAsync({ from: dir, to: FS.documentDirectory + 'moved' });
330        await checkRoot(FS.documentDirectory + 'moved');
331        await FS.copyAsync({
332          from: FS.documentDirectory + 'moved',
333          to: FS.documentDirectory + 'copied',
334        });
335        await checkRoot(FS.documentDirectory + 'copied');
336      }
337    );
338
339    t.it(
340      'delete(idempotent) -> download(md5) -> getInfo(size)',
341      async () => {
342        const localUri = FS.documentDirectory + 'download1.png';
343
344        await FS.deleteAsync(localUri, { idempotent: true });
345
346        const { md5 } = await FS.downloadAsync(
347          'https://s3-us-west-1.amazonaws.com/test-suite-data/avatar2.png',
348          localUri,
349          { md5: true }
350        );
351        t.expect(md5).toBe('1e02045c10b8f1145edc7c8375998f87');
352
353        const { size, modificationTime } = await FS.getInfoAsync(localUri);
354        t.expect(size).toBe(3230);
355        const nowTime = 0.001 * new Date().getTime();
356        t.expect(nowTime - modificationTime).toBeLessThan(3600);
357
358        await FS.deleteAsync(localUri);
359      },
360      30000
361    );
362
363    t.it('throws out-of-scope exceptions', async () => {
364      const p = FS.documentDirectory;
365
366      await throws(() => FS.getInfoAsync(p + '../hello/world'));
367      await throws(() => FS.readAsStringAsync(p + '../hello/world'));
368      await throws(() => FS.writeAsStringAsync(p + '../hello/world', ''));
369      await throws(() => FS.deleteAsync(p + '../hello/world'));
370      await throws(() => FS.deleteAsync(p));
371      await throws(() => FS.deleteAsync(FS.cacheDirectory));
372      await throws(() => FS.moveAsync({ from: p + '../a/b', to: 'c' }));
373      await throws(() => FS.moveAsync({ from: 'c', to: p + '../a/b' }));
374      await throws(() => FS.copyAsync({ from: p + '../a/b', to: 'c' }));
375      await throws(() => FS.copyAsync({ from: 'c', to: p + '../a/b' }));
376      await throws(() => FS.makeDirectoryAsync(p + '../hello/world'));
377      await throws(() => FS.readDirectoryAsync(p + '../hello/world'));
378      await throws(() => FS.downloadAsync('http://www.google.com', p + '../hello/world'));
379      await throws(() => FS.readDirectoryAsync(p + '../'));
380      await throws(() => FS.downloadAsync('http://www.google.com', p + '../hello/world'));
381    });
382
383    t.it('missing parameters', async () => {
384      const p = FS.documentDirectory + 'test';
385
386      await throws(() => FS.moveAsync({ from: p }));
387      await throws(() => FS.moveAsync({ to: p }));
388      await throws(() => FS.copyAsync({ from: p }));
389      await throws(() => FS.copyAsync({ to: p }));
390    });
391
392    t.it('can read root directories', async () => {
393      await FS.readDirectoryAsync(FS.documentDirectory);
394      await FS.readDirectoryAsync(FS.cacheDirectory);
395    });
396
397    /*t.it('can copy from `CameraRoll`, verify hash, other methods restricted', async () => {
398      await Promise.all(
399        (await CameraRoll.getPhotos({
400          first: 1,
401        })).edges.map(async ({ node: { image: { uri: cameraRollUri } } }) => {
402          const destinationUri = FS.documentDirectory + 'photo.jpg';
403
404          await throws(() => FS.readAsStringAsync(cameraRollUri));
405          await throws(() => FS.writeAsStringAsync(cameraRollUri));
406          await throws(() => FS.deleteAsync(cameraRollUri));
407          await throws(() => FS.moveAsync({ from: cameraRollUri, to: destinationUri }));
408          await throws(() => FS.copyAsync({ from: destinationUri, to: cameraRollUri }));
409          await throws(() => FS.makeDirectoryAsync(cameraRollUri));
410          await throws(() => FS.readDirectoryAsync(cameraRollUri));
411          await throws(() => FS.downloadAsync('http://www.google.com', cameraRollUri));
412
413          await FS.copyAsync({ from: cameraRollUri, to: destinationUri });
414
415          const origInfo = await FS.getInfoAsync(cameraRollUri, {
416            size: true,
417            md5: true,
418          });
419          const copyInfo = await FS.getInfoAsync(destinationUri, {
420            size: true,
421            md5: true,
422          });
423
424          t.expect(origInfo.md5).toEqual(copyInfo.md5);
425          t.expect(origInfo.size).toEqual(copyInfo.size);
426        })
427      );
428    });*/
429
430    t.it(
431      'download(network failure)',
432      async () => {
433        const localUri = FS.documentDirectory + 'download1.png';
434
435        const assertExists = async expectedToExist => {
436          let { exists } = await FS.getInfoAsync(localUri);
437          if (expectedToExist) {
438            t.expect(exists).toBeTruthy();
439          } else {
440            t.expect(exists).not.toBeTruthy();
441          }
442        };
443
444        await FS.deleteAsync(localUri, { idempotent: true });
445        await assertExists(false);
446
447        let error;
448        try {
449          const { md5, uri } = await FS.downloadAsync(
450            'https://nonexistent-subdomain.expo.io',
451            localUri,
452            { md5: true }
453          );
454        } catch (e) {
455          error = e;
456        }
457        t.expect(error).toBeTruthy();
458        await assertExists(false);
459        await FS.deleteAsync(localUri, { idempotent: true });
460      },
461      30000
462    );
463
464    t.it(
465      'download(404)',
466      async () => {
467        const localUri = FS.documentDirectory + 'download1.png';
468
469        const assertExists = async expectedToExist => {
470          let { exists } = await FS.getInfoAsync(localUri);
471          if (expectedToExist) {
472            t.expect(exists).toBeTruthy();
473          } else {
474            t.expect(exists).not.toBeTruthy();
475          }
476        };
477
478        await FS.deleteAsync(localUri, { idempotent: true });
479        await assertExists(false);
480
481        const { md5, uri, status } = await FS.downloadAsync('https://expo.io/404', localUri, {
482          md5: true,
483        });
484        await assertExists(true);
485        t.expect(status).toBe(404);
486
487        await FS.deleteAsync(localUri);
488        await assertExists(false);
489      },
490      30000
491    );
492  });
493}
494