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