1import { ExpoConfig } from '@expo/config-types';
2import * as fs from 'fs';
3import { vol } from 'memfs';
4import * as path from 'path';
5
6import * as WarningAggregator from '../../utils/warnings';
7import {
8  formatDeviceFamilies,
9  getDeviceFamilies,
10  getIsTabletOnly,
11  getSupportsTablet,
12  setDeviceFamily,
13} from '../DeviceFamily';
14import { getPbxproj } from '../utils/Xcodeproj';
15
16const fsReal = jest.requireActual('fs') as typeof fs;
17
18jest.mock('fs');
19jest.mock('../../utils/warnings');
20
21const TABLET_AND_PHONE_SUPPORTED = [1, 2];
22const ONLY_PHONE_SUPPORTED = [1];
23const ONLY_TABLET_SUPPORTED = [2];
24
25describe(getDeviceFamilies, () => {
26  it(`warns about invalid config`, () => {
27    getDeviceFamilies({ ios: { isTabletOnly: true, supportsTablet: false } } as any);
28    expect(WarningAggregator.addWarningIOS).toHaveBeenLastCalledWith(
29      'ios.supportsTablet',
30      'Found contradictory values: `{ ios: { isTabletOnly: true, supportsTablet: false } }`. Using `{ isTabletOnly: true }`.'
31    );
32  });
33});
34
35describe('device family', () => {
36  it(`returns false ios.isTabletOnly is not provided`, () => {
37    expect(getIsTabletOnly({ ios: {} })).toBe(false);
38  });
39
40  it(`returns true ios.isTabletOnly is provided`, () => {
41    expect(getIsTabletOnly({ ios: { isTabletOnly: true } })).toBe(true);
42  });
43
44  it(`returns false ios.supportsTablet is provided`, () => {
45    expect(getSupportsTablet({ ios: {} })).toBe(false);
46  });
47
48  it(`returns true ios.supportsTablet is provided`, () => {
49    expect(getSupportsTablet({ ios: { supportsTablet: true } })).toBe(true);
50  });
51
52  it(`supports tablet and mobile if supportsTablet is true`, () => {
53    expect(getDeviceFamilies({ ios: { supportsTablet: true } })).toEqual(
54      TABLET_AND_PHONE_SUPPORTED
55    );
56  });
57
58  it(`supports only mobile if supportsTablet is blank/false and isTabletOnly is blank/false`, () => {
59    expect(getDeviceFamilies({ ios: {} })).toEqual(ONLY_PHONE_SUPPORTED);
60    expect(getDeviceFamilies({ ios: { supportsTablet: false, isTabletOnly: false } })).toEqual(
61      ONLY_PHONE_SUPPORTED
62    );
63  });
64
65  it(`supports only tablet if isTabletOnly is true`, () => {
66    expect(getDeviceFamilies({ ios: { isTabletOnly: true } })).toEqual(ONLY_TABLET_SUPPORTED);
67  });
68
69  // It's important that this format is always correct.
70  // Otherwise the xcode parser will throw `Expected ".", "/*", ";", or [0-9] but "," found.` when we attempt to write to it.
71  it(`formats the families correctly`, () => {
72    expect(formatDeviceFamilies([1])).toEqual(`"1"`);
73    expect(formatDeviceFamilies([1, 2, 3])).toEqual(`"1,2,3"`);
74  });
75
76  // TODO: update tests to run against pbxproj
77  // it(`sets to phone only if no value is provided`, () => {
78  //   expect(setDeviceFamily({}, {})).toMatchObject({ UIDeviceFamily: ONLY_PHONE_SUPPORTED });
79  // });
80
81  // it(`sets to given config when provided`, () => {
82  //   expect(setDeviceFamily({ ios: { supportsTablet: true } }, {})).toMatchObject({
83  //     UIDeviceFamily: TABLET_AND_PHONE_SUPPORTED,
84  //   });
85  // });
86});
87
88describe(setDeviceFamily, () => {
89  const projectRoot = '/tablet';
90  beforeAll(async () => {
91    vol.fromJSON(
92      {
93        'ios/testproject.xcodeproj/project.pbxproj': fsReal.readFileSync(
94          path.join(__dirname, 'fixtures/project.pbxproj'),
95          'utf-8'
96        ),
97        'ios/testproject/AppDelegate.m': '',
98      },
99      projectRoot
100    );
101  });
102
103  afterAll(() => {
104    vol.reset();
105  });
106
107  it('updates device families without throwing', async () => {
108    setDeviceFamilyForRoot({ name: '', slug: '', ios: {} }, projectRoot);
109    setDeviceFamilyForRoot({ name: '', slug: '', ios: { supportsTablet: true } }, projectRoot);
110    setDeviceFamilyForRoot({ name: '', slug: '', ios: { isTabletOnly: true } }, projectRoot);
111  });
112});
113
114function setDeviceFamilyForRoot(config: ExpoConfig, projectRoot: string) {
115  let project = getPbxproj(projectRoot);
116  project = setDeviceFamily(config, { project });
117  fs.writeFileSync(project.filepath, project.writeSync());
118}
119