@simplysm/sd-cli 14.0.18 → 14.0.21
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/dist/angular/vite-angular-plugin.d.ts +2 -0
- package/dist/angular/vite-angular-plugin.d.ts.map +1 -1
- package/dist/angular/vite-angular-plugin.js +57 -28
- package/dist/angular/vite-angular-plugin.js.map +1 -1
- package/dist/angular/vite-postcss-inline-plugin.d.ts.map +1 -1
- package/dist/angular/vite-postcss-inline-plugin.js +4 -1
- package/dist/angular/vite-postcss-inline-plugin.js.map +1 -1
- package/dist/capacitor/capacitor-android.d.ts +16 -0
- package/dist/capacitor/capacitor-android.d.ts.map +1 -0
- package/dist/capacitor/capacitor-android.js +289 -0
- package/dist/capacitor/capacitor-android.js.map +1 -0
- package/dist/capacitor/capacitor.d.ts +0 -50
- package/dist/capacitor/capacitor.d.ts.map +1 -1
- package/dist/capacitor/capacitor.js +16 -281
- package/dist/capacitor/capacitor.js.map +1 -1
- package/dist/commands/check.js +2 -2
- package/dist/commands/check.js.map +1 -1
- package/dist/commands/device.d.ts.map +1 -1
- package/dist/commands/device.js +3 -2
- package/dist/commands/device.js.map +1 -1
- package/dist/commands/lint.d.ts +1 -42
- package/dist/commands/lint.d.ts.map +1 -1
- package/dist/commands/lint.js +1 -151
- package/dist/commands/lint.js.map +1 -1
- package/dist/commands/publish.d.ts.map +1 -1
- package/dist/commands/publish.js +2 -1
- package/dist/commands/publish.js.map +1 -1
- package/dist/commands/typecheck.d.ts +3 -40
- package/dist/commands/typecheck.d.ts.map +1 -1
- package/dist/commands/typecheck.js +3 -232
- package/dist/commands/typecheck.js.map +1 -1
- package/dist/electron/electron.d.ts.map +1 -1
- package/dist/electron/electron.js +20 -8
- package/dist/electron/electron.js.map +1 -1
- package/dist/orchestrators/DevWatchOrchestrator.d.ts +1 -0
- package/dist/orchestrators/DevWatchOrchestrator.d.ts.map +1 -1
- package/dist/orchestrators/DevWatchOrchestrator.js +16 -0
- package/dist/orchestrators/DevWatchOrchestrator.js.map +1 -1
- package/dist/orchestrators/TypecheckOrchestrator.d.ts +74 -0
- package/dist/orchestrators/TypecheckOrchestrator.d.ts.map +1 -0
- package/dist/orchestrators/TypecheckOrchestrator.js +285 -0
- package/dist/orchestrators/TypecheckOrchestrator.js.map +1 -0
- package/dist/sd-cli.js +6 -1
- package/dist/sd-cli.js.map +1 -1
- package/dist/utils/lint-core.d.ts +43 -0
- package/dist/utils/lint-core.d.ts.map +1 -0
- package/dist/utils/lint-core.js +154 -0
- package/dist/utils/lint-core.js.map +1 -0
- package/dist/utils/lint-utils.d.ts +1 -1
- package/dist/utils/lint-utils.d.ts.map +1 -1
- package/dist/utils/server-production-files.d.ts +22 -0
- package/dist/utils/server-production-files.d.ts.map +1 -0
- package/dist/utils/server-production-files.js +162 -0
- package/dist/utils/server-production-files.js.map +1 -0
- package/dist/utils/vite-config.d.ts +1 -1
- package/dist/utils/vite-config.d.ts.map +1 -1
- package/dist/utils/vite-config.js +76 -26
- package/dist/utils/vite-config.js.map +1 -1
- package/dist/utils/vite-scope-watch-plugin.d.ts.map +1 -1
- package/dist/utils/vite-scope-watch-plugin.js +7 -1
- package/dist/utils/vite-scope-watch-plugin.js.map +1 -1
- package/dist/workers/lint.worker.d.ts +1 -1
- package/dist/workers/lint.worker.d.ts.map +1 -1
- package/dist/workers/lint.worker.js +1 -1
- package/dist/workers/lint.worker.js.map +1 -1
- package/dist/workers/server-build.worker.d.ts.map +1 -1
- package/dist/workers/server-build.worker.js +11 -161
- package/dist/workers/server-build.worker.js.map +1 -1
- package/dist/workers/server-runtime.worker.d.ts.map +1 -1
- package/dist/workers/server-runtime.worker.js +15 -0
- package/dist/workers/server-runtime.worker.js.map +1 -1
- package/package.json +9 -7
- package/src/angular/vite-angular-plugin.ts +88 -34
- package/src/angular/vite-postcss-inline-plugin.ts +5 -1
- package/src/capacitor/capacitor-android.ts +368 -0
- package/src/capacitor/capacitor.ts +18 -363
- package/src/commands/check.ts +2 -2
- package/src/commands/device.ts +3 -2
- package/src/commands/lint.ts +1 -201
- package/src/commands/publish.ts +2 -1
- package/src/commands/typecheck.ts +7 -292
- package/src/electron/electron.ts +15 -8
- package/src/orchestrators/DevWatchOrchestrator.ts +18 -0
- package/src/orchestrators/TypecheckOrchestrator.ts +364 -0
- package/src/sd-cli.ts +6 -1
- package/src/utils/lint-core.ts +205 -0
- package/src/utils/lint-utils.ts +1 -1
- package/src/utils/server-production-files.ts +186 -0
- package/src/utils/vite-config.ts +83 -27
- package/src/utils/vite-scope-watch-plugin.ts +6 -1
- package/src/workers/lint.worker.ts +1 -1
- package/src/workers/server-build.worker.ts +10 -185
- package/src/workers/server-runtime.worker.ts +15 -0
- package/tests/angular/linker-disk-cache.spec.ts +31 -25
- package/tests/angular/vite-angular-plugin-hmr-fallback.spec.ts +15 -15
- package/tests/angular/vite-angular-plugin-hmr.spec.ts +9 -9
- package/tests/angular/vite-angular-plugin-legacy-watch.spec.ts +108 -0
- package/tests/angular/vite-angular-plugin-lint.spec.ts +4 -4
- package/tests/angular/vite-angular-plugin-scss-hmr.spec.ts +10 -15
- package/tests/angular/vite-angular-plugin.spec.ts +80 -15
- package/tests/angular/vite-postcss-inline-plugin.spec.ts +10 -0
- package/tests/capacitor/capacitor-android-exports.verify.md +11 -0
- package/tests/capacitor/capacitor-android.spec.ts +219 -0
- package/tests/capacitor/capacitor-build.spec.ts +17 -21
- package/tests/capacitor/capacitor-icon.spec.ts +17 -19
- package/tests/capacitor/capacitor-init.spec.ts +18 -14
- package/tests/capacitor/capacitor-run.spec.ts +10 -24
- package/tests/capacitor/capacitor-workspace.spec.ts +30 -25
- package/tests/commands/check.spec.ts +2 -2
- package/tests/commands/device.spec.ts +12 -7
- package/tests/commands/lint.spec.ts +33 -194
- package/tests/commands/publish-set.verify.md +7 -0
- package/tests/electron/electron-symlink-cleanup.verify.md +8 -0
- package/tests/electron/electron.spec.ts +27 -2
- package/tests/orchestrators/dist-delete-watcher.verify.md +10 -0
- package/tests/orchestrators/typecheck-orchestrator.spec.ts +180 -0
- package/tests/sd-cli-catch-all.verify.md +7 -0
- package/tests/utils/lint-core-import-paths.verify.md +10 -0
- package/tests/utils/lint-core.spec.ts +188 -0
- package/tests/utils/server-production-files-import-paths.verify.md +14 -0
- package/tests/utils/vite-config.spec.ts +255 -133
- package/tests/utils/vite-scope-watch-plugin.spec.ts +22 -0
- package/tests/workers/server-build-context-dispose.verify.md +8 -0
- package/tests/workers/server-runtime-worker.spec.ts +48 -4
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# Capacitor Android 설정 분리 — LLM 검증
|
|
2
|
+
|
|
3
|
+
## 검증 항목
|
|
4
|
+
|
|
5
|
+
- [x] configureAndroid이 capacitor-android.ts에서 export된다: line 13 `export async function configureAndroid`
|
|
6
|
+
- [x] findJava21이 capacitor-android.ts에서 export된다: line 53 `export async function findJava21`
|
|
7
|
+
- [x] findAndroidSdk가 capacitor-android.ts에서 export된다: line 76 `export async function findAndroidSdk`
|
|
8
|
+
- [x] 내부 configure 함수들(_configureJavaHomePath 등)은 export되지 않는다: `^export` 검색 결과 3개만 확인
|
|
9
|
+
- [x] Capacitor 클래스의 _validateTools가 findAndroidSdk/findJava21을 import하여 사용한다: line 8 import, line 217/229 호출
|
|
10
|
+
- [x] Capacitor 클래스에서 9개 Android 설정 private 메서드가 삭제되었다: `_configureAndroid` 등 검색 결과 0건
|
|
11
|
+
- [x] capacitor.ts에서 env import가 제거되었다: `import.*env.*from` 검색 결과 0건
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
import path from "path";
|
|
2
|
+
import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
|
|
3
|
+
|
|
4
|
+
//#region Mocks
|
|
5
|
+
|
|
6
|
+
const mockFsxExists = vi.fn();
|
|
7
|
+
const mockFsxRead = vi.fn();
|
|
8
|
+
const mockFsxWrite = vi.fn().mockResolvedValue(undefined);
|
|
9
|
+
const mockFsxGlob = vi.fn();
|
|
10
|
+
|
|
11
|
+
vi.mock("@simplysm/core-node", () => ({
|
|
12
|
+
fsx: {
|
|
13
|
+
exists: mockFsxExists,
|
|
14
|
+
read: mockFsxRead,
|
|
15
|
+
write: mockFsxWrite,
|
|
16
|
+
glob: mockFsxGlob,
|
|
17
|
+
},
|
|
18
|
+
pathx: {
|
|
19
|
+
posixResolve: (...args: string[]) => path.resolve(...args).replace(/\\/g, "/"),
|
|
20
|
+
posix: (p: string) => p.replace(/\\/g, "/"),
|
|
21
|
+
},
|
|
22
|
+
}));
|
|
23
|
+
|
|
24
|
+
//#endregion
|
|
25
|
+
|
|
26
|
+
describe("findJava21", () => {
|
|
27
|
+
beforeEach(() => {
|
|
28
|
+
vi.clearAllMocks();
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it("여러 패턴 중 첫 매치를 사용하고 마지막 정렬 결과를 반환한다", async () => {
|
|
32
|
+
mockFsxGlob.mockImplementation((pattern: string) => {
|
|
33
|
+
if (pattern.includes("Amazon Corretto")) {
|
|
34
|
+
return ["C:/Program Files/Amazon Corretto/jdk21.0.1", "C:/Program Files/Amazon Corretto/jdk21.0.3"];
|
|
35
|
+
}
|
|
36
|
+
return [];
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
const { findJava21 } = await import("../../src/capacitor/capacitor-android.js");
|
|
40
|
+
const result = await findJava21();
|
|
41
|
+
expect(result).toBe("C:/Program Files/Amazon Corretto/jdk21.0.3");
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
it("모든 패턴에 매치가 없으면 undefined를 반환한다", async () => {
|
|
45
|
+
mockFsxGlob.mockResolvedValue([]);
|
|
46
|
+
|
|
47
|
+
const { findJava21 } = await import("../../src/capacitor/capacitor-android.js");
|
|
48
|
+
const result = await findJava21();
|
|
49
|
+
expect(result).toBeUndefined();
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
describe("findAndroidSdk", () => {
|
|
54
|
+
let savedEnv: Record<string, string | undefined>;
|
|
55
|
+
beforeEach(() => {
|
|
56
|
+
vi.clearAllMocks();
|
|
57
|
+
savedEnv = { ...process.env };
|
|
58
|
+
});
|
|
59
|
+
afterEach(() => {
|
|
60
|
+
process.env = savedEnv;
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
it("ANDROID_SDK_ROOT로 SDK를 감지한다", async () => {
|
|
64
|
+
process.env["ANDROID_SDK_ROOT"] = "D:/Android/Sdk";
|
|
65
|
+
mockFsxExists.mockImplementation((p: string) => p === "D:/Android/Sdk");
|
|
66
|
+
|
|
67
|
+
const { findAndroidSdk } = await import("../../src/capacitor/capacitor-android.js");
|
|
68
|
+
const result = await findAndroidSdk();
|
|
69
|
+
expect(result).toBe("D:/Android/Sdk");
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
it("환경변수와 후보 경로 모두 없으면 undefined를 반환한다", async () => {
|
|
73
|
+
delete process.env["ANDROID_SDK_ROOT"];
|
|
74
|
+
delete process.env["ANDROID_HOME"];
|
|
75
|
+
mockFsxExists.mockResolvedValue(false);
|
|
76
|
+
|
|
77
|
+
const { findAndroidSdk } = await import("../../src/capacitor/capacitor-android.js");
|
|
78
|
+
const result = await findAndroidSdk();
|
|
79
|
+
expect(result).toBeUndefined();
|
|
80
|
+
});
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
describe("configureAndroid", () => {
|
|
84
|
+
let savedEnv: Record<string, string | undefined>;
|
|
85
|
+
beforeEach(() => {
|
|
86
|
+
vi.clearAllMocks();
|
|
87
|
+
savedEnv = { ...process.env };
|
|
88
|
+
process.env["ANDROID_HOME"] = "C:/Android/Sdk";
|
|
89
|
+
});
|
|
90
|
+
afterEach(() => {
|
|
91
|
+
process.env = savedEnv;
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
it("Android 디렉토리가 없으면 에러를 던진다", async () => {
|
|
95
|
+
mockFsxExists.mockResolvedValue(false);
|
|
96
|
+
|
|
97
|
+
const { configureAndroid } = await import("../../src/capacitor/capacitor-android.js");
|
|
98
|
+
await expect(
|
|
99
|
+
configureAndroid("/fake/cap", { appId: "com.test.app", appName: "Test" }, { name: "test", version: "1.0.0" }),
|
|
100
|
+
).rejects.toThrow("Android 프로젝트 디렉토리를 찾을 수 없습니다");
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
it("모든 Android 설정을 순서대로 수행한다", async () => {
|
|
104
|
+
mockFsxExists.mockResolvedValue(true);
|
|
105
|
+
mockFsxGlob.mockResolvedValue(["C:/Program Files/Amazon Corretto/jdk21.0.1"]);
|
|
106
|
+
mockFsxRead.mockImplementation((p: string) => {
|
|
107
|
+
if (p.includes("gradle.properties")) return "org.gradle.jvmargs=-Xmx2048m";
|
|
108
|
+
if (p.includes("local.properties")) return "";
|
|
109
|
+
if (p.includes("AndroidManifest.xml")) {
|
|
110
|
+
return '<manifest xmlns:android="http://schemas.android.com/apk/res/android">\n<application>\n<activity android:name=".MainActivity">\n</activity>\n</application>\n</manifest>';
|
|
111
|
+
}
|
|
112
|
+
if (p.includes("app/build.gradle")) {
|
|
113
|
+
return `android {
|
|
114
|
+
defaultConfig {
|
|
115
|
+
versionCode 1
|
|
116
|
+
versionName "1.0"
|
|
117
|
+
minSdkVersion rootProject.ext.minSdkVersion
|
|
118
|
+
targetSdkVersion rootProject.ext.targetSdkVersion
|
|
119
|
+
}
|
|
120
|
+
buildTypes { release { } }
|
|
121
|
+
}`;
|
|
122
|
+
}
|
|
123
|
+
if (p.includes("build.gradle")) {
|
|
124
|
+
return "buildscript { dependencies { classpath 'com.android.tools.build:gradle:8.2.1' } }";
|
|
125
|
+
}
|
|
126
|
+
if (p.includes("styles.xml")) {
|
|
127
|
+
return `<resources>
|
|
128
|
+
<style name="AppTheme.NoActionBarLaunch" parent="Theme.SplashScreen">
|
|
129
|
+
<item name="android:background">@drawable/splash</item>
|
|
130
|
+
</style>
|
|
131
|
+
</resources>`;
|
|
132
|
+
}
|
|
133
|
+
return "";
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
const { configureAndroid } = await import("../../src/capacitor/capacitor-android.js");
|
|
137
|
+
await configureAndroid(
|
|
138
|
+
"/fake/cap",
|
|
139
|
+
{ appId: "com.test.app", appName: "Test App", platform: { android: {} } },
|
|
140
|
+
{ name: "test-pkg", version: "2.1.0" },
|
|
141
|
+
);
|
|
142
|
+
|
|
143
|
+
const writeCalls = mockFsxWrite.mock.calls;
|
|
144
|
+
|
|
145
|
+
// JAVA_HOME 설정
|
|
146
|
+
expect(writeCalls.some((c) => typeof c[0] === "string" && c[0].includes("gradle.properties"))).toBe(true);
|
|
147
|
+
|
|
148
|
+
// SDK 경로 설정
|
|
149
|
+
expect(writeCalls.some((c) => typeof c[0] === "string" && c[0].includes("local.properties"))).toBe(true);
|
|
150
|
+
|
|
151
|
+
// AndroidManifest.xml usesCleartextTraffic
|
|
152
|
+
const manifestWrite = writeCalls.find(
|
|
153
|
+
(c) => typeof c[0] === "string" && c[0].includes("AndroidManifest.xml"),
|
|
154
|
+
);
|
|
155
|
+
expect(manifestWrite).toBeDefined();
|
|
156
|
+
expect(manifestWrite![1]).toContain("usesCleartextTraffic");
|
|
157
|
+
|
|
158
|
+
// build.gradle versionCode (2.1.0 → 2001000)
|
|
159
|
+
const gradleWrite = writeCalls.find(
|
|
160
|
+
(c) => typeof c[0] === "string" && c[0].includes("app/build.gradle"),
|
|
161
|
+
);
|
|
162
|
+
expect(gradleWrite).toBeDefined();
|
|
163
|
+
expect(gradleWrite![1]).toContain("versionCode 2001000");
|
|
164
|
+
expect(gradleWrite![1]).toContain('versionName "2.1.0"');
|
|
165
|
+
|
|
166
|
+
// styles.xml Theme 변경
|
|
167
|
+
const stylesWrite = writeCalls.find(
|
|
168
|
+
(c) => typeof c[0] === "string" && c[0].includes("styles.xml"),
|
|
169
|
+
);
|
|
170
|
+
expect(stylesWrite).toBeDefined();
|
|
171
|
+
expect(stylesWrite![1]).toContain('parent="Theme.AppCompat.DayNight.NoActionBar"');
|
|
172
|
+
expect(stylesWrite![1]).toContain('"android:windowBackground">@drawable/splash');
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
it("minor/patch >= 100인 버전에서 versionCode가 충돌하지 않는다", async () => {
|
|
176
|
+
mockFsxExists.mockResolvedValue(true);
|
|
177
|
+
mockFsxGlob.mockResolvedValue(["C:/Program Files/Amazon Corretto/jdk21.0.1"]);
|
|
178
|
+
mockFsxRead.mockImplementation((p: string) => {
|
|
179
|
+
if (p.includes("gradle.properties")) return "";
|
|
180
|
+
if (p.includes("local.properties")) return "";
|
|
181
|
+
if (p.includes("AndroidManifest.xml")) {
|
|
182
|
+
return '<manifest xmlns:android="http://schemas.android.com/apk/res/android">\n<application>\n<activity android:name=".MainActivity">\n</activity>\n</application>\n</manifest>';
|
|
183
|
+
}
|
|
184
|
+
if (p.includes("app/build.gradle")) {
|
|
185
|
+
return "android { defaultConfig { versionCode 1\nversionName \"1.0\"\nminSdkVersion rootProject.ext.minSdkVersion\ntargetSdkVersion rootProject.ext.targetSdkVersion } }";
|
|
186
|
+
}
|
|
187
|
+
if (p.includes("build.gradle")) return "";
|
|
188
|
+
if (p.includes("styles.xml")) {
|
|
189
|
+
return '<resources><style name="AppTheme.NoActionBarLaunch" parent="Theme.SplashScreen"></style></resources>';
|
|
190
|
+
}
|
|
191
|
+
return "";
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
const { configureAndroid } = await import("../../src/capacitor/capacitor-android.js");
|
|
195
|
+
|
|
196
|
+
// "1.0.100"과 "1.1.0"은 서로 다른 versionCode를 가져야 한다
|
|
197
|
+
await configureAndroid(
|
|
198
|
+
"/fake/cap",
|
|
199
|
+
{ appId: "com.test.app", appName: "Test" },
|
|
200
|
+
{ name: "test", version: "1.0.100" },
|
|
201
|
+
);
|
|
202
|
+
const call1 = mockFsxWrite.mock.calls.find(
|
|
203
|
+
(c) => typeof c[0] === "string" && c[0].includes("app/build.gradle"),
|
|
204
|
+
);
|
|
205
|
+
expect(call1![1]).toContain("versionCode 1000100");
|
|
206
|
+
|
|
207
|
+
mockFsxWrite.mockClear();
|
|
208
|
+
|
|
209
|
+
await configureAndroid(
|
|
210
|
+
"/fake/cap",
|
|
211
|
+
{ appId: "com.test.app", appName: "Test" },
|
|
212
|
+
{ name: "test", version: "1.1.0" },
|
|
213
|
+
);
|
|
214
|
+
const call2 = mockFsxWrite.mock.calls.find(
|
|
215
|
+
(c) => typeof c[0] === "string" && c[0].includes("app/build.gradle"),
|
|
216
|
+
);
|
|
217
|
+
expect(call2![1]).toContain("versionCode 1001000");
|
|
218
|
+
});
|
|
219
|
+
});
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import path from "path";
|
|
2
|
-
import { describe, it, expect, vi, beforeEach } from "vitest";
|
|
2
|
+
import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
|
|
3
3
|
|
|
4
4
|
//#region Mocks
|
|
5
5
|
|
|
@@ -36,14 +36,6 @@ vi.mock("@simplysm/core-node", () => ({
|
|
|
36
36
|
},
|
|
37
37
|
}));
|
|
38
38
|
|
|
39
|
-
// env mock
|
|
40
|
-
let mockEnv: Record<string, unknown> = {};
|
|
41
|
-
vi.mock("@simplysm/core-common", () => ({
|
|
42
|
-
env: new Proxy({} as Record<string, unknown>, {
|
|
43
|
-
get: (_target, prop) => mockEnv[prop as string],
|
|
44
|
-
}),
|
|
45
|
-
}));
|
|
46
|
-
|
|
47
39
|
// cpx mock (was execa)
|
|
48
40
|
const execaCalls: { command: string; args: string[] }[] = [];
|
|
49
41
|
const mockCpxSpawn = vi.fn((...args: unknown[]) => {
|
|
@@ -71,19 +63,15 @@ vi.mock("sharp", () => ({
|
|
|
71
63
|
}),
|
|
72
64
|
}));
|
|
73
65
|
|
|
74
|
-
// consola mock
|
|
75
|
-
const mockLoggerDebug = vi.fn();
|
|
66
|
+
// consola mock (logger assertion 필요)
|
|
76
67
|
const mockLoggerWarn = vi.fn();
|
|
77
|
-
const
|
|
68
|
+
const _mockConsola = {
|
|
69
|
+
level: 0,
|
|
70
|
+
withTag: () => ({ debug: vi.fn(), warn: mockLoggerWarn, error: vi.fn(), info: vi.fn(), success: vi.fn() }),
|
|
71
|
+
};
|
|
78
72
|
vi.mock("consola", () => ({
|
|
79
|
-
consola:
|
|
80
|
-
|
|
81
|
-
debug: mockLoggerDebug,
|
|
82
|
-
warn: mockLoggerWarn,
|
|
83
|
-
success: mockLoggerSuccess,
|
|
84
|
-
}),
|
|
85
|
-
level: 0,
|
|
86
|
-
},
|
|
73
|
+
consola: _mockConsola,
|
|
74
|
+
default: _mockConsola,
|
|
87
75
|
LogLevels: { debug: 4 },
|
|
88
76
|
}));
|
|
89
77
|
|
|
@@ -167,7 +155,7 @@ function setupDefaultMocks() {
|
|
|
167
155
|
return [];
|
|
168
156
|
});
|
|
169
157
|
|
|
170
|
-
|
|
158
|
+
process.env["ANDROID_HOME"] = "C:/Android/Sdk";
|
|
171
159
|
|
|
172
160
|
execaCalls.length = 0;
|
|
173
161
|
mockFsWriteFile.mockReset();
|
|
@@ -176,6 +164,14 @@ function setupDefaultMocks() {
|
|
|
176
164
|
|
|
177
165
|
//#endregion
|
|
178
166
|
|
|
167
|
+
let savedEnv: Record<string, string | undefined>;
|
|
168
|
+
beforeEach(() => {
|
|
169
|
+
savedEnv = { ...process.env };
|
|
170
|
+
});
|
|
171
|
+
afterEach(() => {
|
|
172
|
+
process.env = savedEnv;
|
|
173
|
+
});
|
|
174
|
+
|
|
179
175
|
describe("Capacitor 빌드", () => {
|
|
180
176
|
beforeEach(() => {
|
|
181
177
|
vi.clearAllMocks();
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import path from "path";
|
|
2
|
-
import { describe, it, expect, vi, beforeEach } from "vitest";
|
|
2
|
+
import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
|
|
3
3
|
|
|
4
4
|
//#region Mocks
|
|
5
5
|
|
|
@@ -34,12 +34,6 @@ vi.mock("@simplysm/core-node", () => ({
|
|
|
34
34
|
},
|
|
35
35
|
}));
|
|
36
36
|
|
|
37
|
-
// env mock
|
|
38
|
-
let mockEnv: Record<string, string | undefined> = {};
|
|
39
|
-
vi.mock("@simplysm/core-common", () => ({
|
|
40
|
-
env: (key: string) => mockEnv[key],
|
|
41
|
-
}));
|
|
42
|
-
|
|
43
37
|
// cpx mock (was execa)
|
|
44
38
|
const execaCalls: { command: string; args: string[] }[] = [];
|
|
45
39
|
const mockCpxSpawn = vi.fn((...args: unknown[]) => {
|
|
@@ -69,19 +63,15 @@ const mockSharpInstance = {
|
|
|
69
63
|
const mockSharp = vi.fn().mockReturnValue(mockSharpInstance);
|
|
70
64
|
vi.mock("sharp", () => ({ default: mockSharp }));
|
|
71
65
|
|
|
72
|
-
// consola mock
|
|
73
|
-
const mockLoggerDebug = vi.fn();
|
|
66
|
+
// consola mock (logger assertion 필요)
|
|
74
67
|
const mockLoggerWarn = vi.fn();
|
|
75
|
-
const
|
|
68
|
+
const _mockConsola = {
|
|
69
|
+
level: 0,
|
|
70
|
+
withTag: () => ({ debug: vi.fn(), warn: mockLoggerWarn, error: vi.fn(), info: vi.fn(), success: vi.fn() }),
|
|
71
|
+
};
|
|
76
72
|
vi.mock("consola", () => ({
|
|
77
|
-
consola:
|
|
78
|
-
|
|
79
|
-
debug: mockLoggerDebug,
|
|
80
|
-
warn: mockLoggerWarn,
|
|
81
|
-
success: mockLoggerSuccess,
|
|
82
|
-
}),
|
|
83
|
-
level: 0,
|
|
84
|
-
},
|
|
73
|
+
consola: _mockConsola,
|
|
74
|
+
default: _mockConsola,
|
|
85
75
|
LogLevels: { debug: 4 },
|
|
86
76
|
}));
|
|
87
77
|
|
|
@@ -149,7 +139,7 @@ function setupDefaultMocks() {
|
|
|
149
139
|
mockFsxGlob.mockResolvedValue(["C:/Program Files/Amazon Corretto/jdk21.0.1"]);
|
|
150
140
|
|
|
151
141
|
// env: Android SDK
|
|
152
|
-
|
|
142
|
+
process.env["ANDROID_HOME"] = "C:/Android/Sdk";
|
|
153
143
|
|
|
154
144
|
// execa 호출 기록 초기화
|
|
155
145
|
execaCalls.length = 0;
|
|
@@ -157,6 +147,14 @@ function setupDefaultMocks() {
|
|
|
157
147
|
|
|
158
148
|
//#endregion
|
|
159
149
|
|
|
150
|
+
let savedEnv: Record<string, string | undefined>;
|
|
151
|
+
beforeEach(() => {
|
|
152
|
+
savedEnv = { ...process.env };
|
|
153
|
+
});
|
|
154
|
+
afterEach(() => {
|
|
155
|
+
process.env = savedEnv;
|
|
156
|
+
});
|
|
157
|
+
|
|
160
158
|
describe("Capacitor 아이콘 처리", () => {
|
|
161
159
|
beforeEach(() => {
|
|
162
160
|
vi.clearAllMocks();
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import path from "path";
|
|
2
|
-
import { describe, it, expect, vi, beforeEach } from "vitest";
|
|
2
|
+
import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
|
|
3
3
|
|
|
4
4
|
//#region Mocks
|
|
5
5
|
|
|
@@ -35,11 +35,6 @@ vi.mock("@simplysm/core-node", () => ({
|
|
|
35
35
|
},
|
|
36
36
|
}));
|
|
37
37
|
|
|
38
|
-
let mockEnv: Record<string, string | undefined> = {};
|
|
39
|
-
vi.mock("@simplysm/core-common", () => ({
|
|
40
|
-
env: (key: string) => mockEnv[key],
|
|
41
|
-
}));
|
|
42
|
-
|
|
43
38
|
const execaCalls: { command: string; args: string[] }[] = [];
|
|
44
39
|
const mockCpxSpawn = vi.fn((...args: unknown[]) => {
|
|
45
40
|
execaCalls.push({ command: args[0] as string, args: (args[1] as string[] | undefined) ?? [] });
|
|
@@ -65,13 +60,14 @@ vi.mock("sharp", () => ({
|
|
|
65
60
|
}),
|
|
66
61
|
}));
|
|
67
62
|
|
|
68
|
-
const mockLoggerDebug = vi.fn();
|
|
69
63
|
const mockLoggerWarn = vi.fn();
|
|
64
|
+
const _mockConsola = {
|
|
65
|
+
level: 0,
|
|
66
|
+
withTag: () => ({ debug: vi.fn(), warn: mockLoggerWarn, error: vi.fn(), info: vi.fn(), success: vi.fn() }),
|
|
67
|
+
};
|
|
70
68
|
vi.mock("consola", () => ({
|
|
71
|
-
consola:
|
|
72
|
-
|
|
73
|
-
withTag: () => ({ debug: mockLoggerDebug, warn: mockLoggerWarn }),
|
|
74
|
-
},
|
|
69
|
+
consola: _mockConsola,
|
|
70
|
+
default: _mockConsola,
|
|
75
71
|
LogLevels: { debug: 4 },
|
|
76
72
|
}));
|
|
77
73
|
|
|
@@ -152,7 +148,7 @@ function setupDefaultMocks() {
|
|
|
152
148
|
|
|
153
149
|
mockFsxGlob.mockResolvedValue(["C:/Program Files/Amazon Corretto/jdk21.0.1"]);
|
|
154
150
|
|
|
155
|
-
|
|
151
|
+
process.env["ANDROID_HOME"] = "C:/Android/Sdk";
|
|
156
152
|
|
|
157
153
|
execaCalls.length = 0;
|
|
158
154
|
mockFsWriteFile.mockReset();
|
|
@@ -161,6 +157,14 @@ function setupDefaultMocks() {
|
|
|
161
157
|
|
|
162
158
|
//#endregion
|
|
163
159
|
|
|
160
|
+
let savedEnv: Record<string, string | undefined>;
|
|
161
|
+
beforeEach(() => {
|
|
162
|
+
savedEnv = { ...process.env };
|
|
163
|
+
});
|
|
164
|
+
afterEach(() => {
|
|
165
|
+
process.env = savedEnv;
|
|
166
|
+
});
|
|
167
|
+
|
|
164
168
|
describe("Capacitor 설정 검증", () => {
|
|
165
169
|
beforeEach(() => {
|
|
166
170
|
vi.clearAllMocks();
|
|
@@ -398,7 +402,7 @@ describe("Android 개발 도구 감지", () => {
|
|
|
398
402
|
});
|
|
399
403
|
|
|
400
404
|
it("Android SDK 미설치 시 에러가 발생한다", async () => {
|
|
401
|
-
|
|
405
|
+
delete process.env["ANDROID_HOME"];
|
|
402
406
|
mockFsxExists.mockImplementation((p: string) => {
|
|
403
407
|
const n = p.replace(/\\/g, "/");
|
|
404
408
|
if (n.includes(".capacitor.lock")) return false;
|
|
@@ -518,7 +522,7 @@ describe("Android 네이티브 설정", () => {
|
|
|
518
522
|
typeof call[0] === "string" &&
|
|
519
523
|
call[0].includes("build.gradle") &&
|
|
520
524
|
typeof call[1] === "string" &&
|
|
521
|
-
call[1].includes("versionCode
|
|
525
|
+
call[1].includes("versionCode 1002003"),
|
|
522
526
|
);
|
|
523
527
|
expect(gradleWrite).toBeDefined();
|
|
524
528
|
});
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import path from "path";
|
|
2
|
-
import { describe, it, expect, vi, beforeEach } from "vitest";
|
|
2
|
+
import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
|
|
3
3
|
|
|
4
4
|
//#region Mocks
|
|
5
5
|
|
|
@@ -36,14 +36,6 @@ vi.mock("@simplysm/core-node", () => ({
|
|
|
36
36
|
},
|
|
37
37
|
}));
|
|
38
38
|
|
|
39
|
-
// env mock
|
|
40
|
-
let mockEnv: Record<string, unknown> = {};
|
|
41
|
-
vi.mock("@simplysm/core-common", () => ({
|
|
42
|
-
env: new Proxy({} as Record<string, unknown>, {
|
|
43
|
-
get: (_target, prop) => mockEnv[prop as string],
|
|
44
|
-
}),
|
|
45
|
-
}));
|
|
46
|
-
|
|
47
39
|
// cpx mock (was execa) — tracks commands and resolves immediately
|
|
48
40
|
const execaCalls: { command: string; args: string[] }[] = [];
|
|
49
41
|
let execaFactory: (...args: unknown[]) => Promise<{ stdout: string; stderr: string; exitCode: number }> = () =>
|
|
@@ -74,20 +66,6 @@ vi.mock("sharp", () => ({
|
|
|
74
66
|
}),
|
|
75
67
|
}));
|
|
76
68
|
|
|
77
|
-
// consola mock
|
|
78
|
-
const mockLoggerDebug = vi.fn();
|
|
79
|
-
const mockLoggerWarn = vi.fn();
|
|
80
|
-
vi.mock("consola", () => ({
|
|
81
|
-
consola: {
|
|
82
|
-
withTag: () => ({
|
|
83
|
-
debug: mockLoggerDebug,
|
|
84
|
-
warn: mockLoggerWarn,
|
|
85
|
-
info: vi.fn(),
|
|
86
|
-
}),
|
|
87
|
-
level: 0,
|
|
88
|
-
},
|
|
89
|
-
LogLevels: { debug: 4 },
|
|
90
|
-
}));
|
|
91
69
|
|
|
92
70
|
//#endregion
|
|
93
71
|
|
|
@@ -141,7 +119,7 @@ export default config;`;
|
|
|
141
119
|
return [];
|
|
142
120
|
});
|
|
143
121
|
|
|
144
|
-
|
|
122
|
+
process.env["ANDROID_HOME"] = "C:/Android/Sdk";
|
|
145
123
|
|
|
146
124
|
execaCalls.length = 0;
|
|
147
125
|
execaFactory = () => Promise.resolve({ stdout: "", stderr: "", exitCode: 0 });
|
|
@@ -151,6 +129,14 @@ export default config;`;
|
|
|
151
129
|
|
|
152
130
|
//#endregion
|
|
153
131
|
|
|
132
|
+
let savedEnv: Record<string, string | undefined>;
|
|
133
|
+
beforeEach(() => {
|
|
134
|
+
savedEnv = { ...process.env };
|
|
135
|
+
});
|
|
136
|
+
afterEach(() => {
|
|
137
|
+
process.env = savedEnv;
|
|
138
|
+
});
|
|
139
|
+
|
|
154
140
|
describe("Capacitor.run()", () => {
|
|
155
141
|
beforeEach(() => {
|
|
156
142
|
vi.clearAllMocks();
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import path from "path";
|
|
2
|
-
import { describe, it, expect, vi, beforeEach } from "vitest";
|
|
2
|
+
import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
|
|
3
3
|
|
|
4
4
|
//#region Mocks
|
|
5
5
|
|
|
@@ -35,11 +35,6 @@ vi.mock("@simplysm/core-node", () => ({
|
|
|
35
35
|
},
|
|
36
36
|
}));
|
|
37
37
|
|
|
38
|
-
let mockEnv: Record<string, string | undefined> = {};
|
|
39
|
-
vi.mock("@simplysm/core-common", () => ({
|
|
40
|
-
env: (key: string) => mockEnv[key],
|
|
41
|
-
}));
|
|
42
|
-
|
|
43
38
|
const execaCalls: { command: string; args: string[] }[] = [];
|
|
44
39
|
const mockCpxSpawn = vi.fn((...args: unknown[]) => {
|
|
45
40
|
execaCalls.push({ command: args[0] as string, args: (args[1] as string[] | undefined) ?? [] });
|
|
@@ -70,12 +65,15 @@ vi.mock("fs/promises", () => ({
|
|
|
70
65
|
symlink: mockSymlink,
|
|
71
66
|
}));
|
|
72
67
|
|
|
73
|
-
vi.mock("
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
68
|
+
vi.mock("module", () => ({
|
|
69
|
+
createRequire: () => ({
|
|
70
|
+
resolve: (id: string) => {
|
|
71
|
+
if (id.includes("capacitor-plugin-auto-update")) {
|
|
72
|
+
return path.resolve("/fake/workspace/packages/capacitor-plugin-auto-update/package.json");
|
|
73
|
+
}
|
|
74
|
+
throw new Error(`Cannot find module '${id}'`);
|
|
75
|
+
},
|
|
76
|
+
}),
|
|
79
77
|
}));
|
|
80
78
|
|
|
81
79
|
//#endregion
|
|
@@ -143,19 +141,27 @@ function setupDefaultMocks() {
|
|
|
143
141
|
});
|
|
144
142
|
|
|
145
143
|
mockFsxGlob.mockResolvedValue(["C:/Program Files/Amazon Corretto/jdk21.0.1"]);
|
|
146
|
-
|
|
144
|
+
process.env["ANDROID_HOME"] = "C:/Android/Sdk";
|
|
147
145
|
execaCalls.length = 0;
|
|
148
146
|
}
|
|
149
147
|
|
|
150
148
|
//#endregion
|
|
151
149
|
|
|
150
|
+
let savedEnv: Record<string, string | undefined>;
|
|
151
|
+
beforeEach(() => {
|
|
152
|
+
savedEnv = { ...process.env };
|
|
153
|
+
});
|
|
154
|
+
afterEach(() => {
|
|
155
|
+
process.env = savedEnv;
|
|
156
|
+
});
|
|
157
|
+
|
|
152
158
|
describe("workspace:* 플러그인 해석", () => {
|
|
153
159
|
beforeEach(() => {
|
|
154
160
|
vi.clearAllMocks();
|
|
155
161
|
setupDefaultMocks();
|
|
156
162
|
});
|
|
157
163
|
|
|
158
|
-
it("workspace:*
|
|
164
|
+
it("workspace:* 플러그인이 link: 프로토콜로 package.json에 등록된다", async () => {
|
|
159
165
|
const { Capacitor } = await import("../../src/capacitor/capacitor.js");
|
|
160
166
|
|
|
161
167
|
const cap = await Capacitor.create(PKG_PATH, {
|
|
@@ -170,7 +176,6 @@ describe("workspace:* 플러그인 해석", () => {
|
|
|
170
176
|
|
|
171
177
|
await cap.initialize();
|
|
172
178
|
|
|
173
|
-
// 1. .capacitor/package.json에 workspace:* 플러그인이 포함되지 않아야 한다
|
|
174
179
|
const writeJsonCalls = mockFsxWriteJson.mock.calls;
|
|
175
180
|
const capPkgWrite = writeJsonCalls.find(
|
|
176
181
|
(call) => typeof call[0] === "string" && call[0].includes("package.json"),
|
|
@@ -180,18 +185,18 @@ describe("workspace:* 플러그인 해석", () => {
|
|
|
180
185
|
string,
|
|
181
186
|
string
|
|
182
187
|
>;
|
|
183
|
-
expect(deps["@simplysm/capacitor-plugin-auto-update"]).toBeUndefined();
|
|
184
188
|
|
|
185
|
-
//
|
|
189
|
+
// 1. workspace:* 플러그인이 link: 프로토콜로 등록되어야 한다
|
|
190
|
+
expect(deps["@simplysm/capacitor-plugin-auto-update"]).toMatch(/^link:/);
|
|
191
|
+
|
|
192
|
+
// 2. link: 경로는 상대경로여야 한다 (절대경로가 아님)
|
|
193
|
+
const linkPath = deps["@simplysm/capacitor-plugin-auto-update"].replace(/^link:/, "");
|
|
194
|
+
expect(linkPath).not.toMatch(/^[A-Z]:|^\//i);
|
|
195
|
+
|
|
196
|
+
// 3. 일반 npm 플러그인은 버전 문자열로 등록되어야 한다
|
|
186
197
|
expect(deps["@capacitor/camera"]).toBe("^7.0.0");
|
|
187
198
|
|
|
188
|
-
//
|
|
189
|
-
expect(mockSymlink).toHaveBeenCalled();
|
|
190
|
-
const symlinkCall = mockSymlink.mock.calls.find(
|
|
191
|
-
(call) =>
|
|
192
|
-
typeof call[1] === "string" &&
|
|
193
|
-
call[1].replace(/\\/g, "/").includes("@simplysm/capacitor-plugin-auto-update"),
|
|
194
|
-
);
|
|
195
|
-
expect(symlinkCall).toBeDefined();
|
|
199
|
+
// 4. 수동 symlink 생성이 호출되지 않아야 한다
|
|
200
|
+
expect(mockSymlink).not.toHaveBeenCalled();
|
|
196
201
|
});
|
|
197
202
|
});
|
|
@@ -9,11 +9,11 @@ const mocks = vi.hoisted(() => ({
|
|
|
9
9
|
discoverWorkspacePackages: vi.fn(),
|
|
10
10
|
}));
|
|
11
11
|
|
|
12
|
-
vi.mock("../../src/
|
|
12
|
+
vi.mock("../../src/orchestrators/TypecheckOrchestrator", () => ({
|
|
13
13
|
executeTypecheck: mocks.executeTypecheck,
|
|
14
14
|
}));
|
|
15
15
|
|
|
16
|
-
vi.mock("../../src/
|
|
16
|
+
vi.mock("../../src/utils/lint-core", () => ({
|
|
17
17
|
executeLint: mocks.executeLint,
|
|
18
18
|
}));
|
|
19
19
|
|