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('...b.a.-c.0.n...')).toBe(false);
56    expect(validatePackage('.')).toBe(false);
57    expect(validatePackage('. ..')).toBe(false);
58    expect(validatePackage('_')).toBe(false);
59    expect(validatePackage(',')).toBe(false);
60  });
61  it(`prevents using reserved java keywords`, () => {
62    expect(validatePackage('bacon.native.com')).toBe(false);
63    expect(validatePackage('byte')).toBe(false);
64  });
65});
66
67describe(getBundleIdWarningInternalAsync, () => {
68  resetOfflineMode();
69  it(`returns null if the URL cannot be reached`, async () => {
70    jest.mocked(isUrlAvailableAsync).mockResolvedValueOnce(false);
71    expect(await getBundleIdWarningInternalAsync('bacon')).toBe(null);
72  });
73  it(`returns null and warns if running in offline-mode`, async () => {
74    process.env.EXPO_OFFLINE = '1';
75    await expect(getBundleIdWarningInternalAsync('bacon')).resolves.toBe(null);
76    expect(Log.warn).toBeCalledWith(expect.stringMatching(/offline-mode/));
77  });
78  it(`returns warning if in use`, async () => {
79    jest.mocked(isUrlAvailableAsync).mockResolvedValueOnce(true);
80
81    jest.mocked(fetch).mockResolvedValueOnce({
82      status: 200,
83      json() {
84        return Promise.resolve({
85          resultCount: 1,
86          results: [
87            {
88              trackName: 'Pillar Valley',
89              sellerName: 'Evan Bacon',
90              kind: 'software',
91              artistName: 'Evan Bacon',
92              genres: ['Games', 'Entertainment', 'Family', 'Casual'],
93            },
94          ],
95        });
96      },
97    } as any);
98
99    expect(
100      stripAnsi(await getBundleIdWarningInternalAsync('com.bacon.pillarvalley'))
101    ).toMatchInlineSnapshot(
102      `"⚠️  The app Pillar Valley by Evan Bacon is already using com.bacon.pillarvalley"`
103    );
104  });
105});
106
107describe(getPackageNameWarningInternalAsync, () => {
108  resetOfflineMode();
109
110  it(`returns null if the URL cannot be reached`, async () => {
111    jest.mocked(isUrlAvailableAsync).mockResolvedValueOnce(false);
112    expect(await getPackageNameWarningInternalAsync('bacon')).toBe(null);
113  });
114  it(`returns null and warns if running in offline-mode`, async () => {
115    process.env.EXPO_OFFLINE = '1';
116    expect(await getPackageNameWarningInternalAsync('123')).toBe(null);
117    expect(Log.warn).toBeCalledWith(expect.stringMatching(/offline-mode/));
118  });
119  it(`returns warning if in use`, async () => {
120    jest.mocked(isUrlAvailableAsync).mockResolvedValueOnce(true);
121    jest.mocked(fetch).mockResolvedValueOnce({
122      status: 200,
123    } as any);
124
125    expect(
126      stripAnsi(await getPackageNameWarningInternalAsync('com.bacon.pillarvalley'))
127    ).toMatchInlineSnapshot(
128      `"⚠️  The package com.bacon.pillarvalley is already in use. Learn more: https://play.google.com/store/apps/details?id=com.bacon.pillarvalley"`
129    );
130  });
131});
132