1import fetch from 'node-fetch';
2
3import { Log } from '../../log';
4import { stripAnsi } from '../ansi';
5import { isUrlAvailableAsync } from '../url';
6import {
7  getBundleIdWarningInternalAsync,
8  getPackageNameWarningInternalAsync,
9  validateBundleId,
10  validatePackage,
11  validatePackageWithWarning,
12} from '../validateApplicationId';
13
14jest.mock('node-fetch');
15jest.mock('../../log');
16jest.mock('../url');
17
18function resetOfflineMode() {
19  beforeEach(() => {
20    delete process.env.EXPO_OFFLINE;
21  });
22  afterAll(() => {
23    delete process.env.EXPO_OFFLINE;
24  });
25}
26
27describe(validateBundleId, () => {
28  it(`validates`, () => {
29    expect(validateBundleId('bacon')).toBe(true);
30    expect(validateBundleId('...b.a.-c.0.n...')).toBe(true);
31    expect(validateBundleId('.')).toBe(true);
32    expect(validateBundleId('. ..')).toBe(false);
33    expect(validateBundleId('_')).toBe(false);
34    expect(validateBundleId(',')).toBe(false);
35  });
36});
37
38describe(validatePackageWithWarning, () => {
39  it(`validates with warnings`, () => {
40    expect(validatePackageWithWarning('bacon.native')).toEqual(
41      `"native" is a reserved Java keyword.`
42    );
43    expect(validatePackageWithWarning('bacon')).toEqual(
44      'Package name must contain more than one segment, separated by ".", e.g. com.bacon'
45    );
46    expect(validatePackageWithWarning(',')).toEqual(
47      `Package name must contain more than one segment, separated by ".", e.g. com.,`
48    );
49  });
50});
51describe(validatePackage, () => {
52  it(`validates`, () => {
53    expect(validatePackage('bacon.com.hey')).toBe(true);
54    expect(validatePackage('bacon')).toBe(false);
55    expect(validatePackage('com.native')).toBe(false);
56    expect(validatePackage('native.android')).toBe(false);
57    expect(validatePackage('...b.a.-c.0.n...')).toBe(false);
58    expect(validatePackage('.')).toBe(false);
59    expect(validatePackage('. ..')).toBe(false);
60    expect(validatePackage('_')).toBe(false);
61    expect(validatePackage(',')).toBe(false);
62  });
63  it(`prevents using reserved java keywords`, () => {
64    expect(validatePackage('bacon.native.com')).toBe(false);
65    expect(validatePackage('byte')).toBe(false);
66  });
67});
68
69describe(getBundleIdWarningInternalAsync, () => {
70  resetOfflineMode();
71  it(`returns null if the URL cannot be reached`, async () => {
72    jest.mocked(isUrlAvailableAsync).mockResolvedValueOnce(false);
73    expect(await getBundleIdWarningInternalAsync('bacon')).toBe(null);
74  });
75  it(`returns null and warns if running in offline-mode`, async () => {
76    process.env.EXPO_OFFLINE = '1';
77    await expect(getBundleIdWarningInternalAsync('bacon')).resolves.toBe(null);
78    expect(Log.warn).toBeCalledWith(expect.stringMatching(/offline-mode/));
79  });
80  it(`returns warning if in use`, async () => {
81    jest.mocked(isUrlAvailableAsync).mockResolvedValueOnce(true);
82
83    jest.mocked(fetch).mockResolvedValueOnce({
84      status: 200,
85      json() {
86        return Promise.resolve({
87          resultCount: 1,
88          results: [
89            {
90              trackName: 'Pillar Valley',
91              sellerName: 'Evan Bacon',
92              kind: 'software',
93              artistName: 'Evan Bacon',
94              genres: ['Games', 'Entertainment', 'Family', 'Casual'],
95            },
96          ],
97        });
98      },
99    } as any);
100
101    expect(
102      stripAnsi(await getBundleIdWarningInternalAsync('com.bacon.pillarvalley'))
103    ).toMatchInlineSnapshot(
104      `"⚠️  The app Pillar Valley by Evan Bacon is already using com.bacon.pillarvalley"`
105    );
106  });
107});
108
109describe(getPackageNameWarningInternalAsync, () => {
110  resetOfflineMode();
111
112  it(`returns null if the URL cannot be reached`, async () => {
113    jest.mocked(isUrlAvailableAsync).mockResolvedValueOnce(false);
114    expect(await getPackageNameWarningInternalAsync('bacon')).toBe(null);
115  });
116  it(`returns null and warns if running in offline-mode`, async () => {
117    process.env.EXPO_OFFLINE = '1';
118    expect(await getPackageNameWarningInternalAsync('123')).toBe(null);
119    expect(Log.warn).toBeCalledWith(expect.stringMatching(/offline-mode/));
120  });
121  it(`returns warning if in use`, async () => {
122    jest.mocked(isUrlAvailableAsync).mockResolvedValueOnce(true);
123    jest.mocked(fetch).mockResolvedValueOnce({
124      status: 200,
125    } as any);
126
127    expect(
128      stripAnsi(await getPackageNameWarningInternalAsync('com.bacon.pillarvalley'))
129    ).toMatchInlineSnapshot(
130      `"⚠️  The package com.bacon.pillarvalley is already in use. Learn more: https://play.google.com/store/apps/details?id=com.bacon.pillarvalley"`
131    );
132  });
133});
134