@leonxin/meetgames 0.1.8 → 0.1.11
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/.agents/skills/meet-sdk-regression/SKILL.md +93 -0
- package/.cursor/mcp.example.json +16 -0
- package/.cursor/mcp.json +11 -0
- package/.cursor/skills/meetgames-mcp/SKILL.md +48 -0
- package/.vite/vitest/results.json +1 -0
- package/README.md +31 -8
- package/dist/aab-converter/aab-entry.d.ts +3 -0
- package/dist/aab-converter/aab-entry.d.ts.map +1 -0
- package/dist/aab-converter/aab-entry.js +49 -0
- package/dist/aab-converter/aab-entry.js.map +1 -0
- package/dist/aab-converter/apksExtractor.d.ts +2 -0
- package/dist/aab-converter/apksExtractor.d.ts.map +1 -0
- package/dist/aab-converter/apksExtractor.js +108 -0
- package/dist/aab-converter/apksExtractor.js.map +1 -0
- package/dist/aab-converter/bundletoolRunner.d.ts +15 -0
- package/dist/aab-converter/bundletoolRunner.d.ts.map +1 -0
- package/dist/aab-converter/bundletoolRunner.js +46 -0
- package/dist/aab-converter/bundletoolRunner.js.map +1 -0
- package/dist/aab-converter/cliArgs.d.ts +27 -0
- package/dist/aab-converter/cliArgs.d.ts.map +1 -0
- package/dist/aab-converter/cliArgs.js +170 -0
- package/dist/aab-converter/cliArgs.js.map +1 -0
- package/dist/aab-converter/convertAabToApk.d.ts +7 -0
- package/dist/aab-converter/convertAabToApk.d.ts.map +1 -0
- package/dist/aab-converter/convertAabToApk.js +69 -0
- package/dist/aab-converter/convertAabToApk.js.map +1 -0
- package/dist/aab-converter/resourcePaths.d.ts +4 -0
- package/dist/aab-converter/resourcePaths.d.ts.map +1 -0
- package/dist/aab-converter/resourcePaths.js +42 -0
- package/dist/aab-converter/resourcePaths.js.map +1 -0
- package/dist/aab-converter/signingOptions.d.ts +9 -0
- package/dist/aab-converter/signingOptions.d.ts.map +1 -0
- package/dist/aab-converter/signingOptions.js +21 -0
- package/dist/aab-converter/signingOptions.js.map +1 -0
- package/dist/aab-converter/types.d.ts +24 -0
- package/dist/aab-converter/types.d.ts.map +1 -0
- package/dist/aab-converter/types.js +2 -0
- package/dist/aab-converter/types.js.map +1 -0
- package/dist/android/adapter.d.ts.map +1 -1
- package/dist/android/adapter.js +2 -2
- package/dist/android/adapter.js.map +1 -1
- package/dist/android/detect.d.ts +2 -2
- package/dist/android/detect.d.ts.map +1 -1
- package/dist/android/detect.js +36 -8
- package/dist/android/detect.js.map +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +157 -31
- package/dist/cli.js.map +1 -1
- package/dist/config/meetSdkDefaultConfig.d.ts +19 -2
- package/dist/config/meetSdkDefaultConfig.d.ts.map +1 -1
- package/dist/config/meetSdkDefaultConfig.js +67 -5
- package/dist/config/meetSdkDefaultConfig.js.map +1 -1
- package/dist/config/meetSdkIosConfig.d.ts +21 -0
- package/dist/config/meetSdkIosConfig.d.ts.map +1 -0
- package/dist/config/meetSdkIosConfig.js +66 -0
- package/dist/config/meetSdkIosConfig.js.map +1 -0
- package/dist/config/meetSdkRemoteConfig.d.ts +18 -1
- package/dist/config/meetSdkRemoteConfig.d.ts.map +1 -1
- package/dist/config/meetSdkRemoteConfig.js +61 -25
- package/dist/config/meetSdkRemoteConfig.js.map +1 -1
- package/dist/contracts/types.d.ts +19 -6
- package/dist/contracts/types.d.ts.map +1 -1
- package/dist/core/doctor.d.ts +17 -0
- package/dist/core/doctor.d.ts.map +1 -0
- package/dist/core/doctor.js +444 -0
- package/dist/core/doctor.js.map +1 -0
- package/dist/core/pipeline.d.ts.map +1 -1
- package/dist/core/pipeline.js +0 -15
- package/dist/core/pipeline.js.map +1 -1
- package/dist/core/platform.d.ts +12 -0
- package/dist/core/platform.d.ts.map +1 -0
- package/dist/core/platform.js +40 -0
- package/dist/core/platform.js.map +1 -0
- package/dist/core/reporter.js +1 -1
- package/dist/core/reporter.js.map +1 -1
- package/dist/core/workspace.d.ts +2 -2
- package/dist/core/workspace.d.ts.map +1 -1
- package/dist/core/workspace.js +4 -5
- package/dist/core/workspace.js.map +1 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -1
- package/dist/index.js.map +1 -1
- package/dist/ios/channelConfig.d.ts +1 -0
- package/dist/ios/channelConfig.d.ts.map +1 -1
- package/dist/ios/channelConfig.js +82 -0
- package/dist/ios/channelConfig.js.map +1 -1
- package/dist/ios/codeUtils.d.ts +1 -0
- package/dist/ios/codeUtils.d.ts.map +1 -1
- package/dist/ios/codeUtils.js +11 -2
- package/dist/ios/codeUtils.js.map +1 -1
- package/dist/ios/detect.d.ts +2 -2
- package/dist/ios/detect.d.ts.map +1 -1
- package/dist/ios/detect.js +49 -10
- package/dist/ios/detect.js.map +1 -1
- package/dist/ios/entitlements.d.ts +4 -0
- package/dist/ios/entitlements.d.ts.map +1 -0
- package/dist/ios/entitlements.js +53 -0
- package/dist/ios/entitlements.js.map +1 -0
- package/dist/ios/fileManager.d.ts.map +1 -1
- package/dist/ios/fileManager.js +3 -2
- package/dist/ios/fileManager.js.map +1 -1
- package/dist/ios/infoPlist.d.ts +1 -1
- package/dist/ios/infoPlist.d.ts.map +1 -1
- package/dist/ios/infoPlist.js.map +1 -1
- package/dist/ios/integrate.d.ts.map +1 -1
- package/dist/ios/integrate.js +211 -36
- package/dist/ios/integrate.js.map +1 -1
- package/dist/ios/pbxprojEditor.d.ts +2 -0
- package/dist/ios/pbxprojEditor.d.ts.map +1 -1
- package/dist/ios/pbxprojEditor.js +179 -1
- package/dist/ios/pbxprojEditor.js.map +1 -1
- package/dist/ios/pluginConfig.d.ts +1 -0
- package/dist/ios/pluginConfig.d.ts.map +1 -1
- package/dist/ios/pluginConfig.js +36 -4
- package/dist/ios/pluginConfig.js.map +1 -1
- package/dist/ios/sdkBundle.d.ts +1 -1
- package/dist/ios/sdkBundle.d.ts.map +1 -1
- package/dist/ios/sdkBundle.js +7 -5
- package/dist/ios/sdkBundle.js.map +1 -1
- package/dist/ios/template.d.ts +1 -0
- package/dist/ios/template.d.ts.map +1 -1
- package/dist/ios/template.js +14 -1
- package/dist/ios/template.js.map +1 -1
- package/dist/ios/types.d.ts +2 -2
- package/dist/ios/types.d.ts.map +1 -1
- package/dist/mcp/server.d.ts.map +1 -1
- package/dist/mcp/server.js +14 -13
- package/dist/mcp/server.js.map +1 -1
- package/dist/mcp/service.d.ts +8 -6
- package/dist/mcp/service.d.ts.map +1 -1
- package/dist/mcp/service.js +34 -14
- package/dist/mcp/service.js.map +1 -1
- package/dist/ops/handlers.d.ts.map +1 -1
- package/dist/ops/handlers.js +10 -4
- package/dist/ops/handlers.js.map +1 -1
- package/dist/remote/sdkHomeDownload.d.ts +65 -0
- package/dist/remote/sdkHomeDownload.d.ts.map +1 -0
- package/dist/remote/sdkHomeDownload.js +208 -0
- package/dist/remote/sdkHomeDownload.js.map +1 -0
- package/dist/remote/topsdkDownloadSdkConfig.d.ts.map +1 -1
- package/dist/remote/topsdkDownloadSdkConfig.js +11 -1
- package/dist/remote/topsdkDownloadSdkConfig.js.map +1 -1
- package/dist/shared/errors.d.ts +7 -0
- package/dist/shared/errors.d.ts.map +1 -0
- package/dist/shared/errors.js +16 -0
- package/dist/shared/errors.js.map +1 -0
- package/dist/shared/fileUtils.d.ts +5 -0
- package/dist/shared/fileUtils.d.ts.map +1 -0
- package/dist/shared/fileUtils.js +35 -0
- package/dist/shared/fileUtils.js.map +1 -0
- package/dist/shared/logger.d.ts +10 -0
- package/dist/shared/logger.d.ts.map +1 -0
- package/dist/shared/logger.js +37 -0
- package/dist/shared/logger.js.map +1 -0
- package/dist/shared/pathUtils.d.ts +4 -0
- package/dist/shared/pathUtils.d.ts.map +1 -0
- package/dist/shared/pathUtils.js +22 -0
- package/dist/shared/pathUtils.js.map +1 -0
- package/dist/shared/processRunner.d.ts +12 -0
- package/dist/shared/processRunner.d.ts.map +1 -0
- package/dist/shared/processRunner.js +31 -0
- package/dist/shared/processRunner.js.map +1 -0
- package/docs/AAB_CONVERTER_CLI_PLAN.md +392 -0
- package/docs/API.md +246 -32
- package/docs/CLI.md +292 -0
- package/docs/INTEGRATION.md +116 -0
- package/docs/MCP.md +86 -0
- package/docs/README.md +19 -10
- package/docs/{api → archive/api}/downloadSDKConfig.md +8 -6
- package/docs/{api → archive/api}/getChannelConfig-meetgames.md +1 -1
- package/docs/archive/ios-migration.md +2139 -0
- package/docs/{ → archive/product/}/346/212/200/346/234/257/346/226/271/346/241/210/350/260/203/347/240/224.md +7 -7
- package/docs/{ → archive/product/}/351/234/200/346/261/202/346/226/207/346/241/243.md +15 -14
- package/logs/convert-20260622-155037.log +5 -0
- package/logs/convert-20260622-155226.log +6 -0
- package/meetsdk-android.json +2 -1
- package/meetsdk-ios.json +15 -0
- package/package.json +10 -36
- package/scripts/package-aab-cli-win.mjs +193 -0
- package/src/aab-converter/aab-entry.ts +48 -0
- package/src/aab-converter/apksExtractor.ts +119 -0
- package/src/aab-converter/bundletoolRunner.ts +63 -0
- package/src/aab-converter/cliArgs.ts +194 -0
- package/src/aab-converter/convertAabToApk.ts +81 -0
- package/src/aab-converter/resourcePaths.ts +43 -0
- package/src/aab-converter/signingOptions.ts +29 -0
- package/src/aab-converter/types.ts +26 -0
- package/src/android/adapter.ts +9 -0
- package/src/android/assembleIntegrationJson.ts +33 -0
- package/src/android/detect.ts +132 -0
- package/src/android/downloadGoogleServicesJson.ts +56 -0
- package/src/android/gradle.ts +116 -0
- package/src/android/manifest.ts +50 -0
- package/src/android/meetSdkRemoteGradle.ts +837 -0
- package/src/cli.ts +488 -0
- package/src/config/fetchConfigWrite.ts +30 -0
- package/src/config/loadAndroidIntegration.ts +41 -0
- package/src/config/loadManifest.ts +40 -0
- package/src/config/meetSdkDefaultConfig.ts +99 -0
- package/src/config/meetSdkIosConfig.ts +87 -0
- package/src/config/meetSdkRemoteConfig.ts +1211 -0
- package/src/config/topsdkFeatureModules.ts +92 -0
- package/src/contracts/types.ts +121 -0
- package/src/core/doctor.ts +485 -0
- package/src/core/patch.ts +64 -0
- package/src/core/pipeline.ts +107 -0
- package/src/core/platform.ts +47 -0
- package/src/core/previewPatches.ts +23 -0
- package/src/core/reporter.ts +24 -0
- package/src/core/workspace.ts +29 -0
- package/src/entry.ts +7 -0
- package/src/index.ts +133 -0
- package/src/ios/channelConfig.ts +128 -0
- package/src/ios/codeUtils.ts +160 -0
- package/src/ios/detect.ts +105 -0
- package/src/ios/entitlements.ts +61 -0
- package/src/ios/fileManager.ts +48 -0
- package/src/ios/infoPlist.ts +55 -0
- package/src/ios/integrate.ts +516 -0
- package/src/ios/pbxprojEditor.ts +383 -0
- package/src/ios/pluginConfig.ts +97 -0
- package/src/ios/reserved.ts +8 -0
- package/src/ios/sdkBundle.ts +36 -0
- package/src/ios/template.ts +36 -0
- package/src/ios/types.ts +65 -0
- package/src/mcp/server.ts +170 -0
- package/src/mcp/service.ts +222 -0
- package/src/mcp-entry.ts +7 -0
- package/src/ops/fileStore.ts +56 -0
- package/src/ops/handlers.ts +304 -0
- package/src/remote/fetchJson.ts +22 -0
- package/src/remote/sdkHomeDownload.ts +274 -0
- package/src/remote/topsdkDownloadSdkConfig.ts +93 -0
- package/src/remote/topsdkGetSdkConfig.ts +122 -0
- package/src/remote/topsdkSign.ts +10 -0
- package/src/shared/errors.ts +16 -0
- package/src/shared/fileUtils.ts +41 -0
- package/src/shared/logger.ts +49 -0
- package/src/shared/pathUtils.ts +24 -0
- package/src/shared/processRunner.ts +43 -0
- package/test-projects/README.md +51 -0
- package/test-projects/_preview/pipeline.patch +281 -0
- package/tests/aab-converter.test.ts +213 -0
- package/tests/assemble.test.ts +12 -0
- package/tests/doctor.test.ts +89 -0
- package/tests/downloadGoogleServicesJson.test.ts +47 -0
- package/tests/fetch-remote.test.ts +23 -0
- package/tests/fetchConfigOverrides.test.ts +28 -0
- package/tests/fetchConfigWrite.test.ts +54 -0
- package/tests/gradle.test.ts +33 -0
- package/tests/integration-json.test.ts +29 -0
- package/tests/ios.codeUtils.test.ts +23 -0
- package/tests/ios.sdkBundle.test.ts +16 -0
- package/tests/loadManifest.test.ts +15 -0
- package/tests/manifest-xml.test.ts +30 -0
- package/tests/mcp.e2e.ts +217 -0
- package/tests/mcp.service.test.ts +53 -0
- package/tests/meetSdkRemoteConfig.test.ts +456 -0
- package/tests/meetSdkRemoteGradle.test.ts +414 -0
- package/tests/pipeline.android.test.ts +96 -0
- package/tests/pipeline.integration-json.test.ts +58 -0
- package/tests/pipeline.ios.test.ts +385 -0
- package/tests/pipeline.preview.patch.test.ts +85 -0
- package/tests/platformSelection.test.ts +77 -0
- package/tests/sdkHomeDownload.test.ts +124 -0
- package/tests/sdkVersionConfig.test.ts +130 -0
- package/tests/test-projects-hosts.test.ts +78 -0
- package/tests/topsdk.test.ts +53 -0
- package/tests/topsdkDownloadSdkConfig.test.ts +81 -0
- package/tests/topsdkFeatureModules.test.ts +116 -0
- package/tsconfig.json +19 -0
- package/vitest.config.ts +9 -0
- package/vitest.mcp.config.ts +11 -0
- package/bundled/android/sample.txt +0 -1
- package/docs/ANDROID.md +0 -133
- package/docs/CURSOR-MCP-SETUP.md +0 -72
- package/docs/MCP-SKILL.md +0 -63
- package/fixtures/api-samples/getChannelConfig-meetgames.sample.json +0 -123
- package/fixtures/meetsdk-remote-config.download-shape.json +0 -20
- package/fixtures/meetsdk-remote-config.mock.json +0 -69
- package/fixtures/recipes/android-default.fixture.yaml +0 -15
- package/fixtures/recipes/android-integration.fixture.json +0 -29
- package/fixtures/topsdk-config-reference.json +0 -39
- /package/docs/{api → archive/api}/getSDKConfig.md +0 -0
|
@@ -0,0 +1,385 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import os from "node:os";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { fileURLToPath } from "node:url";
|
|
5
|
+
import { describe, expect, it } from "vitest";
|
|
6
|
+
import { loadManifestFile } from "../src/config/loadManifest.js";
|
|
7
|
+
import { buildWorkspaceContext } from "../src/core/workspace.js";
|
|
8
|
+
import { runPipeline } from "../src/core/pipeline.js";
|
|
9
|
+
import { detectIOS } from "../src/ios/detect.js";
|
|
10
|
+
import { resolveIosSdkRoot } from "../src/ios/sdkBundle.js";
|
|
11
|
+
|
|
12
|
+
const here = path.dirname(fileURLToPath(import.meta.url));
|
|
13
|
+
const pkgRoot = path.resolve(here, "..");
|
|
14
|
+
const iosRoot = path.join(pkgRoot, "fixtures", "ios-test-project", "tooltest");
|
|
15
|
+
const iosRemoteConfigFixture = path.join(pkgRoot, "fixtures", "meetsdk-remote-config.ios-tooltest.json");
|
|
16
|
+
const hasIosProjectFixture = fs.existsSync(iosRoot);
|
|
17
|
+
const manifest = () => loadManifestFile(path.join(pkgRoot, "recipes", "ios-default.yaml"));
|
|
18
|
+
|
|
19
|
+
function writeOfflineRemoteConfig(projectRoot: string): void {
|
|
20
|
+
fs.copyFileSync(iosRemoteConfigFixture, path.join(projectRoot, "meetsdk-remote-config.json"));
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function copyProjectToTemp(): string {
|
|
24
|
+
const tmp = fs.mkdtempSync(path.join(os.tmpdir(), "meet-ios-integrate-"));
|
|
25
|
+
fs.cpSync(iosRoot, tmp, { recursive: true });
|
|
26
|
+
writeOfflineRemoteConfig(tmp);
|
|
27
|
+
return tmp;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function pbxBuildFileRefErrors(pbx: string): string[] {
|
|
31
|
+
const fileRefs = new Set<string>();
|
|
32
|
+
for (const match of pbx.matchAll(/^\s*([A-F0-9]{24}) \/\* .* \*\/ = \{isa = PBXFileReference;/gm)) {
|
|
33
|
+
fileRefs.add(match[1]);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const errors: string[] = [];
|
|
37
|
+
for (const match of pbx.matchAll(
|
|
38
|
+
/^\s*([A-F0-9]{24}) \/\* (.*?) \*\/ = \{isa = PBXBuildFile; fileRef = ([A-F0-9]{24}) /gm
|
|
39
|
+
)) {
|
|
40
|
+
const [, buildFileId, label, fileRefId] = match;
|
|
41
|
+
if (!fileRefs.has(fileRefId)) errors.push(`${label}: ${buildFileId} -> ${fileRefId}`);
|
|
42
|
+
}
|
|
43
|
+
return errors;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function plannedIosAsset(patch: string, binaryCopies: Array<{ relTo: string }>, name: string): boolean {
|
|
47
|
+
return patch.includes(name) || binaryCopies.some((c) => c.relTo.includes(name));
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
describe("pipeline ios bundled SDK", () => {
|
|
51
|
+
it("resolves bundled TopSDK iOS V1.6 package", () => {
|
|
52
|
+
const sdkRoot = resolveIosSdkRoot(pkgRoot);
|
|
53
|
+
expect(fs.existsSync(path.join(sdkRoot, "sdk", "topsdkConfig.t.json"))).toBe(true);
|
|
54
|
+
expect(fs.existsSync(path.join(sdkRoot, "plugins", "GuestSignin", "config.t.json"))).toBe(true);
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
describe.skipIf(!hasIosProjectFixture)("pipeline ios fixture", () => {
|
|
59
|
+
it("detects tooltest xcodeproj with AppDelegate target", () => {
|
|
60
|
+
const d = detectIOS(iosRoot);
|
|
61
|
+
expect(d.ok).toBe(true);
|
|
62
|
+
if (d.ok) {
|
|
63
|
+
expect(d.targetName).toBe("tooltest");
|
|
64
|
+
expect(fs.existsSync(path.join(iosRoot, "tooltest", "AppDelegate.m"))).toBe(true);
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
it("detects when project root is the xcodeproj directory itself", () => {
|
|
69
|
+
const xcodeprojRoot = path.join(iosRoot, "tooltest.xcodeproj");
|
|
70
|
+
const d = detectIOS(xcodeprojRoot);
|
|
71
|
+
expect(d.ok).toBe(true);
|
|
72
|
+
if (d.ok) {
|
|
73
|
+
expect(d.xcodeprojPath).toBe(xcodeprojRoot);
|
|
74
|
+
expect(d.targetName).toBe("tooltest");
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
it("builds workspace context when project root is the xcodeproj directory", () => {
|
|
79
|
+
const ctx = buildWorkspaceContext(path.join(iosRoot, "tooltest.xcodeproj"), pkgRoot);
|
|
80
|
+
expect(ctx.ios?.ok).toBe(true);
|
|
81
|
+
expect(ctx.ios?.ok && ctx.ios.xcodeprojPath).toBe(path.join(iosRoot, "tooltest.xcodeproj"));
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
it("dry-run: plans TOPCore + GuestSignin frameworks and pbxproj edits", async () => {
|
|
85
|
+
const tmp = copyProjectToTemp();
|
|
86
|
+
try {
|
|
87
|
+
const ctx = buildWorkspaceContext(tmp, pkgRoot);
|
|
88
|
+
expect(ctx.ios?.ok).toBe(true);
|
|
89
|
+
const { report, patch, binaryCopies } = await runPipeline(ctx, manifest(), { dryRun: true });
|
|
90
|
+
expect(report.errors).toEqual([]);
|
|
91
|
+
expect(plannedIosAsset(patch, binaryCopies, "TOPCore.framework")).toBe(true);
|
|
92
|
+
expect(plannedIosAsset(patch, binaryCopies, "TOPGuestSigninPlugin.framework")).toBe(true);
|
|
93
|
+
expect(binaryCopies.some((c) => c.relTo.includes("topSDK/TOPCore.framework"))).toBe(true);
|
|
94
|
+
expect(binaryCopies.some((c) => c.relTo.includes("topSDK/TOPGuestSigninPlugin.framework"))).toBe(true);
|
|
95
|
+
|
|
96
|
+
} finally {
|
|
97
|
+
fs.rmSync(tmp, { recursive: true, force: true });
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
it("apply: copies SDK, updates pbxproj, injects AppDelegate", async () => {
|
|
102
|
+
const tmp = copyProjectToTemp();
|
|
103
|
+
try {
|
|
104
|
+
const ctx = buildWorkspaceContext(tmp, pkgRoot);
|
|
105
|
+
const { report } = await runPipeline(ctx, manifest(), { dryRun: false });
|
|
106
|
+
expect(report.errors).toEqual([]);
|
|
107
|
+
|
|
108
|
+
expect(fs.existsSync(path.join(tmp, "topSDK", "TOPCore.framework"))).toBe(true);
|
|
109
|
+
expect(fs.existsSync(path.join(tmp, "topSDK", "TOPSDK.framework"))).toBe(true);
|
|
110
|
+
expect(fs.existsSync(path.join(tmp, "topSDK", "TOPGuestSigninPlugin.framework"))).toBe(true);
|
|
111
|
+
expect(fs.existsSync(path.join(tmp, "topSDK", "TopSDKSource.bundle"))).toBe(true);
|
|
112
|
+
|
|
113
|
+
const pbx = fs.readFileSync(path.join(tmp, "tooltest.xcodeproj", "project.pbxproj"), "utf8");
|
|
114
|
+
expect(pbx).toContain("TOPCore.framework");
|
|
115
|
+
expect(pbx).toContain("TOPGuestSigninPlugin.framework");
|
|
116
|
+
expect(pbx).toContain("-ObjC");
|
|
117
|
+
expect(pbxBuildFileRefErrors(pbx)).toEqual([]);
|
|
118
|
+
|
|
119
|
+
const delegate = fs.readFileSync(path.join(tmp, "tooltest", "AppDelegate.m"), "utf8");
|
|
120
|
+
expect(delegate).toContain("#import <TOPSDK/TopSDK.h>");
|
|
121
|
+
expect(delegate).toContain("TopSDK.sharedInstance");
|
|
122
|
+
expect(delegate).toContain("TOPDataSDK");
|
|
123
|
+
|
|
124
|
+
const plist = fs.readFileSync(path.join(tmp, "tooltest", "Info.plist"), "utf8");
|
|
125
|
+
expect(plist).toContain("<key>TOPSDK</key>");
|
|
126
|
+
expect(plist).toContain("<key>APP_ID</key>");
|
|
127
|
+
expect(plist).toContain("mock-ios-tooltest-app-id");
|
|
128
|
+
expect(plist).toContain("GuestSignin");
|
|
129
|
+
} finally {
|
|
130
|
+
fs.rmSync(tmp, { recursive: true, force: true });
|
|
131
|
+
}
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
it("apply: enables Apple Sign In entitlement when AppleSignin plugin is enabled", async () => {
|
|
135
|
+
const tmp = copyProjectToTemp();
|
|
136
|
+
try {
|
|
137
|
+
const configPath = path.join(tmp, "meetsdk-remote-config.json");
|
|
138
|
+
const remote = JSON.parse(fs.readFileSync(configPath, "utf8")) as Record<string, unknown>;
|
|
139
|
+
remote.sdkModules = {
|
|
140
|
+
login: { apple: {} },
|
|
141
|
+
payment: {},
|
|
142
|
+
analytics: {},
|
|
143
|
+
};
|
|
144
|
+
fs.writeFileSync(configPath, JSON.stringify(remote, null, 2), "utf8");
|
|
145
|
+
|
|
146
|
+
const ctx = buildWorkspaceContext(tmp, pkgRoot);
|
|
147
|
+
const { report } = await runPipeline(ctx, manifest(), { dryRun: false });
|
|
148
|
+
expect(report.errors).toEqual([]);
|
|
149
|
+
|
|
150
|
+
const entitlementsPath = path.join(tmp, "tooltest", "tooltest.entitlements");
|
|
151
|
+
expect(fs.existsSync(entitlementsPath)).toBe(true);
|
|
152
|
+
const entitlements = fs.readFileSync(entitlementsPath, "utf8");
|
|
153
|
+
expect(entitlements).toContain("com.apple.developer.applesignin");
|
|
154
|
+
expect(entitlements).toContain("Default");
|
|
155
|
+
|
|
156
|
+
const pbx = fs.readFileSync(path.join(tmp, "tooltest.xcodeproj", "project.pbxproj"), "utf8");
|
|
157
|
+
expect(pbx).toContain("CODE_SIGN_ENTITLEMENTS");
|
|
158
|
+
expect(pbx).toContain("tooltest/tooltest.entitlements");
|
|
159
|
+
expect(pbx).toContain("TOPAppleSigninPlugin.framework");
|
|
160
|
+
} finally {
|
|
161
|
+
fs.rmSync(tmp, { recursive: true, force: true });
|
|
162
|
+
}
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
it("dry-run: maps supported iOS plugin params from meetsdk remote config", async () => {
|
|
166
|
+
const tmp = copyProjectToTemp();
|
|
167
|
+
try {
|
|
168
|
+
const configPath = path.join(tmp, "meetsdk-remote-config.json");
|
|
169
|
+
const remote = JSON.parse(fs.readFileSync(configPath, "utf8")) as Record<string, unknown>;
|
|
170
|
+
remote.sdkModules = {
|
|
171
|
+
login: {
|
|
172
|
+
guest: {},
|
|
173
|
+
email: {},
|
|
174
|
+
apple: {},
|
|
175
|
+
facebook: {
|
|
176
|
+
clientId: "fb-app",
|
|
177
|
+
scheme: "fb-scheme",
|
|
178
|
+
secret: "fb-token",
|
|
179
|
+
name: "Facebook Display",
|
|
180
|
+
},
|
|
181
|
+
google: {
|
|
182
|
+
clientId: "google-client",
|
|
183
|
+
scheme: "google-reversed",
|
|
184
|
+
},
|
|
185
|
+
snapchat: {
|
|
186
|
+
clientId: "snap-client",
|
|
187
|
+
redirect: "snap-redirect",
|
|
188
|
+
scheme: "snap-scheme",
|
|
189
|
+
},
|
|
190
|
+
line: {
|
|
191
|
+
clientId: "line-client",
|
|
192
|
+
scheme: "line-scheme",
|
|
193
|
+
},
|
|
194
|
+
naver: {
|
|
195
|
+
clientId: "naver-client",
|
|
196
|
+
secret: "naver-secret",
|
|
197
|
+
name: "Naver Name",
|
|
198
|
+
scheme: "naver-scheme",
|
|
199
|
+
},
|
|
200
|
+
kakao: {
|
|
201
|
+
clientId: "kakao-client",
|
|
202
|
+
scheme: "kakao-scheme",
|
|
203
|
+
},
|
|
204
|
+
tiktok: {
|
|
205
|
+
clientId: "tiktok-client",
|
|
206
|
+
secret: "tiktok-secret",
|
|
207
|
+
redirect: "tiktok-scheme://callback",
|
|
208
|
+
},
|
|
209
|
+
discord: {
|
|
210
|
+
clientId: "discord-client",
|
|
211
|
+
secret: "discord-secret",
|
|
212
|
+
redirect: "https://example.invalid/oauth-redirect.html",
|
|
213
|
+
},
|
|
214
|
+
},
|
|
215
|
+
payment: {
|
|
216
|
+
googleIap: {},
|
|
217
|
+
},
|
|
218
|
+
analytics: {
|
|
219
|
+
appsflyer: {
|
|
220
|
+
devKey: "af-dev-key",
|
|
221
|
+
appleAppId: "123456789",
|
|
222
|
+
enableDebugLog: true,
|
|
223
|
+
},
|
|
224
|
+
adjust: {
|
|
225
|
+
appCode: "adjust-token",
|
|
226
|
+
enableSandbox: true,
|
|
227
|
+
},
|
|
228
|
+
facebookdata: {
|
|
229
|
+
clientId: "fb-app",
|
|
230
|
+
scheme: "fb-scheme",
|
|
231
|
+
secret: "fb-token",
|
|
232
|
+
name: "Facebook Display",
|
|
233
|
+
},
|
|
234
|
+
},
|
|
235
|
+
};
|
|
236
|
+
fs.writeFileSync(configPath, JSON.stringify(remote, null, 2), "utf8");
|
|
237
|
+
|
|
238
|
+
const ctx = buildWorkspaceContext(tmp, pkgRoot);
|
|
239
|
+
const { report, patch, binaryCopies } = await runPipeline(ctx, manifest(), { dryRun: true });
|
|
240
|
+
expect(report.errors).toEqual([]);
|
|
241
|
+
expect(report.warnings.join("\n")).not.toContain("Discord");
|
|
242
|
+
expect(plannedIosAsset(patch, binaryCopies, "TOPGoogleSigninPlugin.framework")).toBe(true);
|
|
243
|
+
expect(plannedIosAsset(patch, binaryCopies, "TOPFacebookSigninPlugin.framework")).toBe(true);
|
|
244
|
+
expect(plannedIosAsset(patch, binaryCopies, "TOPSnapchatSigninPlugin.framework")).toBe(true);
|
|
245
|
+
expect(plannedIosAsset(patch, binaryCopies, "TOPLineSigninPlugin.framework")).toBe(true);
|
|
246
|
+
expect(plannedIosAsset(patch, binaryCopies, "TOPNaverSigninPlugin.framework")).toBe(true);
|
|
247
|
+
expect(plannedIosAsset(patch, binaryCopies, "TOPKakaoSigninPlugin.framework")).toBe(true);
|
|
248
|
+
expect(plannedIosAsset(patch, binaryCopies, "TOPTiktokSigninPlugin.framework")).toBe(true);
|
|
249
|
+
expect(plannedIosAsset(patch, binaryCopies, "TOPDiscordSigninPlugin.framework")).toBe(true);
|
|
250
|
+
expect(plannedIosAsset(patch, binaryCopies, "TOPIAPPayPlugin.framework")).toBe(true);
|
|
251
|
+
expect(plannedIosAsset(patch, binaryCopies, "TOPDataAppsFlyerPlugin.framework")).toBe(true);
|
|
252
|
+
expect(plannedIosAsset(patch, binaryCopies, "TOPDataAdjustPlugin.framework")).toBe(true);
|
|
253
|
+
expect(plannedIosAsset(patch, binaryCopies, "TOPDataFacebookPlugin.framework")).toBe(true);
|
|
254
|
+
expect(patch).toContain("FacebookDisplayName");
|
|
255
|
+
expect(patch).toContain("Facebook Display");
|
|
256
|
+
expect(patch).toContain("REVERSED_CLIENT_ID");
|
|
257
|
+
expect(patch).toContain("google-reversed");
|
|
258
|
+
expect(patch).toContain("APPLE_APP_ID");
|
|
259
|
+
expect(patch).toContain("123456789");
|
|
260
|
+
expect(patch).toContain("APP_TOKEN");
|
|
261
|
+
expect(patch).toContain("adjust-token");
|
|
262
|
+
expect(patch).toContain("SCSDKRedirectUrl");
|
|
263
|
+
expect(patch).toContain("snap-redirect");
|
|
264
|
+
expect(binaryCopies.some((c) => c.relTo.includes("topSDK/TOPDataFacebookPlugin.framework"))).toBe(true);
|
|
265
|
+
} finally {
|
|
266
|
+
fs.rmSync(tmp, { recursive: true, force: true });
|
|
267
|
+
}
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
it("dry-run: uses remote GoogleService-Info.plist for iOS Firebase instead of bundled source", async () => {
|
|
271
|
+
const tmp = copyProjectToTemp();
|
|
272
|
+
try {
|
|
273
|
+
const configPath = path.join(tmp, "meetsdk-remote-config.json");
|
|
274
|
+
const remote = JSON.parse(fs.readFileSync(configPath, "utf8")) as Record<string, unknown>;
|
|
275
|
+
remote.sdkModules = {
|
|
276
|
+
login: {},
|
|
277
|
+
payment: {},
|
|
278
|
+
analytics: {
|
|
279
|
+
firebase: {
|
|
280
|
+
firebaseUrl: "https://cdn.example.invalid/GoogleService-Info.plist",
|
|
281
|
+
firebaseName: "GoogleService-Info.plist",
|
|
282
|
+
},
|
|
283
|
+
},
|
|
284
|
+
};
|
|
285
|
+
fs.writeFileSync(configPath, JSON.stringify(remote, null, 2), "utf8");
|
|
286
|
+
|
|
287
|
+
const ctx = buildWorkspaceContext(tmp, pkgRoot);
|
|
288
|
+
const { report, patch } = await runPipeline(ctx, manifest(), { dryRun: true });
|
|
289
|
+
|
|
290
|
+
expect(report.errors).toEqual([]);
|
|
291
|
+
expect(report.warnings.join("\n")).toContain("would download GoogleService-Info.plist to tooltest/GoogleService-Info.plist");
|
|
292
|
+
expect(patch).toContain("TOPDataFirebasePlugin.framework");
|
|
293
|
+
expect(patch).toContain("GoogleService-Info.plist");
|
|
294
|
+
} finally {
|
|
295
|
+
fs.rmSync(tmp, { recursive: true, force: true });
|
|
296
|
+
}
|
|
297
|
+
});
|
|
298
|
+
|
|
299
|
+
it("apply: keeps launch initialization in AppDelegate and routes URL/lifecycle callbacks to SceneDelegate when present", async () => {
|
|
300
|
+
const tmp = copyProjectToTemp();
|
|
301
|
+
try {
|
|
302
|
+
fs.writeFileSync(
|
|
303
|
+
path.join(tmp, "tooltest", "AppDelegate.m"),
|
|
304
|
+
`#import "AppDelegate.h"
|
|
305
|
+
|
|
306
|
+
@implementation AppDelegate
|
|
307
|
+
|
|
308
|
+
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
|
|
309
|
+
return YES;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
@end
|
|
313
|
+
`,
|
|
314
|
+
"utf8"
|
|
315
|
+
);
|
|
316
|
+
fs.writeFileSync(
|
|
317
|
+
path.join(tmp, "tooltest", "SceneDelegate.m"),
|
|
318
|
+
`#import "SceneDelegate.h"
|
|
319
|
+
|
|
320
|
+
@implementation SceneDelegate
|
|
321
|
+
|
|
322
|
+
@end
|
|
323
|
+
`,
|
|
324
|
+
"utf8"
|
|
325
|
+
);
|
|
326
|
+
|
|
327
|
+
const ctx = buildWorkspaceContext(tmp, pkgRoot);
|
|
328
|
+
const { report } = await runPipeline(ctx, manifest(), { dryRun: false });
|
|
329
|
+
expect(report.errors).toEqual([]);
|
|
330
|
+
|
|
331
|
+
const appDelegate = fs.readFileSync(path.join(tmp, "tooltest", "AppDelegate.m"), "utf8");
|
|
332
|
+
expect(appDelegate).toContain("[TopSDK.sharedInstance application:application didFinishLaunchingWithOptions:launchOptions]");
|
|
333
|
+
expect(appDelegate).toContain("[TOPDataSDK application:application didFinishLaunchingWithOptions:launchOptions]");
|
|
334
|
+
expect(appDelegate).not.toContain("openURL:url options");
|
|
335
|
+
expect(appDelegate).not.toContain("continueUserActivity:userActivity restorationHandler");
|
|
336
|
+
expect(appDelegate).not.toContain("applicationDidEnterBackground");
|
|
337
|
+
expect(appDelegate).not.toContain("applicationWillEnterForeground");
|
|
338
|
+
|
|
339
|
+
const sceneDelegate = fs.readFileSync(path.join(tmp, "tooltest", "SceneDelegate.m"), "utf8");
|
|
340
|
+
expect(sceneDelegate).toContain("[TopSDK.sharedInstance scene:scene openURLContexts:URLContexts]");
|
|
341
|
+
expect(sceneDelegate).toContain("[TopSDK.sharedInstance scene:scene continueUserActivity:userActivity]");
|
|
342
|
+
expect(sceneDelegate).toContain("[TopSDK.sharedInstance sceneWillEnterForeground:scene]");
|
|
343
|
+
expect(sceneDelegate).toContain("[TopSDK.sharedInstance sceneDidEnterBackground:scene]");
|
|
344
|
+
} finally {
|
|
345
|
+
fs.rmSync(tmp, { recursive: true, force: true });
|
|
346
|
+
}
|
|
347
|
+
});
|
|
348
|
+
|
|
349
|
+
it("fails when the configured Info.plist file is missing", async () => {
|
|
350
|
+
const tmp = copyProjectToTemp();
|
|
351
|
+
try {
|
|
352
|
+
fs.rmSync(path.join(tmp, "tooltest", "Info.plist"));
|
|
353
|
+
|
|
354
|
+
const ctx = buildWorkspaceContext(tmp, pkgRoot);
|
|
355
|
+
const { report } = await runPipeline(ctx, manifest(), { dryRun: true });
|
|
356
|
+
|
|
357
|
+
expect(report.errors.join("\n")).toContain("Info.plist not found for iOS target tooltest");
|
|
358
|
+
} finally {
|
|
359
|
+
fs.rmSync(tmp, { recursive: true, force: true });
|
|
360
|
+
}
|
|
361
|
+
});
|
|
362
|
+
|
|
363
|
+
it("fails clearly when enabled iOS plugin is missing required remote params", async () => {
|
|
364
|
+
const tmp = copyProjectToTemp();
|
|
365
|
+
try {
|
|
366
|
+
const configPath = path.join(tmp, "meetsdk-remote-config.json");
|
|
367
|
+
const remote = JSON.parse(fs.readFileSync(configPath, "utf8")) as Record<string, unknown>;
|
|
368
|
+
remote.sdkModules = {
|
|
369
|
+
login: { google: {} },
|
|
370
|
+
payment: {},
|
|
371
|
+
analytics: {},
|
|
372
|
+
};
|
|
373
|
+
fs.writeFileSync(configPath, JSON.stringify(remote, null, 2), "utf8");
|
|
374
|
+
|
|
375
|
+
const ctx = buildWorkspaceContext(tmp, pkgRoot);
|
|
376
|
+
const { report } = await runPipeline(ctx, manifest(), { dryRun: false });
|
|
377
|
+
expect(report.errors.join("\n")).toContain("iOS remote config missing required channel params");
|
|
378
|
+
expect(report.errors.join("\n")).toContain("GoogleSignin: GOOGLE.scheme");
|
|
379
|
+
expect(report.errors.join("\n")).toContain("GoogleSignin: GOOGLE.clientId");
|
|
380
|
+
expect(report.results[0]?.changedFiles).toEqual([]);
|
|
381
|
+
} finally {
|
|
382
|
+
fs.rmSync(tmp, { recursive: true, force: true });
|
|
383
|
+
}
|
|
384
|
+
});
|
|
385
|
+
});
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
5
|
+
import { loadAndroidIntegrationFile } from "../src/config/loadAndroidIntegration.js";
|
|
6
|
+
import { loadManifestFile } from "../src/config/loadManifest.js";
|
|
7
|
+
import type { AndroidIntegrationDocument, Manifest } from "../src/contracts/types.js";
|
|
8
|
+
import { clearPreviewPatchFilesIfTargetInside } from "../src/core/previewPatches.js";
|
|
9
|
+
import { buildWorkspaceContext } from "../src/core/workspace.js";
|
|
10
|
+
import { runPipeline } from "../src/core/pipeline.js";
|
|
11
|
+
|
|
12
|
+
const here = path.dirname(fileURLToPath(import.meta.url));
|
|
13
|
+
const pkgRoot = path.resolve(here, "..");
|
|
14
|
+
const fixtureRoot = path.join(pkgRoot, "fixtures", "android-test-project", "android-latest-project");
|
|
15
|
+
const recipeFixtureRoot = path.join(pkgRoot, "fixtures", "recipes");
|
|
16
|
+
const previewPatchPath = path.join(pkgRoot, "test-projects", "_preview", "pipeline.patch");
|
|
17
|
+
|
|
18
|
+
function stubSdkHomeVersion(): void {
|
|
19
|
+
vi.stubGlobal(
|
|
20
|
+
"fetch",
|
|
21
|
+
vi.fn(async () => ({
|
|
22
|
+
ok: true,
|
|
23
|
+
status: 200,
|
|
24
|
+
text: async () =>
|
|
25
|
+
JSON.stringify({
|
|
26
|
+
code: 200,
|
|
27
|
+
data: {
|
|
28
|
+
result: {
|
|
29
|
+
android: { ver: "1.6.1.3", date: "2026-05-25" },
|
|
30
|
+
ios: { ver: "1.6.0.5", date: "2026-06-09" },
|
|
31
|
+
},
|
|
32
|
+
},
|
|
33
|
+
}),
|
|
34
|
+
})) as unknown as typeof fetch
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function toManifest(doc: AndroidIntegrationDocument): Manifest {
|
|
39
|
+
const { meta: _m, remote: _r, ...rest } = doc;
|
|
40
|
+
void _m;
|
|
41
|
+
void _r;
|
|
42
|
+
return rest;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Single on-disk preview for humans / CI artifacts: both fixture manifests (dry-run only).
|
|
47
|
+
*/
|
|
48
|
+
describe("pipeline preview patch", () => {
|
|
49
|
+
beforeEach(() => {
|
|
50
|
+
stubSdkHomeVersion();
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
afterEach(() => {
|
|
54
|
+
vi.unstubAllGlobals();
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
it("writes test-projects/_preview/pipeline.patch (yaml + integration-json)", async () => {
|
|
58
|
+
const ctx = buildWorkspaceContext(fixtureRoot, pkgRoot);
|
|
59
|
+
expect(ctx.android?.ok).toBe(true);
|
|
60
|
+
|
|
61
|
+
const yamlManifest = loadManifestFile(path.join(recipeFixtureRoot, "android-default.fixture.yaml"));
|
|
62
|
+
const rYaml = await runPipeline(ctx, yamlManifest, { dryRun: true });
|
|
63
|
+
expect(rYaml.report.errors).toEqual([]);
|
|
64
|
+
|
|
65
|
+
const doc = loadAndroidIntegrationFile(path.join(recipeFixtureRoot, "android-integration.fixture.json"));
|
|
66
|
+
const rJson = await runPipeline(ctx, toManifest(doc), { dryRun: true });
|
|
67
|
+
expect(rJson.report.errors).toEqual([]);
|
|
68
|
+
|
|
69
|
+
const body = [
|
|
70
|
+
"# dry-run — fixtures/recipes/android-default.fixture.yaml",
|
|
71
|
+
rYaml.patch.trimEnd(),
|
|
72
|
+
"",
|
|
73
|
+
"# dry-run — android-integration.fixture.json",
|
|
74
|
+
rJson.patch.trimEnd(),
|
|
75
|
+
"",
|
|
76
|
+
].join("\n");
|
|
77
|
+
|
|
78
|
+
clearPreviewPatchFilesIfTargetInside(pkgRoot, previewPatchPath);
|
|
79
|
+
fs.mkdirSync(path.dirname(previewPatchPath), { recursive: true });
|
|
80
|
+
fs.writeFileSync(previewPatchPath, body, "utf8");
|
|
81
|
+
|
|
82
|
+
expect(rYaml.patch).toContain("TOPSDK REPO AUTO");
|
|
83
|
+
expect(rJson.patch).toContain("TOPSDK AUTO");
|
|
84
|
+
});
|
|
85
|
+
});
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { describe, expect, it } from "vitest";
|
|
4
|
+
import {
|
|
5
|
+
manifestForPlatform,
|
|
6
|
+
detectSinglePlatform,
|
|
7
|
+
platformContext,
|
|
8
|
+
} from "../src/core/platform.js";
|
|
9
|
+
import { buildWorkspaceContext } from "../src/core/workspace.js";
|
|
10
|
+
import type { Manifest } from "../src/contracts/types.js";
|
|
11
|
+
|
|
12
|
+
const pkgRoot = path.resolve(__dirname, "..");
|
|
13
|
+
const androidLatestRoot = path.join(pkgRoot, "fixtures", "android-test-project", "android-latest-project");
|
|
14
|
+
const iosProjectRoot = path.join(pkgRoot, "fixtures", "ios-test-project", "tooltest");
|
|
15
|
+
const hasIosProjectFixture = fs.existsSync(iosProjectRoot);
|
|
16
|
+
|
|
17
|
+
describe("platform selection", () => {
|
|
18
|
+
it("detects Android project roots as Android only", () => {
|
|
19
|
+
const ctx = buildWorkspaceContext(androidLatestRoot, pkgRoot);
|
|
20
|
+
const detected = detectSinglePlatform(ctx);
|
|
21
|
+
|
|
22
|
+
expect(detected).toEqual({ ok: true, platform: "android" });
|
|
23
|
+
expect(platformContext(ctx, "android").android?.ok).toBe(true);
|
|
24
|
+
expect(platformContext(ctx, "android").ios).toBeUndefined();
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
it.skipIf(!hasIosProjectFixture)("detects iOS project roots as iOS only", () => {
|
|
28
|
+
const ctx = buildWorkspaceContext(iosProjectRoot, pkgRoot);
|
|
29
|
+
const detected = detectSinglePlatform(ctx);
|
|
30
|
+
|
|
31
|
+
expect(detected).toEqual({ ok: true, platform: "ios" });
|
|
32
|
+
expect(platformContext(ctx, "ios").ios?.ok).toBe(true);
|
|
33
|
+
expect(platformContext(ctx, "ios").android).toBeUndefined();
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it("filters mixed manifests to the selected platform before running", () => {
|
|
37
|
+
const manifest: Manifest = {
|
|
38
|
+
version: 1,
|
|
39
|
+
steps: [
|
|
40
|
+
{ op: "android.one", platform: "android" },
|
|
41
|
+
{ op: "ios.one", platform: "ios" },
|
|
42
|
+
{ op: "shared.one", platform: "all" },
|
|
43
|
+
{ op: "shared.two" },
|
|
44
|
+
],
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
expect(manifestForPlatform(manifest, "android").steps.map((s) => s.op)).toEqual([
|
|
48
|
+
"android.one",
|
|
49
|
+
"shared.one",
|
|
50
|
+
"shared.two",
|
|
51
|
+
]);
|
|
52
|
+
expect(manifestForPlatform(manifest, "ios").steps.map((s) => s.op)).toEqual([
|
|
53
|
+
"ios.one",
|
|
54
|
+
"shared.one",
|
|
55
|
+
"shared.two",
|
|
56
|
+
]);
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it.skipIf(!hasIosProjectFixture)("overrides detected iOS target name when explicitly provided", () => {
|
|
60
|
+
const ctx = buildWorkspaceContext(iosProjectRoot, pkgRoot, {
|
|
61
|
+
appTarget: "tooltest",
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
expect(ctx.ios?.ok && ctx.ios.targetName).toBe("tooltest");
|
|
65
|
+
expect(ctx.ios?.ok && ctx.ios.targetNames).toContain("tooltest");
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
it.skipIf(!hasIosProjectFixture)("rejects missing iOS app target names during detection", () => {
|
|
69
|
+
const ctx = buildWorkspaceContext(iosProjectRoot, pkgRoot, {
|
|
70
|
+
appTarget: "GameRelease",
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
expect(ctx.ios?.ok).toBe(false);
|
|
74
|
+
expect(ctx.ios?.ok ? "" : ctx.ios?.error).toContain("IOS_APP_TARGET_NOT_FOUND: GameRelease");
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
});
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { afterEach, describe, expect, it, vi } from "vitest";
|
|
2
|
+
import fs from "node:fs";
|
|
3
|
+
import os from "node:os";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import {
|
|
6
|
+
DEFAULT_IOS_SDK_PLUGINS,
|
|
7
|
+
buildSdkHomeDownloadUrl,
|
|
8
|
+
buildSdkHomeVersionUrl,
|
|
9
|
+
fetchSdkHomeIosDownloadUrl,
|
|
10
|
+
fetchSdkHomeIosVersion,
|
|
11
|
+
fetchSdkHomeVersions,
|
|
12
|
+
resolveIosSdkRootFromDirectory,
|
|
13
|
+
resolveIosSdkZipFileName,
|
|
14
|
+
} from "../src/remote/sdkHomeDownload.js";
|
|
15
|
+
|
|
16
|
+
describe("sdk-home iOS SDK download client", () => {
|
|
17
|
+
afterEach(() => {
|
|
18
|
+
vi.unstubAllGlobals();
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
it("builds version and getDownLoadUrl URLs", () => {
|
|
22
|
+
expect(buildSdkHomeVersionUrl("https://business-api.meetgames.com")).toBe(
|
|
23
|
+
"https://business-api.meetgames.com/sdk/home/version"
|
|
24
|
+
);
|
|
25
|
+
const url = buildSdkHomeDownloadUrl({
|
|
26
|
+
baseUrl: "https://business-api.meetgames.com/",
|
|
27
|
+
version: "1.6.0.5",
|
|
28
|
+
plugins: DEFAULT_IOS_SDK_PLUGINS,
|
|
29
|
+
packageType: "native",
|
|
30
|
+
});
|
|
31
|
+
const u = new URL(url);
|
|
32
|
+
expect(u.pathname).toBe("/sdk/home/sdk-download/getDownLoadUrl");
|
|
33
|
+
expect(u.searchParams.get("version")).toBe("1.6.0.5");
|
|
34
|
+
expect(u.searchParams.get("platform")).toBe("ios");
|
|
35
|
+
expect(u.searchParams.get("plugins")).toBe(DEFAULT_IOS_SDK_PLUGINS.join(","));
|
|
36
|
+
expect(u.searchParams.get("packageType")).toBe("native");
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it("parses the iOS version envelope", async () => {
|
|
40
|
+
vi.stubGlobal(
|
|
41
|
+
"fetch",
|
|
42
|
+
vi.fn(async () => ({
|
|
43
|
+
ok: true,
|
|
44
|
+
status: 200,
|
|
45
|
+
text: async () =>
|
|
46
|
+
JSON.stringify({
|
|
47
|
+
code: 200,
|
|
48
|
+
data: {
|
|
49
|
+
result: {
|
|
50
|
+
android: { ver: "1.6.1.3", date: "2026-05-25" },
|
|
51
|
+
ios: { ver: "1.6.0.5", date: "2026-06-09" },
|
|
52
|
+
},
|
|
53
|
+
},
|
|
54
|
+
}),
|
|
55
|
+
})) as unknown as typeof fetch
|
|
56
|
+
);
|
|
57
|
+
|
|
58
|
+
const { ios } = await fetchSdkHomeIosVersion({ baseUrl: "https://example.com" });
|
|
59
|
+
expect(ios).toEqual({ ver: "1.6.0.5", date: "2026-06-09" });
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it("parses Android and iOS versions from the version envelope", async () => {
|
|
63
|
+
vi.stubGlobal(
|
|
64
|
+
"fetch",
|
|
65
|
+
vi.fn(async () => ({
|
|
66
|
+
ok: true,
|
|
67
|
+
status: 200,
|
|
68
|
+
text: async () =>
|
|
69
|
+
JSON.stringify({
|
|
70
|
+
code: 200,
|
|
71
|
+
data: {
|
|
72
|
+
result: {
|
|
73
|
+
android: { ver: "1.6.1.3", date: "2026-05-25" },
|
|
74
|
+
ios: { ver: "1.6.0.5", date: "2026-06-09" },
|
|
75
|
+
},
|
|
76
|
+
},
|
|
77
|
+
}),
|
|
78
|
+
})) as unknown as typeof fetch
|
|
79
|
+
);
|
|
80
|
+
|
|
81
|
+
const { versions } = await fetchSdkHomeVersions({ baseUrl: "https://example.com" });
|
|
82
|
+
expect(versions.android).toEqual({ ver: "1.6.1.3", date: "2026-05-25" });
|
|
83
|
+
expect(versions.ios).toEqual({ ver: "1.6.0.5", date: "2026-06-09" });
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
it("parses the download URL envelope", async () => {
|
|
87
|
+
vi.stubGlobal(
|
|
88
|
+
"fetch",
|
|
89
|
+
vi.fn(async () => ({
|
|
90
|
+
ok: true,
|
|
91
|
+
status: 200,
|
|
92
|
+
text: async () => JSON.stringify({ code: 200, data: "https://cdn.example.com/topSDK-ios.zip" }),
|
|
93
|
+
})) as unknown as typeof fetch
|
|
94
|
+
);
|
|
95
|
+
|
|
96
|
+
const { sdkZipUrl } = await fetchSdkHomeIosDownloadUrl({
|
|
97
|
+
baseUrl: "https://example.com",
|
|
98
|
+
version: "1.6.0.5",
|
|
99
|
+
});
|
|
100
|
+
expect(sdkZipUrl).toBe("https://cdn.example.com/topSDK-ios.zip");
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
it("uses the sdk-home generated iOS archive name", () => {
|
|
104
|
+
expect(resolveIosSdkZipFileName("1.6.0.5")).toBe("topSDK-ios--V1.6.0.5.zip");
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
it("resolves top-level and nested extracted SDK layouts", () => {
|
|
108
|
+
const root = fs.mkdtempSync(path.join(os.tmpdir(), "meet-sdk-tool-ios-"));
|
|
109
|
+
try {
|
|
110
|
+
fs.mkdirSync(path.join(root, "sdk"), { recursive: true });
|
|
111
|
+
fs.mkdirSync(path.join(root, "plugins"), { recursive: true });
|
|
112
|
+
expect(resolveIosSdkRootFromDirectory(root)).toBe(root);
|
|
113
|
+
|
|
114
|
+
const nestedRoot = path.join(root, "nested");
|
|
115
|
+
fs.rmSync(path.join(root, "sdk"), { recursive: true, force: true });
|
|
116
|
+
fs.rmSync(path.join(root, "plugins"), { recursive: true, force: true });
|
|
117
|
+
fs.mkdirSync(path.join(nestedRoot, "sdk"), { recursive: true });
|
|
118
|
+
fs.mkdirSync(path.join(nestedRoot, "plugins"), { recursive: true });
|
|
119
|
+
expect(resolveIosSdkRootFromDirectory(root)).toBe(nestedRoot);
|
|
120
|
+
} finally {
|
|
121
|
+
fs.rmSync(root, { recursive: true, force: true });
|
|
122
|
+
}
|
|
123
|
+
});
|
|
124
|
+
});
|