1import * as fs from 'fs';
2import { vol } from 'memfs';
3import * as path from 'path';
4
5import { XcodeProject } from '../../Plugin.types';
6import * as WarningAggregator from '../../utils/warnings';
7import { setBitcodeWithConfig } from '../Bitcode';
8import { getPbxproj, isNotComment } from '../utils/Xcodeproj';
9
10const fsReal = jest.requireActual('fs') as typeof fs;
11
12jest.mock('fs');
13jest.mock('../../utils/warnings');
14
15describe(setBitcodeWithConfig, () => {
16  const projectRoot = '/tablet';
17  beforeEach(async () => {
18    vol.fromJSON(
19      {
20        'ios/testproject.xcodeproj/project.pbxproj': fsReal.readFileSync(
21          path.join(__dirname, 'fixtures/project.pbxproj'),
22          'utf-8'
23        ),
24        'ios/testproject/AppDelegate.m': '',
25      },
26      projectRoot
27    );
28  });
29
30  afterEach(() => {
31    vol.reset();
32  });
33
34  it('defaults to not modifying the bitcode settings', async () => {
35    setBitcodeEnabledForRoot({ ios: {} }, projectRoot, validateDefaultBitcode);
36  });
37
38  it('enables bitcode for everything', async () => {
39    setBitcodeEnabledForRoot({ ios: { bitcode: true } }, projectRoot, (project) => {
40      const configurations = getConfigurations(project);
41      for (const [, configuration] of configurations) {
42        expect(configuration.buildSettings.ENABLE_BITCODE).toBe('YES');
43      }
44    });
45  });
46
47  it('disables bitcode for everything', async () => {
48    setBitcodeEnabledForRoot({ ios: { bitcode: false } }, projectRoot, (project) => {
49      const configurations = getConfigurations(project);
50      for (const [, configuration] of configurations) {
51        expect(configuration.buildSettings.ENABLE_BITCODE).toBe('NO');
52      }
53    });
54  });
55
56  it('enables bitcode on specific configuration', async () => {
57    setBitcodeEnabledForRoot({ ios: { bitcode: 'Debug' } }, projectRoot, (project) => {
58      const configurations = getConfigurations(project);
59      for (const [, configuration] of configurations) {
60        // ensure all others are disabled
61        expect(configuration.buildSettings.ENABLE_BITCODE).toBe(
62          configuration.name === 'Debug' ? 'YES' : 'NO'
63        );
64      }
65    });
66  });
67
68  it('warns when enabling bitcode on an invalid configuration', async () => {
69    setBitcodeEnabledForRoot({ ios: { bitcode: 'Bacon' } }, projectRoot, validateDefaultBitcode);
70    expect(WarningAggregator.addWarningIOS).toHaveBeenLastCalledWith(
71      'ios.bitcode',
72      'No configuration named "Bacon". Expected one of: "Debug", "Release".'
73    );
74  });
75});
76
77function getConfigurations(project: XcodeProject) {
78  return Object.entries(project.pbxXCBuildConfigurationSection()).filter(isNotComment);
79}
80
81function setBitcodeEnabledForRoot(
82  config: { ios?: { bitcode?: boolean | string } & any },
83  projectRoot: string,
84  validate: (project: XcodeProject) => void
85) {
86  let project = getPbxproj(projectRoot);
87  project = setBitcodeWithConfig(config, { project });
88  validate(project);
89  fs.writeFileSync(project.filepath, project.writeSync());
90}
91
92function validateDefaultBitcode(project: XcodeProject) {
93  const configurations = getConfigurations(project);
94  for (const [id, configuration] of configurations) {
95    expect(configuration.buildSettings.ENABLE_BITCODE).toBe(
96      // Ensure nothing changed.
97      {
98        '13B07F941A680F5B00A75B9A': 'NO',
99        '13B07F951A680F5B00A75B9A': undefined,
100        '83CBBA201A601CBA00E9B192': undefined,
101        '83CBBA211A601CBA00E9B192': undefined,
102      }[id]
103    );
104  }
105}
106