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