xref: /expo/apps/test-suite/tests/MediaLibrary.js (revision 8ef929c3)
1import { Asset, Permissions, MediaLibrary } from 'expo';
2import { Platform } from 'react-native';
3
4const FILES = [
5  require('../assets/icons/app.png'),
6  require('../assets/icons/loading.png'),
7  require('../assets/black-128x256.png'),
8  require('../assets/big_buck_bunny.mp4'),
9];
10
11const WAIT_TIME = 1000;
12const IMG_NUMBER = 3;
13const VIDEO_NUMBER = 1;
14const F_SIZE = IMG_NUMBER + VIDEO_NUMBER;
15const MEDIA_TYPES = [MediaLibrary.MediaType.photo, MediaLibrary.MediaType.video];
16const DEFAULT_MEDIA_TYPES = [MediaLibrary.MediaType.photo];
17const DEFAULT_PAGE_SIZE = 20;
18const ASSET_KEYS = [
19  'id',
20  'filename',
21  'uri',
22  'mediaType',
23  'width',
24  'height',
25  'creationTime',
26  'modificationTime',
27  'duration',
28  Platform.OS == 'ios' ? 'mediaSubtypes' : 'albumId',
29];
30
31const INFO_KEYS = [
32  'localUri',
33  'location',
34  'exif',
35  ...(Platform != 'ios' ? [] : ['orientation', 'isFavorite']),
36];
37
38const ALBUM_KEYS = [
39  'id',
40  'title',
41  'assetCount',
42  ...(Platform != 'ios'
43    ? []
44    : ['type', 'startTime', 'endTime', 'approximateLocation', 'locationNames']),
45];
46
47const GET_ASSETS_KEYS = ['assets', 'endCursor', 'hasNextPage', 'totalCount'];
48const ALBUM_NAME = '__tseTyrarbiLaidem__';
49const WRONG_NAME = 'wertyuiopdfghjklvbnhjnftyujn';
50const FIRST_ALBUM = '__iosTestAlbum__';
51const SECOND_ALBUM = '__mublAtseTsoi__';
52const WRONG_ID = '1234567890';
53
54async function getFiles() {
55  await Promise.all(FILES.map(req => Asset.loadAsync(req)));
56  return FILES.map(file => Asset.fromModule(file));
57}
58
59async function getAssets(files) {
60  return await Promise.all(files.map(({ localUri }) => MediaLibrary.createAssetAsync(localUri)));
61}
62
63async function createAlbum(assets, name) {
64  const album = await MediaLibrary.createAlbumAsync(name, assets[0], false);
65  await MediaLibrary.addAssetsToAlbumAsync(assets.slice(1), album, false);
66  return album;
67}
68
69function timeoutWrapper(fun, time) {
70  return new Promise(resolve => {
71    setTimeout(() => {
72      fun();
73      resolve(null);
74    }, time);
75  });
76}
77
78export async function test(t) {
79  t.describe('MediaLibrary', async () => {
80    let testAssets;
81    let album;
82    let files;
83
84    t.beforeAll(async () => {
85      await Permissions.askAsync(Permissions.CAMERA_ROLL);
86      files = await getFiles();
87      testAssets = await getAssets(files);
88      album = await MediaLibrary.getAlbumAsync(ALBUM_NAME);
89      if (album == null) album = await createAlbum(testAssets, ALBUM_NAME);
90      else await MediaLibrary.addAssetsToAlbumAsync(testAssets, album, true);
91    });
92
93    t.describe('Every return value has proper shape', async () => {
94      t.it('createAssetAsync', () => {
95        const keys = Object.keys(testAssets[0]);
96        ASSET_KEYS.forEach(key => t.expect(keys).toContain(key));
97      });
98
99      t.it('getAssetInfoAsync', async () => {
100        const { assets } = await MediaLibrary.getAssetsAsync();
101        const value = await MediaLibrary.getAssetInfoAsync(assets[0]);
102        const keys = Object.keys(value);
103        INFO_KEYS.forEach(key => t.expect(keys).toContain(key));
104      });
105
106      t.it('getAlbumAsync', async () => {
107        const value = await MediaLibrary.getAlbumAsync(ALBUM_NAME);
108        const keys = Object.keys(value);
109        ALBUM_KEYS.forEach(key => t.expect(keys).toContain(key));
110      });
111
112      t.it('getAssetsAsync', async () => {
113        const value = await MediaLibrary.getAssetsAsync();
114        const keys = Object.keys(value);
115        GET_ASSETS_KEYS.forEach(key => t.expect(keys).toContain(key));
116      });
117    });
118
119    t.describe('Small tests', async () => {
120      t.it('Function getAlbums returns test album', async () => {
121        const albums = await MediaLibrary.getAlbumsAsync();
122        t.expect(albums.filter(elem => elem.id == album.id).length).toBe(1);
123      });
124
125      t.it('getAlbum returns test album', async () => {
126        const otherAlbum = await MediaLibrary.getAlbumAsync(ALBUM_NAME);
127        t.expect(otherAlbum.title).toBe(album.title);
128        t.expect(otherAlbum.id).toBe(album.id);
129        t.expect(otherAlbum.assetCount).toBe(F_SIZE);
130      });
131
132      t.it('getAlbum with not existing album', async () => {
133        const album = await MediaLibrary.getAlbumAsync(WRONG_NAME);
134        t.expect(album).toBeNull();
135      });
136
137      t.it('getAssetInfo with not existing id', async () => {
138        const asset = await MediaLibrary.getAssetInfoAsync(WRONG_ID);
139        t.expect(asset).toBeNull();
140      });
141
142      // On both platforms assets should perserve their id. On iOS it's native behaviour,
143      // but on Android it should be implemented (but it isn't)
144      // t.it("After createAlbum and addAssetsTo album all assets have the same id", async () => {
145      //   await Promise.all(testAssets.map(async asset => {
146      //     const info = await MediaLibrary.getAssetInfoAsync(asset);
147      //     t.expect(info.id).toBe(asset.id);
148      //   }));
149      // });
150    });
151
152    t.describe('getAssetsAsync', async () => {
153      t.it('No arguments', async () => {
154        const options = {};
155        const { assets } = await MediaLibrary.getAssetsAsync(options);
156        t.expect(assets.length).toBeLessThanOrEqual(DEFAULT_PAGE_SIZE);
157        t.expect(assets.length).toBeGreaterThanOrEqual(IMG_NUMBER);
158        assets.forEach(asset => t.expect(DEFAULT_MEDIA_TYPES).toContain(asset.mediaType));
159      });
160
161      t.it('album', async () => {
162        const options = { album };
163        const { assets } = await MediaLibrary.getAssetsAsync(options);
164        t.expect(assets.length).toBe(IMG_NUMBER);
165        assets.forEach(asset => t.expect(DEFAULT_MEDIA_TYPES).toContain(asset.mediaType));
166        if (Platform.OS == 'android')
167          assets.forEach(asset => t.expect(asset.albumId).toBe(album.id));
168      });
169
170      t.it('first, after', async () => {
171        const options = { first: 2, album };
172        {
173          const { assets, endCursor, hasNextPage, totalCount } = await MediaLibrary.getAssetsAsync(
174            options
175          );
176          t.expect(assets.length).toBe(2);
177          t.expect(totalCount).toBe(IMG_NUMBER);
178          t.expect(hasNextPage).toBeTruthy();
179          assets.forEach(asset => t.expect(DEFAULT_MEDIA_TYPES).toContain(asset.mediaType));
180          options.after = endCursor;
181        }
182        {
183          const { assets, hasNextPage, totalCount } = await MediaLibrary.getAssetsAsync(options);
184          t.expect(assets.length).toBe(IMG_NUMBER - 2);
185          t.expect(totalCount).toBe(IMG_NUMBER);
186          t.expect(hasNextPage).toBeFalsy();
187        }
188      });
189
190      t.it('mediaType: video', async () => {
191        const mediaType = MediaLibrary.MediaType.video;
192        const options = { mediaType, album };
193        const { assets } = await MediaLibrary.getAssetsAsync(options);
194        assets.forEach(asset => t.expect(asset.mediaType).toBe(mediaType));
195        t.expect(assets.length).toBe(1);
196      });
197
198      t.it('mediaType: photo', async () => {
199        const mediaType = MediaLibrary.MediaType.photo;
200        const options = { mediaType, album };
201        const { assets } = await MediaLibrary.getAssetsAsync(options);
202        t.expect(assets.length).toBe(IMG_NUMBER);
203        assets.forEach(asset => t.expect(asset.mediaType).toBe(mediaType));
204      });
205    });
206
207    t.describe('Delete tests', async () => {
208      t.it('deleteAsstetsAsync', async () => {
209        const { assets } = await MediaLibrary.getAssetsAsync({ album, mediaType: MEDIA_TYPES });
210        const result = await MediaLibrary.deleteAssetsAsync(assets.slice(0, 2));
211        const { assets: rest } = await MediaLibrary.getAssetsAsync({
212          album,
213          mediaType: MEDIA_TYPES,
214        });
215        t.expect(result).toEqual(true);
216        t.expect(rest.length).toBe(F_SIZE - 2);
217      });
218      t.it('deleteAlbumsAsync', async () => {
219        const result = await MediaLibrary.deleteAlbumsAsync(album, true);
220        t.expect(result).toEqual(true);
221        album = await MediaLibrary.getAlbumAsync(ALBUM_NAME);
222        t.expect(album).toBeNull();
223      });
224      t.it('deleteManyAlbums', async () => {
225        const assets = await getAssets(files.slice(0, 2));
226        let firstAlbum = await MediaLibrary.createAlbumAsync(FIRST_ALBUM, assets[0], false);
227        let secondAlbum = await MediaLibrary.createAlbumAsync(SECOND_ALBUM, assets[1], false);
228        await MediaLibrary.deleteAlbumsAsync([firstAlbum, secondAlbum], true);
229        firstAlbum = await MediaLibrary.getAlbumAsync(FIRST_ALBUM);
230        secondAlbum = await MediaLibrary.getAlbumAsync(SECOND_ALBUM);
231        const firstAsset = await MediaLibrary.getAssetInfoAsync(assets[0]);
232        const secondAsset = await MediaLibrary.getAssetInfoAsync(assets[1]);
233        t.expect(firstAlbum).toBeNull();
234        t.expect(secondAlbum).toBeNull();
235        t.expect(firstAsset).toBeNull();
236        t.expect(secondAsset).toBeNull();
237      });
238    });
239
240    t.describe('Listeners', async () => {
241      t.it('addAsset calls listener', async () => {
242        const spy = t.jasmine.createSpy('addAsset spy', () => {});
243        const remove = MediaLibrary.addListener(spy);
244        const asset = await MediaLibrary.createAssetAsync(files[0].localUri);
245        await timeoutWrapper(() => t.expect(spy).toHaveBeenCalled(), WAIT_TIME);
246        remove.remove();
247        await MediaLibrary.deleteAssetsAsync(asset);
248      });
249
250      t.it('remove listener', async () => {
251        const spy = t.jasmine.createSpy('remove spy', () => {});
252        const subscription = MediaLibrary.addListener(spy);
253        subscription.remove();
254        const asset = await MediaLibrary.createAssetAsync(files[0].localUri);
255        await MediaLibrary.deleteAssetsAsync(asset);
256        await timeoutWrapper(() => t.expect(spy).not.toHaveBeenCalled(), WAIT_TIME);
257      });
258
259      t.it('deleteListener calls listener', async () => {
260        const spy = t.jasmine.createSpy('deleteAsset spy', () => {});
261        const asset = await MediaLibrary.createAssetAsync(files[0].localUri);
262        const subscription = MediaLibrary.addListener(spy);
263        await MediaLibrary.deleteAssetsAsync(asset);
264        await timeoutWrapper(() => t.expect(spy).toHaveBeenCalled(), WAIT_TIME);
265        subscription.remove();
266      });
267
268      t.it('removeAllListeners', async () => {
269        const spy = t.jasmine.createSpy('removeAll', () => {});
270        MediaLibrary.addListener(spy);
271        MediaLibrary.removeAllListeners();
272        const asset = await MediaLibrary.createAssetAsync(files[0].localUri);
273        await timeoutWrapper(() => t.expect(spy).not.toHaveBeenCalled(), WAIT_TIME);
274        await MediaLibrary.deleteAssetsAsync(asset);
275      });
276    });
277  });
278}
279