xref: /expo/apps/test-suite/tests/FileSystem.js (revision eafa96e0)
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