Skip to content

Commit bb4b373

Browse files
committed
fix: apply app icon to iOS UI test runner
1 parent 34713df commit bb4b373

12 files changed

Lines changed: 356 additions & 24 deletions

File tree

ios-runner/AgentDeviceRunner/AgentDeviceRunner.xcodeproj/project.pbxproj

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -402,8 +402,6 @@
402402
20EA2EEE2F2CFC7C001CF0EF /* Debug */ = {
403403
isa = XCBuildConfiguration;
404404
buildSettings = {
405-
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
406-
ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = NO;
407405
"CODE_SIGN_ENTITLEMENTS[sdk=macosx*]" = AgentDeviceRunnerUITests/AgentDeviceRunnerUITests.entitlements;
408406
CODE_SIGN_STYLE = Automatic;
409407
CURRENT_PROJECT_VERSION = 1;
@@ -430,8 +428,6 @@
430428
20EA2EEF2F2CFC7C001CF0EF /* Release */ = {
431429
isa = XCBuildConfiguration;
432430
buildSettings = {
433-
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
434-
ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = NO;
435431
"CODE_SIGN_ENTITLEMENTS[sdk=macosx*]" = AgentDeviceRunnerUITests/AgentDeviceRunnerUITests.entitlements;
436432
CODE_SIGN_STYLE = Automatic;
437433
CURRENT_PROJECT_VERSION = 1;
-79.6 KB
Loading
-99.6 KB
Loading

ios-runner/AgentDeviceRunner/AgentDeviceRunnerUITests/Assets.xcassets/AppIcon.appiconset/Contents.json

Lines changed: 0 additions & 14 deletions
This file was deleted.

ios-runner/AgentDeviceRunner/AgentDeviceRunnerUITests/Assets.xcassets/Contents.json

Lines changed: 0 additions & 6 deletions
This file was deleted.

scripts/build-xcuitest-apple.sh

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,4 +104,5 @@ xcodebuild build-for-testing \
104104
ENABLE_CODE_COVERAGE=NO \
105105
$SIGNING_BUILD_SETTINGS
106106

107+
node --experimental-strip-types src/platforms/ios/patch-runner-icon.ts "$DERIVED_PATH"
107108
node scripts/write-xcuitest-cache-metadata.mjs "$PLATFORM" "$DERIVED_PATH" "$DESTINATION"

scripts/write-xcuitest-cache-metadata.mjs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,9 @@ function isRunnerSourceFile(fileName, filePath) {
8585
return filePath.includes(`${path.sep}.xcodeproj${path.sep}`);
8686
}
8787
return [
88+
'.jpg',
89+
'.json',
90+
'.png',
8891
'.swift',
8992
'.plist',
9093
'.entitlements',
Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
import assert from 'node:assert/strict';
2+
import fs from 'node:fs';
3+
import os from 'node:os';
4+
import path from 'node:path';
5+
import { test } from 'vitest';
6+
7+
import { createLocalAppleToolProvider, withAppleToolProvider } from '../tool-provider.ts';
8+
import {
9+
applyXctestRunnerAppIcon,
10+
applyXctestRunnerAppIconFromDerivedPath,
11+
} from '../runner-icon.ts';
12+
13+
async function withTempDir<T>(prefix: string, fn: (root: string) => Promise<T> | T): Promise<T> {
14+
const root = fs.mkdtempSync(path.join(os.tmpdir(), prefix));
15+
try {
16+
return await fn(root);
17+
} finally {
18+
fs.rmSync(root, { recursive: true, force: true });
19+
}
20+
}
21+
22+
test('copies app icon artifacts into synthesized simulator XCTest runner app', async () => {
23+
await withTempDir('agent-device-runner-icon-', async (root) => {
24+
const productsDir = path.join(root, 'Build', 'Products', 'Debug-iphonesimulator');
25+
const sourceAppPath = path.join(productsDir, 'AgentDeviceRunner.app');
26+
const runnerAppPath = path.join(productsDir, 'AgentDeviceRunnerUITests-Runner.app');
27+
fs.mkdirSync(sourceAppPath, { recursive: true });
28+
fs.mkdirSync(runnerAppPath, { recursive: true });
29+
fs.writeFileSync(path.join(sourceAppPath, 'AppIcon60x60@2x.png'), 'icon');
30+
fs.writeFileSync(path.join(sourceAppPath, 'Assets.car'), 'catalog');
31+
fs.writeFileSync(
32+
path.join(sourceAppPath, 'Info.plist'),
33+
JSON.stringify({
34+
CFBundleIcons: {
35+
CFBundlePrimaryIcon: {
36+
CFBundleIconFiles: ['AppIcon60x60'],
37+
CFBundleIconName: 'AppIcon',
38+
},
39+
},
40+
}),
41+
);
42+
fs.writeFileSync(
43+
path.join(runnerAppPath, 'Info.plist'),
44+
JSON.stringify({ CFBundleName: 'XCTRunner' }),
45+
);
46+
47+
const calls: Array<[string, string[]]> = [];
48+
const provider = createLocalAppleToolProvider({
49+
runCommand: async (cmd, args) => {
50+
calls.push([cmd, args]);
51+
if (cmd === 'plutil') {
52+
fs.copyFileSync(args.at(-1) as string, args[args.indexOf('-o') + 1] as string);
53+
}
54+
return { exitCode: 0, stdout: '', stderr: '' };
55+
},
56+
plist: {
57+
readJson: async (plistPath) => JSON.parse(fs.readFileSync(plistPath, 'utf8')),
58+
},
59+
});
60+
61+
await withAppleToolProvider(
62+
provider,
63+
async () => await applyXctestRunnerAppIcon([sourceAppPath, runnerAppPath]),
64+
);
65+
66+
assert.equal(fs.readFileSync(path.join(runnerAppPath, 'AppIcon60x60@2x.png'), 'utf8'), 'icon');
67+
assert.equal(fs.readFileSync(path.join(runnerAppPath, 'Assets.car'), 'utf8'), 'catalog');
68+
assert.deepEqual(JSON.parse(fs.readFileSync(path.join(runnerAppPath, 'Info.plist'), 'utf8')), {
69+
CFBundleName: 'XCTRunner',
70+
CFBundleIcons: {
71+
CFBundlePrimaryIcon: {
72+
CFBundleIconFiles: ['AppIcon60x60'],
73+
CFBundleIconName: 'AppIcon',
74+
},
75+
},
76+
});
77+
assert.deepEqual(calls.at(-1), [
78+
'codesign',
79+
['--force', '--sign', '-', '--timestamp=none', '--generate-entitlement-der', runnerAppPath],
80+
]);
81+
});
82+
});
83+
84+
test('finds simulator XCTest runner app from derived data', async () => {
85+
await withTempDir('agent-device-runner-icon-', async (root) => {
86+
const productsDir = path.join(root, 'Build', 'Products', 'Debug-iphonesimulator');
87+
const sourceAppPath = path.join(productsDir, 'AgentDeviceRunner.app');
88+
const runnerAppPath = path.join(productsDir, 'AgentDeviceRunnerUITests-Runner.app');
89+
fs.mkdirSync(sourceAppPath, { recursive: true });
90+
fs.mkdirSync(runnerAppPath, { recursive: true });
91+
fs.writeFileSync(path.join(sourceAppPath, 'AppIcon60x60@2x.png'), 'icon');
92+
fs.writeFileSync(path.join(sourceAppPath, 'Info.plist'), JSON.stringify({}));
93+
fs.writeFileSync(path.join(runnerAppPath, 'Info.plist'), JSON.stringify({}));
94+
95+
const provider = createLocalAppleToolProvider({
96+
runCommand: async () => ({ exitCode: 0, stdout: '', stderr: '' }),
97+
plist: {
98+
readJson: async (plistPath) => JSON.parse(fs.readFileSync(plistPath, 'utf8')),
99+
},
100+
});
101+
102+
await withAppleToolProvider(
103+
provider,
104+
async () => await applyXctestRunnerAppIconFromDerivedPath(root),
105+
);
106+
107+
assert.equal(fs.readFileSync(path.join(runnerAppPath, 'AppIcon60x60@2x.png'), 'utf8'), 'icon');
108+
});
109+
});
110+
111+
test('does not patch device XCTest runner apps', async () => {
112+
await withTempDir('agent-device-runner-icon-', async (root) => {
113+
const productsDir = path.join(root, 'Build', 'Products', 'Debug-iphoneos');
114+
const sourceAppPath = path.join(productsDir, 'AgentDeviceRunner.app');
115+
const runnerAppPath = path.join(productsDir, 'AgentDeviceRunnerUITests-Runner.app');
116+
fs.mkdirSync(sourceAppPath, { recursive: true });
117+
fs.mkdirSync(runnerAppPath, { recursive: true });
118+
fs.writeFileSync(path.join(sourceAppPath, 'AppIcon60x60@2x.png'), 'icon');
119+
120+
const calls: Array<[string, string[]]> = [];
121+
const provider = createLocalAppleToolProvider({
122+
runCommand: async (cmd, args) => {
123+
calls.push([cmd, args]);
124+
return { exitCode: 0, stdout: '', stderr: '' };
125+
},
126+
});
127+
128+
await withAppleToolProvider(
129+
provider,
130+
async () => await applyXctestRunnerAppIcon([sourceAppPath, runnerAppPath]),
131+
);
132+
133+
assert.equal(fs.existsSync(path.join(runnerAppPath, 'AppIcon60x60@2x.png')), false);
134+
assert.deepEqual(calls, []);
135+
});
136+
});
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import { applyXctestRunnerAppIconFromDerivedPath } from './runner-icon.ts';
2+
3+
const [derivedPath] = process.argv.slice(2);
4+
5+
if (!derivedPath) {
6+
console.error('Usage: patch-runner-icon.ts <derived>');
7+
process.exit(1);
8+
}
9+
10+
await applyXctestRunnerAppIconFromDerivedPath(derivedPath);

0 commit comments

Comments
 (0)