@leonxin/meetgames 0.1.13 → 0.1.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/config/meetsdk-ios.json +1 -1
- package/dist/android/manifest.d.ts.map +1 -1
- package/dist/android/manifest.js +9 -0
- package/dist/android/manifest.js.map +1 -1
- package/dist/android/meetSdkRemoteGradle.d.ts.map +1 -1
- package/dist/android/meetSdkRemoteGradle.js +126 -6
- package/dist/android/meetSdkRemoteGradle.js.map +1 -1
- package/dist/config/meetSdkRemoteConfig.d.ts +2 -0
- package/dist/config/meetSdkRemoteConfig.d.ts.map +1 -1
- package/dist/config/meetSdkRemoteConfig.js +14 -1
- package/dist/config/meetSdkRemoteConfig.js.map +1 -1
- package/dist/ios/channelConfig.d.ts.map +1 -1
- package/dist/ios/channelConfig.js +18 -1
- package/dist/ios/channelConfig.js.map +1 -1
- package/dist/ios/infoPlist.d.ts +0 -1
- package/dist/ios/infoPlist.d.ts.map +1 -1
- package/dist/ios/infoPlist.js +0 -5
- package/dist/ios/infoPlist.js.map +1 -1
- package/dist/ios/integrate.d.ts.map +1 -1
- package/dist/ios/integrate.js +172 -97
- package/dist/ios/integrate.js.map +1 -1
- package/dist/ios/pbxprojEditor.d.ts +1 -1
- package/dist/ios/pbxprojEditor.d.ts.map +1 -1
- package/dist/ios/pbxprojEditor.js +88 -15
- package/dist/ios/pbxprojEditor.js.map +1 -1
- package/dist/ops/fileStore.d.ts.map +1 -1
- package/dist/ops/fileStore.js +6 -5
- package/dist/ops/fileStore.js.map +1 -1
- package/dist/ops/handlers.d.ts.map +1 -1
- package/dist/ops/handlers.js +35 -3
- package/dist/ops/handlers.js.map +1 -1
- package/docs/API.md +1 -1
- package/docs/INTEGRATION.md +30 -2
- package/package.json +1 -1
- package/src/android/manifest.ts +11 -0
- package/src/android/meetSdkRemoteGradle.ts +125 -7
- package/src/config/meetSdkRemoteConfig.ts +13 -1
- package/src/ios/channelConfig.ts +18 -1
- package/src/ios/infoPlist.ts +0 -7
- package/src/ios/integrate.ts +177 -96
- package/src/ios/pbxprojEditor.ts +98 -16
- package/src/ops/fileStore.ts +7 -6
- package/src/ops/handlers.ts +38 -3
- package/tests/doctor.test.ts +43 -3
- package/tests/meetSdkRemoteConfig.test.ts +3 -2
- package/tests/meetSdkRemoteGradle.test.ts +2 -2
- package/tests/pipeline.android.test.ts +64 -10
- package/tests/pipeline.ios.test.ts +134 -43
- package/tests/platformSelection.test.ts +4 -4
package/src/ops/handlers.ts
CHANGED
|
@@ -25,14 +25,49 @@ import { loadMeetSdkDefaultConfigWithLatestAndroidVersion } from "../config/meet
|
|
|
25
25
|
import { insertPermissions } from "../android/manifest.js";
|
|
26
26
|
import type { TextFileStore } from "./fileStore.js";
|
|
27
27
|
|
|
28
|
-
function ok(changed: string[], warnings: string[] = []): StepResult {
|
|
29
|
-
return { ok: true, changedFiles: changed, warnings, errors: [] };
|
|
28
|
+
function ok(changed: string[], warnings: string[] = [], logs: string[] = []): StepResult {
|
|
29
|
+
return { ok: true, changedFiles: changed, logs, warnings, errors: [] };
|
|
30
30
|
}
|
|
31
31
|
|
|
32
32
|
function fail(errors: string[]): StepResult {
|
|
33
33
|
return { ok: false, changedFiles: [], warnings: [], errors };
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
+
function androidApplicationId(content: string): string | undefined {
|
|
37
|
+
const match = content.match(/^\s*applicationId\s+(?:=+\s*)?["']([^"']*)["']/m);
|
|
38
|
+
return match?.[1];
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function androidResValues(content: string): Map<string, string> {
|
|
42
|
+
const out = new Map<string, string>();
|
|
43
|
+
const re = /resValue\s*\(\s*['"]([^'"]+)['"]\s*,\s*['"]([^'"]+)['"]\s*,\s*(['"])(.*?)\3\s*\)/g;
|
|
44
|
+
for (const match of content.matchAll(re)) {
|
|
45
|
+
out.set(match[2], `${match[1]}:${match[4]}`);
|
|
46
|
+
}
|
|
47
|
+
return out;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function androidConfigChangeLogs(before: string, after: string): string[] {
|
|
51
|
+
const logs: string[] = [];
|
|
52
|
+
const beforeAppId = androidApplicationId(before);
|
|
53
|
+
const afterAppId = androidApplicationId(after);
|
|
54
|
+
if (afterAppId !== undefined && beforeAppId !== afterAppId) {
|
|
55
|
+
logs.push(`[meetgames] Android applicationId ${beforeAppId === undefined ? "<missing>" : beforeAppId} -> ${afterAppId}`);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const beforeRes = androidResValues(before);
|
|
59
|
+
const afterRes = androidResValues(after);
|
|
60
|
+
for (const [key, next] of afterRes) {
|
|
61
|
+
const prev = beforeRes.get(key);
|
|
62
|
+
if (prev === undefined) {
|
|
63
|
+
logs.push(`[meetgames] Android resValue ${key} <missing> -> ${next}`);
|
|
64
|
+
} else if (prev !== next) {
|
|
65
|
+
logs.push(`[meetgames] Android resValue ${key} ${prev} -> ${next}`);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return logs;
|
|
69
|
+
}
|
|
70
|
+
|
|
36
71
|
function readRootBuildGradle(projectRoot: string): { rel: string; abs: string } | null {
|
|
37
72
|
const a = path.join(projectRoot, "build.gradle");
|
|
38
73
|
if (fs.existsSync(a)) return { rel: "build.gradle", abs: a };
|
|
@@ -183,7 +218,7 @@ export const opHandlers: Record<string, OpHandler> = {
|
|
|
183
218
|
|
|
184
219
|
store.write(modRel, modU.content);
|
|
185
220
|
if (modU.changed) changed.push(modRel);
|
|
186
|
-
return ok(changed, [...warnings, ...modU.warnings]);
|
|
221
|
+
return ok(changed, [...warnings, ...modU.warnings], androidConfigChangeLogs(modBefore, modU.content));
|
|
187
222
|
},
|
|
188
223
|
|
|
189
224
|
"gradle.insertRepositories": (ctx, store, args, _dry, _bc) => {
|
package/tests/doctor.test.ts
CHANGED
|
@@ -11,15 +11,55 @@ import { resolveIosSdkRootFromDirectory } from "../src/remote/sdkHomeDownload.js
|
|
|
11
11
|
|
|
12
12
|
const pkgRoot = path.resolve(__dirname, "..");
|
|
13
13
|
const androidLatestRoot = path.join(pkgRoot, "fixtures", "android-test-project", "android-latest-project");
|
|
14
|
-
const iosProjectRoot = path.join(pkgRoot, "fixtures", "ios-test-project", "
|
|
15
|
-
const iosRemoteConfigFixture = path.join(pkgRoot, "fixtures", "meetsdk-remote-config.ios-tooltest.json");
|
|
14
|
+
const iosProjectRoot = path.join(pkgRoot, "fixtures", "ios-test-project", "native-sample");
|
|
16
15
|
const hasIosProjectFixture = fs.existsSync(iosProjectRoot);
|
|
17
16
|
const fixtureIosSdkRoot = resolveIosSdkRootFromDirectory(path.join(pkgRoot, "fixtures", "ios-sdk", "topSDK-ios--V1.6.0.5"));
|
|
18
17
|
|
|
19
18
|
function copyIosProjectToTemp(): string {
|
|
20
19
|
const tmp = fs.mkdtempSync(path.join(os.tmpdir(), "meet-ios-doctor-"));
|
|
21
20
|
fs.cpSync(iosProjectRoot, tmp, { recursive: true });
|
|
22
|
-
fs.
|
|
21
|
+
fs.writeFileSync(
|
|
22
|
+
path.join(tmp, "meetsdk-remote-config.json"),
|
|
23
|
+
JSON.stringify(
|
|
24
|
+
{
|
|
25
|
+
packageName: "com.meetgames.topsdk.demo",
|
|
26
|
+
channel: "APPLE",
|
|
27
|
+
devicePlatform: "ios",
|
|
28
|
+
topsdk: {
|
|
29
|
+
appId: "mock-ios-native-sample-app-id",
|
|
30
|
+
appSecret: "mock-ios-native-sample-app-secret-do-not-use-in-production",
|
|
31
|
+
},
|
|
32
|
+
sdkModules: {
|
|
33
|
+
login: {
|
|
34
|
+
guest: {},
|
|
35
|
+
facebook: {
|
|
36
|
+
clientId: "883695101201170",
|
|
37
|
+
scheme: "fb883695101201170",
|
|
38
|
+
secret: "f840b8663b1351ddcb8f6a640cee18c6",
|
|
39
|
+
name: "top-demo",
|
|
40
|
+
openMessenger: "0",
|
|
41
|
+
},
|
|
42
|
+
google: {
|
|
43
|
+
clientId: "396842465987-8sg3ngohnl5f2r8no5etu401ruv6snql.apps.googleusercontent.com",
|
|
44
|
+
scheme: "com.googleusercontent.apps.396842465987-8sg3ngohnl5f2r8no5etu401ruv6snql",
|
|
45
|
+
},
|
|
46
|
+
apple: {},
|
|
47
|
+
},
|
|
48
|
+
payment: { googleIap: {} },
|
|
49
|
+
analytics: {
|
|
50
|
+
appsflyer: {
|
|
51
|
+
devKey: "af-dev-key",
|
|
52
|
+
appleAppId: "123456789",
|
|
53
|
+
enableDebugLog: true,
|
|
54
|
+
},
|
|
55
|
+
},
|
|
56
|
+
},
|
|
57
|
+
},
|
|
58
|
+
null,
|
|
59
|
+
2
|
|
60
|
+
),
|
|
61
|
+
"utf8"
|
|
62
|
+
);
|
|
23
63
|
return tmp;
|
|
24
64
|
}
|
|
25
65
|
|
|
@@ -14,7 +14,7 @@ import { loadBuiltInMeetSdkDefaultConfig } from "../src/config/meetSdkDefaultCon
|
|
|
14
14
|
const here = path.dirname(fileURLToPath(import.meta.url));
|
|
15
15
|
const refPath = path.join(here, "..", "fixtures", "topsdk-config-reference.json");
|
|
16
16
|
const offlineMockPath = path.join(here, "..", "fixtures", "meetsdk-remote-config.mock.json");
|
|
17
|
-
const offlineIosMockPath = path.join(here, "..", "fixtures", "meetsdk-remote-config.ios-
|
|
17
|
+
const offlineIosMockPath = path.join(here, "..", "fixtures", "meetsdk-remote-config.ios-native-sample.json");
|
|
18
18
|
const downloadShapePath = path.join(here, "..", "fixtures", "meetsdk-remote-config.download-shape.json");
|
|
19
19
|
|
|
20
20
|
const mapCtx = {
|
|
@@ -53,7 +53,7 @@ describe("meetSdkRemoteConfig", () => {
|
|
|
53
53
|
naver: { clientId: "naver-client", secret: "naver-secret", name: "Naver", scheme: "naver-scheme" },
|
|
54
54
|
apple: {},
|
|
55
55
|
},
|
|
56
|
-
payment: {},
|
|
56
|
+
payment: { appleIap: {} },
|
|
57
57
|
analytics: {
|
|
58
58
|
appsflyer: { devKey: "af-key", appleAppId: "1234567890" },
|
|
59
59
|
firebase: { firebaseUrl: "https://cdn.example/GoogleService-Info.plist", firebaseName: "GoogleService-Info.plist" },
|
|
@@ -82,6 +82,7 @@ describe("meetSdkRemoteConfig", () => {
|
|
|
82
82
|
devKey: "af-key",
|
|
83
83
|
appleAppId: "1234567890",
|
|
84
84
|
});
|
|
85
|
+
expect(parsed!.sdkModules.payment.appleIap).toMatchObject({});
|
|
85
86
|
});
|
|
86
87
|
|
|
87
88
|
it("does not treat legacy remote field names as valid config", () => {
|
|
@@ -150,7 +150,7 @@ android {
|
|
|
150
150
|
expect(modOut.content.indexOf(TOPSDK_PLUGIN_AUTO_START)).toBeLessThan(modOut.content.indexOf("dependencies"));
|
|
151
151
|
});
|
|
152
152
|
|
|
153
|
-
it("
|
|
153
|
+
it("keeps an existing apply plugin with the same id instead of duplicating or rewriting", () => {
|
|
154
154
|
const modIn = `
|
|
155
155
|
apply plugin: 'com.android.application'
|
|
156
156
|
apply plugin: 'com.google.gms.google-services'
|
|
@@ -173,7 +173,7 @@ dependencies { }
|
|
|
173
173
|
.split("\n")
|
|
174
174
|
.filter((l) => l.includes("com.google.gms.google-services"));
|
|
175
175
|
expect(pluginLines.length).toBe(1);
|
|
176
|
-
expect(modOut.content).toContain(TOPSDK_PLUGIN_AUTO_START);
|
|
176
|
+
expect(modOut.content).not.toContain(TOPSDK_PLUGIN_AUTO_START);
|
|
177
177
|
});
|
|
178
178
|
|
|
179
179
|
it("updates classpath by group:artifact instead of duplicating", () => {
|
|
@@ -40,7 +40,10 @@ function testProjectRoot(...names: string[]): string {
|
|
|
40
40
|
return path.join(androidFixtureRoot, names[0] ?? "");
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
-
async function runAndroidPipelineDryRun(
|
|
43
|
+
async function runAndroidPipelineDryRun(
|
|
44
|
+
projectRoot: string,
|
|
45
|
+
opts?: { requirePackageNameInPatch?: boolean; requireRemoteValuesInPatch?: boolean; allowEmptyPatch?: boolean }
|
|
46
|
+
) {
|
|
44
47
|
const manifest = loadManifestFile(path.join(recipeFixtureRoot, "android-default.fixture.yaml"));
|
|
45
48
|
const cfg = JSON.parse(fs.readFileSync(path.join(projectRoot, "meetsdk-remote-config.json"), "utf8")) as {
|
|
46
49
|
packageName: string;
|
|
@@ -51,20 +54,32 @@ async function runAndroidPipelineDryRun(projectRoot: string, opts?: { requirePac
|
|
|
51
54
|
expect(ctx.android?.ok).toBe(true);
|
|
52
55
|
const { report, patch, binaryCopies } = await runPipeline(ctx, manifest, { dryRun: true });
|
|
53
56
|
expect(report.errors).toEqual([]);
|
|
54
|
-
|
|
57
|
+
if (!opts?.allowEmptyPatch) {
|
|
58
|
+
expect(patch.length).toBeGreaterThan(0);
|
|
59
|
+
}
|
|
55
60
|
expect(binaryCopies).toEqual([]);
|
|
56
|
-
|
|
57
|
-
|
|
61
|
+
if (!opts?.allowEmptyPatch || patch.length > 0) {
|
|
62
|
+
expect(patch).toContain("TOPSDK REPO AUTO");
|
|
63
|
+
expect(patch).toContain("TOPSDK AUTO");
|
|
64
|
+
}
|
|
58
65
|
if (opts?.requirePackageNameInPatch !== false) {
|
|
59
66
|
expect(patch).toContain(cfg.packageName);
|
|
60
67
|
}
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
68
|
+
if (opts?.requireRemoteValuesInPatch !== false) {
|
|
69
|
+
expect(patch).toContain(cfg.topsdk.appId);
|
|
70
|
+
expect(patch).toContain(cfg.channel);
|
|
71
|
+
expect(patch).toContain("facebook_app_id");
|
|
72
|
+
expect(patch).toContain("top_app_id");
|
|
73
|
+
}
|
|
65
74
|
return { ctx, patch };
|
|
66
75
|
}
|
|
67
76
|
|
|
77
|
+
function copyAndroidLatestToTemp(): string {
|
|
78
|
+
const tmp = fs.mkdtempSync(path.join(fs.realpathSync("/tmp"), "meet-android-idempotent-"));
|
|
79
|
+
fs.cpSync(androidLatestRoot, tmp, { recursive: true });
|
|
80
|
+
return tmp;
|
|
81
|
+
}
|
|
82
|
+
|
|
68
83
|
describe("pipeline android fixture", () => {
|
|
69
84
|
beforeEach(() => {
|
|
70
85
|
stubSdkHomeVersion();
|
|
@@ -85,11 +100,50 @@ describe("pipeline android fixture", () => {
|
|
|
85
100
|
const hasPowerRaidConfig = fs.existsSync(path.join(powerRaidRoot, "meetsdk-remote-config.json"));
|
|
86
101
|
|
|
87
102
|
it.skipIf(!hasPowerRaidConfig)("dry-run on power-raid (:launcher)", async () => {
|
|
88
|
-
const { ctx, patch } = await runAndroidPipelineDryRun(powerRaidRoot, {
|
|
103
|
+
const { ctx, patch } = await runAndroidPipelineDryRun(powerRaidRoot, {
|
|
104
|
+
requirePackageNameInPatch: false,
|
|
105
|
+
requireRemoteValuesInPatch: false,
|
|
106
|
+
allowEmptyPatch: true,
|
|
107
|
+
});
|
|
89
108
|
expect(ctx.android?.ok && ctx.android.moduleName).toBe(":launcher");
|
|
90
|
-
expect(patch).toContain("launcher/build.gradle");
|
|
91
109
|
const addedLines = patch.split("\n").filter((line) => line.startsWith("+")).join("\n");
|
|
92
110
|
expect(addedLines).not.toContain("//Firebase");
|
|
93
111
|
expect(addedLines).not.toContain("//Appsflyer");
|
|
94
112
|
});
|
|
113
|
+
|
|
114
|
+
it("apply is idempotent and logs Android value updates", async () => {
|
|
115
|
+
const tmp = copyAndroidLatestToTemp();
|
|
116
|
+
try {
|
|
117
|
+
const manifest = loadManifestFile(path.join(recipeFixtureRoot, "android-default.fixture.yaml"));
|
|
118
|
+
const buildGradlePath = path.join(tmp, "app", "build.gradle");
|
|
119
|
+
const beforeGradle = fs.readFileSync(buildGradlePath, "utf8");
|
|
120
|
+
fs.writeFileSync(
|
|
121
|
+
buildGradlePath,
|
|
122
|
+
beforeGradle.replace(
|
|
123
|
+
'versionName "1.0"',
|
|
124
|
+
`versionName "1.0"
|
|
125
|
+
resValue('string', 'top_app_id', 'old-app-id')
|
|
126
|
+
resValue('string', 'facebook_app_id', 'old-facebook-id')`
|
|
127
|
+
),
|
|
128
|
+
"utf8"
|
|
129
|
+
);
|
|
130
|
+
|
|
131
|
+
const ctx = buildWorkspaceContext(tmp, pkgRoot);
|
|
132
|
+
const first = await runPipeline(ctx, manifest, { dryRun: false });
|
|
133
|
+
expect(first.report.errors).toEqual([]);
|
|
134
|
+
const logs = (first.report.logs ?? []).join("\n");
|
|
135
|
+
expect(logs).toContain("Android applicationId com.example.myapplication -> com.meet.integrate.androidsample");
|
|
136
|
+
expect(logs).toContain("Android resValue top_app_id string:old-app-id -> string:mock-topsdk-app-id");
|
|
137
|
+
expect(logs).toContain("Android resValue facebook_app_id string:old-facebook-id -> string:0000000000000000");
|
|
138
|
+
|
|
139
|
+
const second = await runPipeline(buildWorkspaceContext(tmp, pkgRoot), manifest, { dryRun: false });
|
|
140
|
+
expect(second.report.errors).toEqual([]);
|
|
141
|
+
const after = fs.readFileSync(buildGradlePath, "utf8");
|
|
142
|
+
expect((after.match(/resValue\('string', 'top_app_id'/g) ?? []).length).toBe(1);
|
|
143
|
+
expect((after.match(/resValue\('string', 'facebook_app_id'/g) ?? []).length).toBe(1);
|
|
144
|
+
expect((after.match(/applicationId "com.meet.integrate.androidsample"/g) ?? []).length).toBe(1);
|
|
145
|
+
} finally {
|
|
146
|
+
fs.rmSync(tmp, { recursive: true, force: true });
|
|
147
|
+
}
|
|
148
|
+
});
|
|
95
149
|
});
|
|
@@ -7,32 +7,19 @@ import { loadManifestFile } from "../src/config/loadManifest.js";
|
|
|
7
7
|
import { buildWorkspaceContext } from "../src/core/workspace.js";
|
|
8
8
|
import { runPipeline } from "../src/core/pipeline.js";
|
|
9
9
|
import { detectIOS } from "../src/ios/detect.js";
|
|
10
|
+
import { parsePlistXml } from "../src/ios/infoPlist.js";
|
|
10
11
|
import { resolveIosSdkRootFromDirectory } from "../src/remote/sdkHomeDownload.js";
|
|
11
12
|
|
|
12
13
|
const here = path.dirname(fileURLToPath(import.meta.url));
|
|
13
14
|
const pkgRoot = path.resolve(here, "..");
|
|
14
|
-
const iosRoot = path.join(pkgRoot, "fixtures", "ios-test-project", "
|
|
15
|
-
const nativeSampleRoot = path.join(pkgRoot, "fixtures", "ios-test-project", "native-sample");
|
|
16
|
-
const iosRemoteConfigFixture = path.join(pkgRoot, "fixtures", "meetsdk-remote-config.ios-tooltest.json");
|
|
15
|
+
const iosRoot = path.join(pkgRoot, "fixtures", "ios-test-project", "native-sample");
|
|
17
16
|
const hasIosProjectFixture = fs.existsSync(iosRoot);
|
|
18
|
-
const hasNativeSampleFixture = fs.existsSync(nativeSampleRoot);
|
|
19
17
|
const manifest = () => loadManifestFile(path.join(pkgRoot, "recipes", "ios-default.yaml"));
|
|
20
18
|
const fixtureIosSdkRoot = resolveIosSdkRootFromDirectory(path.join(pkgRoot, "fixtures", "ios-sdk", "topSDK-ios--V1.6.0.5"));
|
|
21
19
|
|
|
22
|
-
function writeOfflineRemoteConfig(projectRoot: string): void {
|
|
23
|
-
fs.copyFileSync(iosRemoteConfigFixture, path.join(projectRoot, "meetsdk-remote-config.json"));
|
|
24
|
-
}
|
|
25
|
-
|
|
26
20
|
function copyProjectToTemp(): string {
|
|
27
|
-
const tmp = fs.mkdtempSync(path.join(os.tmpdir(), "meet-ios-integrate-"));
|
|
28
|
-
fs.cpSync(iosRoot, tmp, { recursive: true });
|
|
29
|
-
writeOfflineRemoteConfig(tmp);
|
|
30
|
-
return tmp;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
function copyNativeSampleToTemp(): string {
|
|
34
21
|
const tmp = fs.mkdtempSync(path.join(os.tmpdir(), "meet-ios-native-sample-"));
|
|
35
|
-
fs.cpSync(
|
|
22
|
+
fs.cpSync(iosRoot, tmp, { recursive: true });
|
|
36
23
|
writeNativeSampleRemoteConfig(tmp);
|
|
37
24
|
return tmp;
|
|
38
25
|
}
|
|
@@ -63,7 +50,7 @@ function writeNativeSampleRemoteConfig(projectRoot: string): void {
|
|
|
63
50
|
apple: {},
|
|
64
51
|
},
|
|
65
52
|
payment: {
|
|
66
|
-
|
|
53
|
+
appleIap: {},
|
|
67
54
|
},
|
|
68
55
|
analytics: {
|
|
69
56
|
appsflyer: {
|
|
@@ -97,6 +84,14 @@ function plannedIosAsset(patch: string, binaryCopies: Array<{ relTo: string }>,
|
|
|
97
84
|
return patch.includes(name) || binaryCopies.some((c) => c.relTo.includes(name));
|
|
98
85
|
}
|
|
99
86
|
|
|
87
|
+
function occurrenceCount(content: string, needle: string): number {
|
|
88
|
+
return content.split(needle).length - 1;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function regexCount(content: string, pattern: RegExp): number {
|
|
92
|
+
return [...content.matchAll(pattern)].length;
|
|
93
|
+
}
|
|
94
|
+
|
|
100
95
|
function testIosContext(projectRoot: string) {
|
|
101
96
|
return buildWorkspaceContext(projectRoot, pkgRoot, { iosSdkRoot: fixtureIosSdkRoot });
|
|
102
97
|
}
|
|
@@ -109,29 +104,29 @@ describe("pipeline ios fixture SDK", () => {
|
|
|
109
104
|
});
|
|
110
105
|
|
|
111
106
|
describe.skipIf(!hasIosProjectFixture)("pipeline ios fixture", () => {
|
|
112
|
-
it("detects
|
|
107
|
+
it("detects native-sample xcodeproj with AppDelegate target", () => {
|
|
113
108
|
const d = detectIOS(iosRoot);
|
|
114
109
|
expect(d.ok).toBe(true);
|
|
115
110
|
if (d.ok) {
|
|
116
|
-
expect(d.targetName).toBe("
|
|
117
|
-
expect(fs.existsSync(path.join(iosRoot, "
|
|
111
|
+
expect(d.targetName).toBe("native-sample");
|
|
112
|
+
expect(fs.existsSync(path.join(iosRoot, "native-sample", "AppDelegate.m"))).toBe(true);
|
|
118
113
|
}
|
|
119
114
|
});
|
|
120
115
|
|
|
121
116
|
it("detects when project root is the xcodeproj directory itself", () => {
|
|
122
|
-
const xcodeprojRoot = path.join(iosRoot, "
|
|
117
|
+
const xcodeprojRoot = path.join(iosRoot, "native-sample.xcodeproj");
|
|
123
118
|
const d = detectIOS(xcodeprojRoot);
|
|
124
119
|
expect(d.ok).toBe(true);
|
|
125
120
|
if (d.ok) {
|
|
126
121
|
expect(d.xcodeprojPath).toBe(xcodeprojRoot);
|
|
127
|
-
expect(d.targetName).toBe("
|
|
122
|
+
expect(d.targetName).toBe("native-sample");
|
|
128
123
|
}
|
|
129
124
|
});
|
|
130
125
|
|
|
131
126
|
it("builds workspace context when project root is the xcodeproj directory", () => {
|
|
132
|
-
const ctx = testIosContext(path.join(iosRoot, "
|
|
127
|
+
const ctx = testIosContext(path.join(iosRoot, "native-sample.xcodeproj"));
|
|
133
128
|
expect(ctx.ios?.ok).toBe(true);
|
|
134
|
-
expect(ctx.ios?.ok && ctx.ios.xcodeprojPath).toBe(path.join(iosRoot, "
|
|
129
|
+
expect(ctx.ios?.ok && ctx.ios.xcodeprojPath).toBe(path.join(iosRoot, "native-sample.xcodeproj"));
|
|
135
130
|
});
|
|
136
131
|
|
|
137
132
|
it("dry-run: plans TOPCore + GuestSignin frameworks and pbxproj edits", async () => {
|
|
@@ -161,28 +156,92 @@ describe.skipIf(!hasIosProjectFixture)("pipeline ios fixture", () => {
|
|
|
161
156
|
expect(fs.existsSync(path.join(tmp, "topSDK", "TOPCore.framework"))).toBe(true);
|
|
162
157
|
expect(fs.existsSync(path.join(tmp, "topSDK", "TOPSDK.framework"))).toBe(true);
|
|
163
158
|
expect(fs.existsSync(path.join(tmp, "topSDK", "TOPGuestSigninPlugin.framework"))).toBe(true);
|
|
159
|
+
expect(fs.existsSync(path.join(tmp, "topSDK", "TOPIAPPayPlugin.framework"))).toBe(true);
|
|
160
|
+
expect(fs.readFileSync(path.join(tmp, "topSDK", "TopSDKInstall.swift"), "utf8")).toBe("");
|
|
164
161
|
expect(fs.existsSync(path.join(tmp, "topSDK", "TopSDKSource.bundle"))).toBe(true);
|
|
165
162
|
|
|
166
|
-
const pbx = fs.readFileSync(path.join(tmp, "
|
|
163
|
+
const pbx = fs.readFileSync(path.join(tmp, "native-sample.xcodeproj", "project.pbxproj"), "utf8");
|
|
167
164
|
expect(pbx).toContain("TOPCore.framework");
|
|
168
165
|
expect(pbx).toContain("TOPGuestSigninPlugin.framework");
|
|
166
|
+
expect(pbx).toContain("TOPIAPPayPlugin.framework in Frameworks");
|
|
167
|
+
expect(pbx).toContain("TOPCoreModel.xcdatamodeld in Sources");
|
|
168
|
+
expect(pbx).not.toContain("TOPCoreModel.xcdatamodeld in Resources");
|
|
169
|
+
expect(pbx).not.toContain("FBSDKLoginKit.xcframework in Resources");
|
|
170
|
+
expect(pbx).toContain("LIBRARY_SEARCH_PATHS = \"$(SRCROOT)/topSDK\"");
|
|
171
|
+
expect(pbx).toContain("SWIFT_VERSION = 5.0");
|
|
169
172
|
expect(pbx).toContain('"-ObjC"');
|
|
170
|
-
expect(pbx).toContain(
|
|
173
|
+
expect(pbx).toContain("libc++.tbd");
|
|
171
174
|
expect(pbx).not.toMatch(/\n\s+-ObjC,/);
|
|
172
|
-
expect(pbx).not.toMatch(/\n\s+-lc\+\+,/);
|
|
173
175
|
expect(pbxBuildFileRefErrors(pbx).filter((e) => /TOP|FBSDK|Google|AppsFlyer|TopSDKInstall/.test(e))).toEqual([]);
|
|
174
176
|
expect(pbx).not.toMatch(/\bundefined;/);
|
|
175
177
|
|
|
176
|
-
const delegate = fs.readFileSync(path.join(tmp, "
|
|
178
|
+
const delegate = fs.readFileSync(path.join(tmp, "native-sample", "AppDelegate.m"), "utf8");
|
|
177
179
|
expect(delegate).toContain("#import <TOPSDK/TopSDK.h>");
|
|
178
180
|
expect(delegate).toContain("TopSDK.sharedInstance");
|
|
179
181
|
expect(delegate).toContain("TOPDataSDK");
|
|
180
182
|
|
|
181
|
-
const plist = fs.readFileSync(path.join(tmp, "
|
|
183
|
+
const plist = fs.readFileSync(path.join(tmp, "native-sample", "Info.plist"), "utf8");
|
|
182
184
|
expect(plist).toContain("<key>TOPSDK</key>");
|
|
183
185
|
expect(plist).toContain("<key>APP_ID</key>");
|
|
184
|
-
expect(plist).toContain("mock-ios-
|
|
186
|
+
expect(plist).toContain("mock-ios-native-sample-app-id");
|
|
185
187
|
expect(plist).toContain("GuestSignin");
|
|
188
|
+
expect(plist).toContain("IAPPay");
|
|
189
|
+
expect(plist).not.toContain("NSAppTransportSecurity");
|
|
190
|
+
} finally {
|
|
191
|
+
fs.rmSync(tmp, { recursive: true, force: true });
|
|
192
|
+
}
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
it("apply: is idempotent and logs iOS value updates", async () => {
|
|
196
|
+
const tmp = copyProjectToTemp();
|
|
197
|
+
try {
|
|
198
|
+
const plistPath = path.join(tmp, "native-sample", "Info.plist");
|
|
199
|
+
const beforePlist = fs.readFileSync(plistPath, "utf8");
|
|
200
|
+
fs.writeFileSync(
|
|
201
|
+
plistPath,
|
|
202
|
+
beforePlist
|
|
203
|
+
.replace("<key>FacebookAppID</key>\n\t<string>883695101201170</string>", "<key>FacebookAppID</key>\n\t<string>old-facebook-app-id</string>")
|
|
204
|
+
.replace(
|
|
205
|
+
"</dict>\n</plist>",
|
|
206
|
+
"\t<key>LSApplicationQueriesSchemes</key>\n\t<array>\n\t\t<string>fbapi</string>\n\t</array>\n\t<key>CFBundleURLTypes</key>\n\t<array>\n\t\t<dict>\n\t\t\t<key>CFBundleTypeRole</key>\n\t\t\t<string>Editor</string>\n\t\t\t<key>CFBundleURLSchemes</key>\n\t\t\t<array>\n\t\t\t\t<string>fb883695101201170</string>\n\t\t\t</array>\n\t\t</dict>\n\t</array>\n</dict>\n</plist>"
|
|
207
|
+
),
|
|
208
|
+
"utf8"
|
|
209
|
+
);
|
|
210
|
+
|
|
211
|
+
const first = await runPipeline(testIosContext(tmp), manifest(), { dryRun: false });
|
|
212
|
+
expect(first.report.errors).toEqual([]);
|
|
213
|
+
const firstLogs = (first.report.logs ?? []).join("\n");
|
|
214
|
+
expect(firstLogs).toContain("SUCCESS: info.plist配置key:FacebookAppID old-facebook-app-id -> 883695101201170");
|
|
215
|
+
|
|
216
|
+
const second = await runPipeline(testIosContext(tmp), manifest(), { dryRun: false });
|
|
217
|
+
expect(second.report.errors).toEqual([]);
|
|
218
|
+
const secondLogs = (second.report.logs ?? []).join("\n");
|
|
219
|
+
expect(secondLogs).toContain("SUCCESS: info.plist配置key:FacebookAppID 未变化 value:883695101201170");
|
|
220
|
+
expect(secondLogs).toContain("SUCCESS: BuildSetting设置VALIDATE_WORKSPACE 未变化 value:YES");
|
|
221
|
+
|
|
222
|
+
const plist = fs.readFileSync(plistPath, "utf8");
|
|
223
|
+
const plistData = parsePlistXml(plist);
|
|
224
|
+
const queries = Array.isArray(plistData.LSApplicationQueriesSchemes)
|
|
225
|
+
? (plistData.LSApplicationQueriesSchemes as string[])
|
|
226
|
+
: [];
|
|
227
|
+
const urlTypes = Array.isArray(plistData.CFBundleURLTypes)
|
|
228
|
+
? (plistData.CFBundleURLTypes as Array<Record<string, unknown>>)
|
|
229
|
+
: [];
|
|
230
|
+
const urlSchemes = urlTypes.flatMap((entry) =>
|
|
231
|
+
Array.isArray(entry.CFBundleURLSchemes) ? (entry.CFBundleURLSchemes as string[]) : []
|
|
232
|
+
);
|
|
233
|
+
expect(occurrenceCount(plist, "<key>FacebookAppID</key>")).toBe(1);
|
|
234
|
+
expect(occurrenceCount(plist, "<key>FacebookClientToken</key>")).toBe(1);
|
|
235
|
+
expect(queries.filter((scheme) => scheme === "fbapi")).toHaveLength(1);
|
|
236
|
+
expect(urlSchemes.filter((scheme) => scheme === "fb883695101201170")).toHaveLength(1);
|
|
237
|
+
expect(urlSchemes.filter((scheme) => scheme === "com.googleusercontent.apps.396842465987-8sg3ngohnl5f2r8no5etu401ruv6snql")).toHaveLength(1);
|
|
238
|
+
expect(occurrenceCount(plist, "<key>TOPSDK</key>")).toBe(1);
|
|
239
|
+
|
|
240
|
+
const pbx = fs.readFileSync(path.join(tmp, "native-sample.xcodeproj", "project.pbxproj"), "utf8");
|
|
241
|
+
expect(regexCount(pbx, /\/\* TopSDKInstall\.swift in Sources \*\/ = \{isa = PBXBuildFile;/g)).toBe(1);
|
|
242
|
+
expect(regexCount(pbx, /\/\* TOPFacebookSigninPlugin\.framework in Frameworks \*\/ = \{isa = PBXBuildFile;/g)).toBe(1);
|
|
243
|
+
expect(regexCount(pbx, /\/\* TOPGoogleSigninPlugin\.framework in Frameworks \*\/ = \{isa = PBXBuildFile;/g)).toBe(1);
|
|
244
|
+
expect(occurrenceCount(pbx, "VALIDATE_WORKSPACE = YES")).toBe(2);
|
|
186
245
|
} finally {
|
|
187
246
|
fs.rmSync(tmp, { recursive: true, force: true });
|
|
188
247
|
}
|
|
@@ -204,15 +263,15 @@ describe.skipIf(!hasIosProjectFixture)("pipeline ios fixture", () => {
|
|
|
204
263
|
const { report } = await runPipeline(ctx, manifest(), { dryRun: false });
|
|
205
264
|
expect(report.errors).toEqual([]);
|
|
206
265
|
|
|
207
|
-
const entitlementsPath = path.join(tmp, "
|
|
266
|
+
const entitlementsPath = path.join(tmp, "native-sample", "native-sample.entitlements");
|
|
208
267
|
expect(fs.existsSync(entitlementsPath)).toBe(true);
|
|
209
268
|
const entitlements = fs.readFileSync(entitlementsPath, "utf8");
|
|
210
269
|
expect(entitlements).toContain("com.apple.developer.applesignin");
|
|
211
270
|
expect(entitlements).toContain("Default");
|
|
212
271
|
|
|
213
|
-
const pbx = fs.readFileSync(path.join(tmp, "
|
|
272
|
+
const pbx = fs.readFileSync(path.join(tmp, "native-sample.xcodeproj", "project.pbxproj"), "utf8");
|
|
214
273
|
expect(pbx).toContain("CODE_SIGN_ENTITLEMENTS");
|
|
215
|
-
expect(pbx).toContain("
|
|
274
|
+
expect(pbx).toContain("native-sample/native-sample.entitlements");
|
|
216
275
|
expect(pbx).toContain("TOPAppleSigninPlugin.framework");
|
|
217
276
|
} finally {
|
|
218
277
|
fs.rmSync(tmp, { recursive: true, force: true });
|
|
@@ -345,7 +404,7 @@ describe.skipIf(!hasIosProjectFixture)("pipeline ios fixture", () => {
|
|
|
345
404
|
const { report, patch, binaryCopies } = await runPipeline(ctx, manifest(), { dryRun: true });
|
|
346
405
|
|
|
347
406
|
expect(report.errors).toEqual([]);
|
|
348
|
-
expect(report.warnings.join("\n")).toContain("would download GoogleService-Info.plist to
|
|
407
|
+
expect(report.warnings.join("\n")).toContain("would download GoogleService-Info.plist to native-sample/GoogleService-Info.plist");
|
|
349
408
|
expect(plannedIosAsset(patch, binaryCopies, "TOPDataFirebasePlugin.framework")).toBe(true);
|
|
350
409
|
} finally {
|
|
351
410
|
fs.rmSync(tmp, { recursive: true, force: true });
|
|
@@ -356,7 +415,7 @@ describe.skipIf(!hasIosProjectFixture)("pipeline ios fixture", () => {
|
|
|
356
415
|
const tmp = copyProjectToTemp();
|
|
357
416
|
try {
|
|
358
417
|
fs.writeFileSync(
|
|
359
|
-
path.join(tmp, "
|
|
418
|
+
path.join(tmp, "native-sample", "AppDelegate.m"),
|
|
360
419
|
`#import "AppDelegate.h"
|
|
361
420
|
|
|
362
421
|
@implementation AppDelegate
|
|
@@ -370,7 +429,7 @@ describe.skipIf(!hasIosProjectFixture)("pipeline ios fixture", () => {
|
|
|
370
429
|
"utf8"
|
|
371
430
|
);
|
|
372
431
|
fs.writeFileSync(
|
|
373
|
-
path.join(tmp, "
|
|
432
|
+
path.join(tmp, "native-sample", "SceneDelegate.m"),
|
|
374
433
|
`#import "SceneDelegate.h"
|
|
375
434
|
|
|
376
435
|
@implementation SceneDelegate
|
|
@@ -384,7 +443,7 @@ describe.skipIf(!hasIosProjectFixture)("pipeline ios fixture", () => {
|
|
|
384
443
|
const { report } = await runPipeline(ctx, manifest(), { dryRun: false });
|
|
385
444
|
expect(report.errors).toEqual([]);
|
|
386
445
|
|
|
387
|
-
const appDelegate = fs.readFileSync(path.join(tmp, "
|
|
446
|
+
const appDelegate = fs.readFileSync(path.join(tmp, "native-sample", "AppDelegate.m"), "utf8");
|
|
388
447
|
expect(appDelegate).toContain("[TopSDK.sharedInstance application:application didFinishLaunchingWithOptions:launchOptions]");
|
|
389
448
|
expect(appDelegate).toContain("[TOPDataSDK application:application didFinishLaunchingWithOptions:launchOptions]");
|
|
390
449
|
expect(appDelegate).not.toContain("openURL:url options");
|
|
@@ -392,7 +451,7 @@ describe.skipIf(!hasIosProjectFixture)("pipeline ios fixture", () => {
|
|
|
392
451
|
expect(appDelegate).not.toContain("applicationDidEnterBackground");
|
|
393
452
|
expect(appDelegate).not.toContain("applicationWillEnterForeground");
|
|
394
453
|
|
|
395
|
-
const sceneDelegate = fs.readFileSync(path.join(tmp, "
|
|
454
|
+
const sceneDelegate = fs.readFileSync(path.join(tmp, "native-sample", "SceneDelegate.m"), "utf8");
|
|
396
455
|
expect(sceneDelegate).toContain("[TopSDK.sharedInstance scene:scene openURLContexts:URLContexts]");
|
|
397
456
|
expect(sceneDelegate).toContain("[TopSDK.sharedInstance scene:scene continueUserActivity:userActivity]");
|
|
398
457
|
expect(sceneDelegate).toContain("[TopSDK.sharedInstance sceneWillEnterForeground:scene]");
|
|
@@ -405,12 +464,12 @@ describe.skipIf(!hasIosProjectFixture)("pipeline ios fixture", () => {
|
|
|
405
464
|
it("fails when the configured Info.plist file is missing", async () => {
|
|
406
465
|
const tmp = copyProjectToTemp();
|
|
407
466
|
try {
|
|
408
|
-
fs.rmSync(path.join(tmp, "
|
|
467
|
+
fs.rmSync(path.join(tmp, "native-sample", "Info.plist"));
|
|
409
468
|
|
|
410
469
|
const ctx = testIosContext(tmp);
|
|
411
470
|
const { report } = await runPipeline(ctx, manifest(), { dryRun: true });
|
|
412
471
|
|
|
413
|
-
expect(report.errors.join("\n")).toContain("Info.plist not found for iOS target
|
|
472
|
+
expect(report.errors.join("\n")).toContain("Info.plist not found for iOS target native-sample");
|
|
414
473
|
} finally {
|
|
415
474
|
fs.rmSync(tmp, { recursive: true, force: true });
|
|
416
475
|
}
|
|
@@ -440,9 +499,9 @@ describe.skipIf(!hasIosProjectFixture)("pipeline ios fixture", () => {
|
|
|
440
499
|
});
|
|
441
500
|
});
|
|
442
501
|
|
|
443
|
-
describe.skipIf(!
|
|
502
|
+
describe.skipIf(!hasIosProjectFixture)("pipeline ios native-sample parity", () => {
|
|
444
503
|
it("apply: mirrors topsdk-tool-ios logs and integration points for native-sample", async () => {
|
|
445
|
-
const tmp =
|
|
504
|
+
const tmp = copyProjectToTemp();
|
|
446
505
|
try {
|
|
447
506
|
const ctx = testIosContext(tmp);
|
|
448
507
|
const { report } = await runPipeline(ctx, manifest(), { dryRun: false });
|
|
@@ -461,13 +520,45 @@ describe.skipIf(!hasNativeSampleFixture)("pipeline ios native-sample parity", ()
|
|
|
461
520
|
expect(logs).toContain("检测到工程存在UIWindowSceneDelegate实现类,将自动接入对应接口");
|
|
462
521
|
expect(logs).toContain("SUCCESS: info.plist配置写入完成");
|
|
463
522
|
expect(logs).toContain("!! 接入流程已全部结束 !!");
|
|
523
|
+
const orderedLogMarkers = [
|
|
524
|
+
"- 插件:GuestSignin, 类型:1,版本:1.6.0.3",
|
|
525
|
+
"SUCCESS: 插件GuestSignin接入完成",
|
|
526
|
+
"- 插件:UI, 类型:3,版本:1.6.0.3",
|
|
527
|
+
"SUCCESS: 插件UI接入完成",
|
|
528
|
+
"- 插件:IAPPay, 类型:2,版本:1.6.0.3",
|
|
529
|
+
"SUCCESS: 插件IAPPay接入完成",
|
|
530
|
+
"- 插件:AppsFlyerManager, 类型:4,版本:1.6.0.3",
|
|
531
|
+
"SUCCESS: info.plist添加配置key:NSAdvertisingAttributionReportEndpoint value:https://appsflyer-skadnetwork.com/ 完成",
|
|
532
|
+
"SUCCESS: 插件AppsFlyerManager接入完成",
|
|
533
|
+
"- 插件:FacebookSignin, 类型:1,版本:1.6.0.3",
|
|
534
|
+
"SUCCESS: 未检测到Swift代码文件,自动添加TopSDKInstall.swift完成",
|
|
535
|
+
"SUCCESS: 添加queriesSchemes:fbapi 完成",
|
|
536
|
+
"SUCCESS: 设置urlScheme:fb883695101201170完成",
|
|
537
|
+
"SUCCESS: 插件FacebookSignin接入完成",
|
|
538
|
+
"- 插件:GoogleSignin, 类型:1,版本:1.6.0.3",
|
|
539
|
+
"SUCCESS: 插件GoogleSignin接入完成",
|
|
540
|
+
"- 插件:AppleSignin, 类型:1,版本:1.6.0.3",
|
|
541
|
+
"SUCCESS: 启用SigninWithApple完成",
|
|
542
|
+
"SUCCESS: 插件AppleSignin接入完成",
|
|
543
|
+
"- 插件:TOPSDK, 类型:0,版本:1.6.0.3",
|
|
544
|
+
"检测到工程UIApplicationDelegate实现类,将自动接入对应接口",
|
|
545
|
+
"检测到工程存在UIWindowSceneDelegate实现类,将自动接入对应接口",
|
|
546
|
+
"SUCCESS: 插件TOPSDK接入完成",
|
|
547
|
+
"SUCCESS: info.plist配置写入完成",
|
|
548
|
+
];
|
|
549
|
+
let lastLogIndex = -1;
|
|
550
|
+
for (const marker of orderedLogMarkers) {
|
|
551
|
+
const index = logs.indexOf(marker);
|
|
552
|
+
expect(index, marker).toBeGreaterThan(lastLogIndex);
|
|
553
|
+
lastLogIndex = index;
|
|
554
|
+
}
|
|
464
555
|
|
|
465
556
|
expect(fs.existsSync(path.join(tmp, "topSDK", "TOPUIPlugin.framework"))).toBe(true);
|
|
466
557
|
expect(fs.existsSync(path.join(tmp, "topSDK", "TOPFacebookSigninPlugin.framework"))).toBe(true);
|
|
467
558
|
expect(fs.existsSync(path.join(tmp, "topSDK", "TOPGoogleSigninPlugin.framework"))).toBe(true);
|
|
468
559
|
expect(fs.existsSync(path.join(tmp, "topSDK", "TOPAppleSigninPlugin.framework"))).toBe(true);
|
|
469
560
|
expect(fs.existsSync(path.join(tmp, "topSDK", "TopSDKSource.bundle"))).toBe(true);
|
|
470
|
-
expect(fs.
|
|
561
|
+
expect(fs.readFileSync(path.join(tmp, "topSDK", "TopSDKInstall.swift"), "utf8")).toBe("");
|
|
471
562
|
|
|
472
563
|
const pbx = fs.readFileSync(path.join(tmp, "native-sample.xcodeproj", "project.pbxproj"), "utf8");
|
|
473
564
|
expect(pbx).toContain("TOPUIPlugin.framework");
|
|
@@ -11,7 +11,7 @@ import type { Manifest } from "../src/contracts/types.js";
|
|
|
11
11
|
|
|
12
12
|
const pkgRoot = path.resolve(__dirname, "..");
|
|
13
13
|
const androidLatestRoot = path.join(pkgRoot, "fixtures", "android-test-project", "android-latest-project");
|
|
14
|
-
const iosProjectRoot = path.join(pkgRoot, "fixtures", "ios-test-project", "
|
|
14
|
+
const iosProjectRoot = path.join(pkgRoot, "fixtures", "ios-test-project", "native-sample");
|
|
15
15
|
const hasIosProjectFixture = fs.existsSync(iosProjectRoot);
|
|
16
16
|
|
|
17
17
|
describe("platform selection", () => {
|
|
@@ -58,11 +58,11 @@ describe("platform selection", () => {
|
|
|
58
58
|
|
|
59
59
|
it.skipIf(!hasIosProjectFixture)("overrides detected iOS target name when explicitly provided", () => {
|
|
60
60
|
const ctx = buildWorkspaceContext(iosProjectRoot, pkgRoot, {
|
|
61
|
-
appTarget: "
|
|
61
|
+
appTarget: "native-sample",
|
|
62
62
|
});
|
|
63
63
|
|
|
64
|
-
expect(ctx.ios?.ok && ctx.ios.targetName).toBe("
|
|
65
|
-
expect(ctx.ios?.ok && ctx.ios.targetNames).toContain("
|
|
64
|
+
expect(ctx.ios?.ok && ctx.ios.targetName).toBe("native-sample");
|
|
65
|
+
expect(ctx.ios?.ok && ctx.ios.targetNames).toContain("native-sample");
|
|
66
66
|
});
|
|
67
67
|
|
|
68
68
|
it.skipIf(!hasIosProjectFixture)("rejects missing iOS app target names during detection", () => {
|