1import { Asset } from 'expo-asset'; 2import * as FileSystem from 'expo-file-system'; 3import * as ImageManipulator from 'expo-image-manipulator'; 4import { Platform } from 'react-native'; 5 6export const name = 'ImageManipulator'; 7 8export async function test(t) { 9 t.describe('ImageManipulator', async () => { 10 let image; 11 12 t.beforeAll(async () => { 13 image = Asset.fromModule(require('../assets/example_image_1.jpg')); 14 await image.downloadAsync(); 15 }); 16 17 t.describe('manipulateAsync()', async () => { 18 t.it('returns valid image', async () => { 19 const result = await ImageManipulator.manipulateAsync(image.localUri, [ 20 { resize: { width: 100, height: 100 } }, 21 ]); 22 t.expect(result).toBeDefined(); 23 t.expect(typeof result.uri).toBe('string'); 24 t.expect(typeof result.width).toBe('number'); 25 t.expect(typeof result.height).toBe('number'); 26 }); 27 28 t.it('returns valid image from base64 data URL', async () => { 29 // 1x1 red image 30 const result = await ImageManipulator.manipulateAsync( 31 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8z8DwHwAFBQIAX8jx0gAAAABJRU5ErkJggg==', 32 [{ resize: { width: 100, height: 100 } }] 33 ); 34 t.expect(result).toBeDefined(); 35 t.expect(typeof result.uri).toBe('string'); 36 t.expect(result.width).toBe(100); 37 t.expect(result.height).toBe(100); 38 }); 39 40 t.it('saves with default format', async () => { 41 const result = await ImageManipulator.manipulateAsync(image.localUri, [ 42 { resize: { width: 100, height: 100 } }, 43 ]); 44 45 if (Platform.OS === 'web') { 46 t.expect(result.uri.startsWith('data:image/jpeg;base64,')).toBe(true); 47 } else { 48 t.expect(result.uri.endsWith('.jpg')).toBe(true); 49 } 50 }); 51 52 t.it('saves as JPEG', async () => { 53 const result = await ImageManipulator.manipulateAsync( 54 image.localUri, 55 [{ resize: { width: 100, height: 100 } }], 56 { 57 format: ImageManipulator.SaveFormat.JPEG, 58 } 59 ); 60 61 if (Platform.OS === 'web') { 62 t.expect(result.uri.startsWith('data:image/jpeg;base64,')).toBe(true); 63 } else { 64 t.expect(result.uri.endsWith('.jpg')).toBe(true); 65 } 66 }); 67 68 t.it('saves as PNG', async () => { 69 const result = await ImageManipulator.manipulateAsync( 70 image.localUri, 71 [{ resize: { width: 100, height: 100 } }], 72 { 73 format: ImageManipulator.SaveFormat.PNG, 74 } 75 ); 76 77 if (Platform.OS === 'web') { 78 t.expect(result.uri.startsWith('data:image/png;base64,')).toBe(true); 79 } else { 80 t.expect(result.uri.endsWith('.png')).toBe(true); 81 } 82 }); 83 84 t.it('provides Base64 with no header or newline terminator', async () => { 85 const result = await ImageManipulator.manipulateAsync( 86 image.localUri, 87 [{ resize: { width: 100, height: 100 } }], 88 { 89 base64: true, 90 } 91 ); 92 93 t.expect(typeof result.base64).toBe('string'); 94 t.expect(result.base64).not.toContain('\n'); 95 t.expect(result.base64).not.toContain('\r'); 96 t.expect(result.base64.startsWith('data:image/jpeg;base64,')).toBe(false); 97 }); 98 99 t.it('performs compression', async () => { 100 const result = await ImageManipulator.manipulateAsync( 101 image.localUri, 102 [{ flip: ImageManipulator.FlipType.Vertical }], 103 { 104 compress: 0.0, 105 } 106 ); 107 108 if (Platform.OS === 'web') { 109 const imageInfo = await fetch(image.localUri).then((a) => a.blob()); 110 const resultInfo = await fetch(result.uri).then((a) => a.blob()); 111 112 t.expect(imageInfo.size).toBeGreaterThan(resultInfo.size); 113 } else { 114 const imageInfo = await FileSystem.getInfoAsync(image.localUri); 115 const resultInfo = await FileSystem.getInfoAsync(result.uri); 116 117 t.expect(imageInfo.size).toBeGreaterThan(resultInfo.size); 118 } 119 }); 120 121 t.it('rotates images', async () => { 122 const result = await ImageManipulator.manipulateAsync(image.localUri, [{ rotate: 45 }]); 123 t.expect(result.width).toBeGreaterThan(image.width); 124 }); 125 126 t.it('flips horizontally', async () => { 127 const result = await ImageManipulator.manipulateAsync(image.localUri, [ 128 { flip: ImageManipulator.FlipType.Horizontal }, 129 ]); 130 t.expect(result.width).toBe(image.width); 131 t.expect(result.height).toBe(image.height); 132 }); 133 134 t.it('flips vertically', async () => { 135 const result = await ImageManipulator.manipulateAsync(image.localUri, [ 136 { flip: ImageManipulator.FlipType.Vertical }, 137 ]); 138 t.expect(result.width).toBe(image.width); 139 t.expect(result.height).toBe(image.height); 140 }); 141 142 t.it('resizes image', async () => { 143 const result = await ImageManipulator.manipulateAsync(image.localUri, [ 144 { resize: { width: 100, height: 100 } }, 145 ]); 146 t.expect(result.height).toBe(100); 147 t.expect(result.width).toBe(100); 148 }); 149 150 t.it('crops image', async () => { 151 const result = await ImageManipulator.manipulateAsync(image.localUri, [ 152 { crop: { originX: 20, originY: 20, width: 100, height: 100 } }, 153 ]); 154 t.expect(result.height).toBe(100); 155 t.expect(result.width).toBe(100); 156 }); 157 158 t.it('performs multiple transformations', async () => { 159 const result = await ImageManipulator.manipulateAsync(image.localUri, [ 160 { resize: { width: 200, height: 200 } }, 161 { flip: ImageManipulator.FlipType.Vertical }, 162 { rotate: 45 }, 163 { crop: { originX: 20, originY: 20, width: 100, height: 100 } }, 164 ]); 165 t.expect(result.height).toBe(100); 166 t.expect(result.width).toBe(100); 167 }); 168 }); 169 }); 170} 171