@leonxin/meetgames 0.1.13 → 0.1.14

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.
@@ -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", "tooltest");
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(nativeSampleRoot, tmp, { recursive: true });
22
+ fs.cpSync(iosRoot, tmp, { recursive: true });
36
23
  writeNativeSampleRemoteConfig(tmp);
37
24
  return tmp;
38
25
  }
@@ -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 tooltest xcodeproj with AppDelegate target", () => {
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("tooltest");
117
- expect(fs.existsSync(path.join(iosRoot, "tooltest", "AppDelegate.m"))).toBe(true);
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, "tooltest.xcodeproj");
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("tooltest");
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, "tooltest.xcodeproj"));
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, "tooltest.xcodeproj"));
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 () => {
@@ -163,31 +158,85 @@ describe.skipIf(!hasIosProjectFixture)("pipeline ios fixture", () => {
163
158
  expect(fs.existsSync(path.join(tmp, "topSDK", "TOPGuestSigninPlugin.framework"))).toBe(true);
164
159
  expect(fs.existsSync(path.join(tmp, "topSDK", "TopSDKSource.bundle"))).toBe(true);
165
160
 
166
- const pbx = fs.readFileSync(path.join(tmp, "tooltest.xcodeproj", "project.pbxproj"), "utf8");
161
+ const pbx = fs.readFileSync(path.join(tmp, "native-sample.xcodeproj", "project.pbxproj"), "utf8");
167
162
  expect(pbx).toContain("TOPCore.framework");
168
163
  expect(pbx).toContain("TOPGuestSigninPlugin.framework");
169
164
  expect(pbx).toContain('"-ObjC"');
170
- expect(pbx).toContain('"-lc++"');
165
+ expect(pbx).toContain("libc++.tbd");
171
166
  expect(pbx).not.toMatch(/\n\s+-ObjC,/);
172
- expect(pbx).not.toMatch(/\n\s+-lc\+\+,/);
173
167
  expect(pbxBuildFileRefErrors(pbx).filter((e) => /TOP|FBSDK|Google|AppsFlyer|TopSDKInstall/.test(e))).toEqual([]);
174
168
  expect(pbx).not.toMatch(/\bundefined;/);
175
169
 
176
- const delegate = fs.readFileSync(path.join(tmp, "tooltest", "AppDelegate.m"), "utf8");
170
+ const delegate = fs.readFileSync(path.join(tmp, "native-sample", "AppDelegate.m"), "utf8");
177
171
  expect(delegate).toContain("#import <TOPSDK/TopSDK.h>");
178
172
  expect(delegate).toContain("TopSDK.sharedInstance");
179
173
  expect(delegate).toContain("TOPDataSDK");
180
174
 
181
- const plist = fs.readFileSync(path.join(tmp, "tooltest", "Info.plist"), "utf8");
175
+ const plist = fs.readFileSync(path.join(tmp, "native-sample", "Info.plist"), "utf8");
182
176
  expect(plist).toContain("<key>TOPSDK</key>");
183
177
  expect(plist).toContain("<key>APP_ID</key>");
184
- expect(plist).toContain("mock-ios-tooltest-app-id");
178
+ expect(plist).toContain("mock-ios-native-sample-app-id");
185
179
  expect(plist).toContain("GuestSignin");
186
180
  } finally {
187
181
  fs.rmSync(tmp, { recursive: true, force: true });
188
182
  }
189
183
  });
190
184
 
185
+ it("apply: is idempotent and logs iOS value updates", async () => {
186
+ const tmp = copyProjectToTemp();
187
+ try {
188
+ const plistPath = path.join(tmp, "native-sample", "Info.plist");
189
+ const beforePlist = fs.readFileSync(plistPath, "utf8");
190
+ fs.writeFileSync(
191
+ plistPath,
192
+ beforePlist
193
+ .replace("<key>FacebookAppID</key>\n\t<string>883695101201170</string>", "<key>FacebookAppID</key>\n\t<string>old-facebook-app-id</string>")
194
+ .replace(
195
+ "</dict>\n</plist>",
196
+ "\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>"
197
+ ),
198
+ "utf8"
199
+ );
200
+
201
+ const first = await runPipeline(testIosContext(tmp), manifest(), { dryRun: false });
202
+ expect(first.report.errors).toEqual([]);
203
+ const firstLogs = (first.report.logs ?? []).join("\n");
204
+ expect(firstLogs).toContain("SUCCESS: info.plist配置key:FacebookAppID old-facebook-app-id -> 883695101201170");
205
+
206
+ const second = await runPipeline(testIosContext(tmp), manifest(), { dryRun: false });
207
+ expect(second.report.errors).toEqual([]);
208
+ const secondLogs = (second.report.logs ?? []).join("\n");
209
+ expect(secondLogs).toContain("SUCCESS: info.plist配置key:FacebookAppID 未变化 value:883695101201170");
210
+ expect(secondLogs).toContain("SUCCESS: BuildSetting设置VALIDATE_WORKSPACE 未变化 value:YES");
211
+
212
+ const plist = fs.readFileSync(plistPath, "utf8");
213
+ const plistData = parsePlistXml(plist);
214
+ const queries = Array.isArray(plistData.LSApplicationQueriesSchemes)
215
+ ? (plistData.LSApplicationQueriesSchemes as string[])
216
+ : [];
217
+ const urlTypes = Array.isArray(plistData.CFBundleURLTypes)
218
+ ? (plistData.CFBundleURLTypes as Array<Record<string, unknown>>)
219
+ : [];
220
+ const urlSchemes = urlTypes.flatMap((entry) =>
221
+ Array.isArray(entry.CFBundleURLSchemes) ? (entry.CFBundleURLSchemes as string[]) : []
222
+ );
223
+ expect(occurrenceCount(plist, "<key>FacebookAppID</key>")).toBe(1);
224
+ expect(occurrenceCount(plist, "<key>FacebookClientToken</key>")).toBe(1);
225
+ expect(queries.filter((scheme) => scheme === "fbapi")).toHaveLength(1);
226
+ expect(urlSchemes.filter((scheme) => scheme === "fb883695101201170")).toHaveLength(1);
227
+ expect(urlSchemes.filter((scheme) => scheme === "com.googleusercontent.apps.396842465987-8sg3ngohnl5f2r8no5etu401ruv6snql")).toHaveLength(1);
228
+ expect(occurrenceCount(plist, "<key>TOPSDK</key>")).toBe(1);
229
+
230
+ const pbx = fs.readFileSync(path.join(tmp, "native-sample.xcodeproj", "project.pbxproj"), "utf8");
231
+ expect(regexCount(pbx, /\/\* TopSDKInstall\.swift in Sources \*\/ = \{isa = PBXBuildFile;/g)).toBe(1);
232
+ expect(regexCount(pbx, /\/\* TOPFacebookSigninPlugin\.framework in Frameworks \*\/ = \{isa = PBXBuildFile;/g)).toBe(1);
233
+ expect(regexCount(pbx, /\/\* TOPGoogleSigninPlugin\.framework in Frameworks \*\/ = \{isa = PBXBuildFile;/g)).toBe(1);
234
+ expect(occurrenceCount(pbx, "VALIDATE_WORKSPACE = YES")).toBe(2);
235
+ } finally {
236
+ fs.rmSync(tmp, { recursive: true, force: true });
237
+ }
238
+ });
239
+
191
240
  it("apply: enables Apple Sign In entitlement when AppleSignin plugin is enabled", async () => {
192
241
  const tmp = copyProjectToTemp();
193
242
  try {
@@ -204,15 +253,15 @@ describe.skipIf(!hasIosProjectFixture)("pipeline ios fixture", () => {
204
253
  const { report } = await runPipeline(ctx, manifest(), { dryRun: false });
205
254
  expect(report.errors).toEqual([]);
206
255
 
207
- const entitlementsPath = path.join(tmp, "tooltest", "tooltest.entitlements");
256
+ const entitlementsPath = path.join(tmp, "native-sample", "native-sample.entitlements");
208
257
  expect(fs.existsSync(entitlementsPath)).toBe(true);
209
258
  const entitlements = fs.readFileSync(entitlementsPath, "utf8");
210
259
  expect(entitlements).toContain("com.apple.developer.applesignin");
211
260
  expect(entitlements).toContain("Default");
212
261
 
213
- const pbx = fs.readFileSync(path.join(tmp, "tooltest.xcodeproj", "project.pbxproj"), "utf8");
262
+ const pbx = fs.readFileSync(path.join(tmp, "native-sample.xcodeproj", "project.pbxproj"), "utf8");
214
263
  expect(pbx).toContain("CODE_SIGN_ENTITLEMENTS");
215
- expect(pbx).toContain("tooltest/tooltest.entitlements");
264
+ expect(pbx).toContain("native-sample/native-sample.entitlements");
216
265
  expect(pbx).toContain("TOPAppleSigninPlugin.framework");
217
266
  } finally {
218
267
  fs.rmSync(tmp, { recursive: true, force: true });
@@ -345,7 +394,7 @@ describe.skipIf(!hasIosProjectFixture)("pipeline ios fixture", () => {
345
394
  const { report, patch, binaryCopies } = await runPipeline(ctx, manifest(), { dryRun: true });
346
395
 
347
396
  expect(report.errors).toEqual([]);
348
- expect(report.warnings.join("\n")).toContain("would download GoogleService-Info.plist to tooltest/GoogleService-Info.plist");
397
+ expect(report.warnings.join("\n")).toContain("would download GoogleService-Info.plist to native-sample/GoogleService-Info.plist");
349
398
  expect(plannedIosAsset(patch, binaryCopies, "TOPDataFirebasePlugin.framework")).toBe(true);
350
399
  } finally {
351
400
  fs.rmSync(tmp, { recursive: true, force: true });
@@ -356,7 +405,7 @@ describe.skipIf(!hasIosProjectFixture)("pipeline ios fixture", () => {
356
405
  const tmp = copyProjectToTemp();
357
406
  try {
358
407
  fs.writeFileSync(
359
- path.join(tmp, "tooltest", "AppDelegate.m"),
408
+ path.join(tmp, "native-sample", "AppDelegate.m"),
360
409
  `#import "AppDelegate.h"
361
410
 
362
411
  @implementation AppDelegate
@@ -370,7 +419,7 @@ describe.skipIf(!hasIosProjectFixture)("pipeline ios fixture", () => {
370
419
  "utf8"
371
420
  );
372
421
  fs.writeFileSync(
373
- path.join(tmp, "tooltest", "SceneDelegate.m"),
422
+ path.join(tmp, "native-sample", "SceneDelegate.m"),
374
423
  `#import "SceneDelegate.h"
375
424
 
376
425
  @implementation SceneDelegate
@@ -384,7 +433,7 @@ describe.skipIf(!hasIosProjectFixture)("pipeline ios fixture", () => {
384
433
  const { report } = await runPipeline(ctx, manifest(), { dryRun: false });
385
434
  expect(report.errors).toEqual([]);
386
435
 
387
- const appDelegate = fs.readFileSync(path.join(tmp, "tooltest", "AppDelegate.m"), "utf8");
436
+ const appDelegate = fs.readFileSync(path.join(tmp, "native-sample", "AppDelegate.m"), "utf8");
388
437
  expect(appDelegate).toContain("[TopSDK.sharedInstance application:application didFinishLaunchingWithOptions:launchOptions]");
389
438
  expect(appDelegate).toContain("[TOPDataSDK application:application didFinishLaunchingWithOptions:launchOptions]");
390
439
  expect(appDelegate).not.toContain("openURL:url options");
@@ -392,7 +441,7 @@ describe.skipIf(!hasIosProjectFixture)("pipeline ios fixture", () => {
392
441
  expect(appDelegate).not.toContain("applicationDidEnterBackground");
393
442
  expect(appDelegate).not.toContain("applicationWillEnterForeground");
394
443
 
395
- const sceneDelegate = fs.readFileSync(path.join(tmp, "tooltest", "SceneDelegate.m"), "utf8");
444
+ const sceneDelegate = fs.readFileSync(path.join(tmp, "native-sample", "SceneDelegate.m"), "utf8");
396
445
  expect(sceneDelegate).toContain("[TopSDK.sharedInstance scene:scene openURLContexts:URLContexts]");
397
446
  expect(sceneDelegate).toContain("[TopSDK.sharedInstance scene:scene continueUserActivity:userActivity]");
398
447
  expect(sceneDelegate).toContain("[TopSDK.sharedInstance sceneWillEnterForeground:scene]");
@@ -405,12 +454,12 @@ describe.skipIf(!hasIosProjectFixture)("pipeline ios fixture", () => {
405
454
  it("fails when the configured Info.plist file is missing", async () => {
406
455
  const tmp = copyProjectToTemp();
407
456
  try {
408
- fs.rmSync(path.join(tmp, "tooltest", "Info.plist"));
457
+ fs.rmSync(path.join(tmp, "native-sample", "Info.plist"));
409
458
 
410
459
  const ctx = testIosContext(tmp);
411
460
  const { report } = await runPipeline(ctx, manifest(), { dryRun: true });
412
461
 
413
- expect(report.errors.join("\n")).toContain("Info.plist not found for iOS target tooltest");
462
+ expect(report.errors.join("\n")).toContain("Info.plist not found for iOS target native-sample");
414
463
  } finally {
415
464
  fs.rmSync(tmp, { recursive: true, force: true });
416
465
  }
@@ -440,9 +489,9 @@ describe.skipIf(!hasIosProjectFixture)("pipeline ios fixture", () => {
440
489
  });
441
490
  });
442
491
 
443
- describe.skipIf(!hasNativeSampleFixture)("pipeline ios native-sample parity", () => {
492
+ describe.skipIf(!hasIosProjectFixture)("pipeline ios native-sample parity", () => {
444
493
  it("apply: mirrors topsdk-tool-ios logs and integration points for native-sample", async () => {
445
- const tmp = copyNativeSampleToTemp();
494
+ const tmp = copyProjectToTemp();
446
495
  try {
447
496
  const ctx = testIosContext(tmp);
448
497
  const { report } = await runPipeline(ctx, manifest(), { dryRun: false });
@@ -461,6 +510,38 @@ describe.skipIf(!hasNativeSampleFixture)("pipeline ios native-sample parity", ()
461
510
  expect(logs).toContain("检测到工程存在UIWindowSceneDelegate实现类,将自动接入对应接口");
462
511
  expect(logs).toContain("SUCCESS: info.plist配置写入完成");
463
512
  expect(logs).toContain("!! 接入流程已全部结束 !!");
513
+ const orderedLogMarkers = [
514
+ "- 插件:GuestSignin, 类型:1,版本:1.6.0.3",
515
+ "SUCCESS: 插件GuestSignin接入完成",
516
+ "- 插件:UI, 类型:3,版本:1.6.0.3",
517
+ "SUCCESS: 插件UI接入完成",
518
+ "- 插件:IAPPay, 类型:2,版本:1.6.0.3",
519
+ "SUCCESS: 插件IAPPay接入完成",
520
+ "- 插件:AppsFlyerManager, 类型:4,版本:1.6.0.3",
521
+ "SUCCESS: info.plist添加配置key:NSAdvertisingAttributionReportEndpoint value:https://appsflyer-skadnetwork.com/ 完成",
522
+ "SUCCESS: 插件AppsFlyerManager接入完成",
523
+ "- 插件:FacebookSignin, 类型:1,版本:1.6.0.3",
524
+ "SUCCESS: 未检测到Swift代码文件,自动添加TopSDKInstall.swift完成",
525
+ "SUCCESS: 添加queriesSchemes:fbapi 完成",
526
+ "SUCCESS: 设置urlScheme:fb883695101201170完成",
527
+ "SUCCESS: 插件FacebookSignin接入完成",
528
+ "- 插件:GoogleSignin, 类型:1,版本:1.6.0.3",
529
+ "SUCCESS: 插件GoogleSignin接入完成",
530
+ "- 插件:AppleSignin, 类型:1,版本:1.6.0.3",
531
+ "SUCCESS: 启用SigninWithApple完成",
532
+ "SUCCESS: 插件AppleSignin接入完成",
533
+ "- 插件:TOPSDK, 类型:0,版本:1.6.0.3",
534
+ "检测到工程UIApplicationDelegate实现类,将自动接入对应接口",
535
+ "检测到工程存在UIWindowSceneDelegate实现类,将自动接入对应接口",
536
+ "SUCCESS: 插件TOPSDK接入完成",
537
+ "SUCCESS: info.plist配置写入完成",
538
+ ];
539
+ let lastLogIndex = -1;
540
+ for (const marker of orderedLogMarkers) {
541
+ const index = logs.indexOf(marker);
542
+ expect(index, marker).toBeGreaterThan(lastLogIndex);
543
+ lastLogIndex = index;
544
+ }
464
545
 
465
546
  expect(fs.existsSync(path.join(tmp, "topSDK", "TOPUIPlugin.framework"))).toBe(true);
466
547
  expect(fs.existsSync(path.join(tmp, "topSDK", "TOPFacebookSigninPlugin.framework"))).toBe(true);
@@ -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", "tooltest");
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: "tooltest",
61
+ appTarget: "native-sample",
62
62
  });
63
63
 
64
- expect(ctx.ios?.ok && ctx.ios.targetName).toBe("tooltest");
65
- expect(ctx.ios?.ok && ctx.ios.targetNames).toContain("tooltest");
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", () => {