1'use strict';
2
3import { Asset } from 'expo-asset';
4import { BarCodeScanner } from 'expo-barcode-scanner';
5import React from 'react';
6import { Platform } from 'react-native';
7
8import * as TestUtils from '../TestUtils';
9import { mountAndWaitFor as originalMountAndWaitFor } from './helpers';
10
11export const name = 'BarCodeScanner';
12const style = { width: 200, height: 200 };
13
14export async function test(t, { setPortalChild, cleanupPortal }) {
15  const shouldSkipTestsRequiringPermissions = await TestUtils.shouldSkipTestsRequiringPermissionsAsync();
16  const describeWithPermissions = shouldSkipTestsRequiringPermissions ? t.xdescribe : t.describe;
17
18  const testPoint = (value, expected, inaccuracy) => {
19    t.expect(value).toBeGreaterThanOrEqual(expected - inaccuracy);
20    t.expect(value).toBeLessThan(expected + inaccuracy);
21  };
22
23  const testBarCodeBounds = (bounds, expectedBounds, sizeInaccuracy, originInaccuracy) => {
24    t.expect(bounds).toBeDefined();
25    t.expect(typeof bounds.origin).toBe('object');
26    t.expect(typeof bounds.origin.x).toBe('number');
27    t.expect(typeof bounds.origin.y).toBe('number');
28    t.expect(typeof bounds.size).toBe('object');
29    t.expect(typeof bounds.size.width).toBe('number');
30    t.expect(typeof bounds.size.height).toBe('number');
31
32    testPoint(bounds.origin.x, expectedBounds.origin.x, originInaccuracy);
33    testPoint(bounds.origin.y, expectedBounds.origin.y, originInaccuracy);
34
35    testPoint(bounds.size.width, expectedBounds.size.width, sizeInaccuracy);
36    testPoint(bounds.size.height, expectedBounds.size.height, sizeInaccuracy);
37  };
38
39  describeWithPermissions('BarCodeScanner', () => {
40    const mountAndWaitFor = (child, propName = 'ref') =>
41      new Promise(resolve => {
42        const response = originalMountAndWaitFor(child, propName, setPortalChild);
43        setTimeout(() => resolve(response), 1500);
44      });
45
46    t.beforeAll(async () => {
47      await TestUtils.acceptPermissionsAndRunCommandAsync(() => {
48        return BarCodeScanner.requestPermissionsAsync();
49      });
50    });
51
52    t.beforeEach(async () => {
53      const { status } = await BarCodeScanner.getPermissionsAsync();
54      t.expect(status).toEqual('granted');
55    });
56
57    t.afterEach(async () => {
58      await cleanupPortal();
59    });
60
61    t.describe('when created', () => {
62      t.it('displays the view', async () => {
63        await mountAndWaitFor(<BarCodeScanner style={style} />);
64      });
65    });
66
67    t.describe('scanFromURLAsync', () => {
68      t.it('returns empty result when there is no barcode', async () => {
69        const asset = await Asset.fromModule(require('../assets/black-128x256.png'));
70        const result = await BarCodeScanner.scanFromURLAsync(asset.uri);
71
72        t.expect(result).toBeDefined();
73        t.expect(result.length).toEqual(0);
74      });
75
76      t.it('scans a QR code from asset', async () => {
77        const asset = await Asset.fromModule(require('../assets/qrcode_expo.jpg'));
78        const result = await BarCodeScanner.scanFromURLAsync(asset.uri);
79
80        t.expect(result).toBeDefined();
81        t.expect(result.length).toEqual(1);
82        t.expect(result[0]).toBeDefined();
83        t.expect(result[0].type).toEqual(BarCodeScanner.Constants.BarCodeType.qr);
84        t.expect(result[0].data).toEqual('https://expo.io/');
85        testBarCodeBounds(
86          result[0].bounds,
87          {
88            origin: {
89              x: 40,
90              y: 40,
91            },
92            size: {
93              width: 210,
94              height: 210,
95            },
96          },
97          1,
98          1
99        );
100        t.expect(result[0].cornerPoints).toBeDefined();
101        t.expect(result[0].cornerPoints.length).toEqual(4);
102      });
103
104      t.it('scans a QR code from photo asset', async () => {
105        // Public domain photo from https://commons.wikimedia.org/wiki/File:QR_Code_Damaged.jpg
106        const asset = await Asset.fromModule(require('../assets/qrcode_photo_wikipedia.jpg'));
107        const result = await BarCodeScanner.scanFromURLAsync(asset.uri);
108
109        t.expect(result).toBeDefined();
110        t.expect(result.length).toEqual(1);
111        t.expect(result[0]).toBeDefined();
112        t.expect(result[0].type).toEqual(BarCodeScanner.Constants.BarCodeType.qr);
113        t.expect(result[0].data).toEqual('http://en.m.wikipedia.org');
114        testBarCodeBounds(
115          result[0].bounds,
116          {
117            origin: {
118              x: 94,
119              y: 94,
120            },
121            size: {
122              width: 294,
123              height: 296,
124            },
125          },
126          10,
127          10
128        );
129        t.expect(result[0].cornerPoints).toBeDefined();
130        t.expect(result[0].cornerPoints.length).toEqual(4);
131      });
132
133      t.it('scans a QR code from base64 URL', async () => {
134        const url =
135          'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyAQMAAAAk8RryAAAABlBMVEX/' +
136          '//8AAABVwtN+AAAAcElEQVQY04XFMQrAMAgFUCGr4FUCXQO9uuAq/KsEugq2m2bqWx79kQRn1EzGyu' +
137          '1whx/Pc+gxKSnXku4ZNdFQolq2m3jN9/SrD0Ws9l4Ysx5uj9QftqstqQatmey2ftjW6GPI7PvD2iYE' +
138          'uJbEmlT/eAEXiXvHFX7hfQAAAABJRU5ErkJggg==';
139        const result = await BarCodeScanner.scanFromURLAsync(url);
140
141        t.expect(result).toBeDefined();
142        t.expect(result.length).toEqual(1);
143        t.expect(result[0]).toBeDefined();
144        t.expect(result[0].type).toEqual(BarCodeScanner.Constants.BarCodeType.qr);
145        t.expect(result[0].data).toEqual('test');
146      });
147
148      if (Platform.OS === 'android') {
149        t.it('scans a Data Matrix code from asset', async () => {
150          const asset = await Asset.fromModule(require('../assets/datamatrix_expo.png'));
151          const result = await BarCodeScanner.scanFromURLAsync(asset.uri);
152
153          t.expect(result).toBeDefined();
154          t.expect(result.length).toEqual(1);
155          t.expect(result[0]).toBeDefined();
156          t.expect(result[0].type).toEqual(BarCodeScanner.Constants.BarCodeType.datamatrix);
157          t.expect(result[0].data).toEqual('https://expo.io/');
158          testBarCodeBounds(
159            result[0].bounds,
160            {
161              origin: {
162                x: 7,
163                y: 7,
164              },
165              size: {
166                width: 141,
167                height: 141,
168              },
169            },
170            1,
171            1
172          );
173          t.expect(result[0].cornerPoints).toBeDefined();
174          t.expect(result[0].cornerPoints.length).toEqual(4);
175        });
176      }
177
178      t.it('respects barCodeTypes parameter', async () => {
179        const asset = await Asset.fromModule(require('../assets/datamatrix_expo.png'));
180        const result = await BarCodeScanner.scanFromURLAsync(asset.uri, [
181          BarCodeScanner.Constants.BarCodeType.qr,
182        ]);
183
184        t.expect(result).toBeDefined();
185        t.expect(result.length).toEqual(0);
186      });
187
188      t.it('works with multiple codes', async () => {
189        const asset = await Asset.fromModule(require('../assets/multiple_codes.png'));
190        const result = await BarCodeScanner.scanFromURLAsync(asset.uri);
191
192        t.expect(result).toBeDefined();
193        t.expect(result.length > 0).toBe(true);
194      });
195    });
196  });
197}
198