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