edsger 0.45.0 → 0.45.1
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/package.json +3 -3
- package/tsconfig.build.json +4 -0
- package/tsconfig.json +3 -9
- package/dist/api/__tests__/app-store.test.d.ts +0 -7
- package/dist/api/__tests__/app-store.test.js +0 -60
- package/dist/api/__tests__/intelligence.test.d.ts +0 -11
- package/dist/api/__tests__/intelligence.test.js +0 -315
- package/dist/api/features/__tests__/feature-utils.test.d.ts +0 -4
- package/dist/api/features/__tests__/feature-utils.test.js +0 -370
- package/dist/api/features/__tests__/status-updater.test.d.ts +0 -4
- package/dist/api/features/__tests__/status-updater.test.js +0 -88
- package/dist/commands/build/__tests__/build.test.d.ts +0 -5
- package/dist/commands/build/__tests__/build.test.js +0 -206
- package/dist/commands/build/__tests__/detect-project.test.d.ts +0 -6
- package/dist/commands/build/__tests__/detect-project.test.js +0 -160
- package/dist/commands/build/__tests__/run-build.test.d.ts +0 -6
- package/dist/commands/build/__tests__/run-build.test.js +0 -433
- package/dist/commands/intelligence/__tests__/command.test.d.ts +0 -4
- package/dist/commands/intelligence/__tests__/command.test.js +0 -48
- package/dist/commands/workflow/core/__tests__/feature-filter.test.d.ts +0 -5
- package/dist/commands/workflow/core/__tests__/feature-filter.test.js +0 -316
- package/dist/commands/workflow/core/__tests__/pipeline-evaluator.test.d.ts +0 -4
- package/dist/commands/workflow/core/__tests__/pipeline-evaluator.test.js +0 -397
- package/dist/commands/workflow/core/__tests__/state-manager.test.d.ts +0 -4
- package/dist/commands/workflow/core/__tests__/state-manager.test.js +0 -384
- package/dist/config/__tests__/config.test.d.ts +0 -4
- package/dist/config/__tests__/config.test.js +0 -286
- package/dist/config/__tests__/feature-status.test.d.ts +0 -4
- package/dist/config/__tests__/feature-status.test.js +0 -111
- package/dist/errors/__tests__/index.test.d.ts +0 -4
- package/dist/errors/__tests__/index.test.js +0 -349
- package/dist/phases/app-store-generation/__tests__/agent.test.d.ts +0 -5
- package/dist/phases/app-store-generation/__tests__/agent.test.js +0 -142
- package/dist/phases/app-store-generation/__tests__/context.test.d.ts +0 -4
- package/dist/phases/app-store-generation/__tests__/context.test.js +0 -284
- package/dist/phases/app-store-generation/__tests__/prompts.test.d.ts +0 -4
- package/dist/phases/app-store-generation/__tests__/prompts.test.js +0 -122
- package/dist/phases/app-store-generation/__tests__/screenshot-composer.test.d.ts +0 -5
- package/dist/phases/app-store-generation/__tests__/screenshot-composer.test.js +0 -826
- package/dist/phases/code-review/__tests__/diff-utils.test.d.ts +0 -1
- package/dist/phases/code-review/__tests__/diff-utils.test.js +0 -101
- package/dist/phases/intelligence-analysis/__tests__/context.test.d.ts +0 -4
- package/dist/phases/intelligence-analysis/__tests__/context.test.js +0 -192
- package/dist/phases/intelligence-analysis/__tests__/matching.test.d.ts +0 -13
- package/dist/phases/intelligence-analysis/__tests__/matching.test.js +0 -154
- package/dist/phases/intelligence-analysis/__tests__/orchestration.test.d.ts +0 -5
- package/dist/phases/intelligence-analysis/__tests__/orchestration.test.js +0 -378
- package/dist/phases/intelligence-analysis/__tests__/prompts.test.d.ts +0 -4
- package/dist/phases/intelligence-analysis/__tests__/prompts.test.js +0 -33
- package/dist/phases/pr-execution/__tests__/file-assigner.test.d.ts +0 -1
- package/dist/phases/pr-execution/__tests__/file-assigner.test.js +0 -303
- package/dist/phases/pr-resolve/__tests__/checklist-learner.test.d.ts +0 -1
- package/dist/phases/pr-resolve/__tests__/checklist-learner.test.js +0 -157
- package/dist/phases/pr-resolve/__tests__/prompts.test.d.ts +0 -1
- package/dist/phases/pr-resolve/__tests__/prompts.test.js +0 -116
- package/dist/phases/pr-resolve/__tests__/resolve-mapping.test.d.ts +0 -1
- package/dist/phases/pr-resolve/__tests__/resolve-mapping.test.js +0 -138
- package/dist/phases/pr-resolve/__tests__/types.test.d.ts +0 -1
- package/dist/phases/pr-resolve/__tests__/types.test.js +0 -43
- package/dist/phases/pr-resolve/__tests__/workspace.test.d.ts +0 -1
- package/dist/phases/pr-resolve/__tests__/workspace.test.js +0 -111
- package/dist/phases/pr-review/__tests__/prompts.test.d.ts +0 -1
- package/dist/phases/pr-review/__tests__/prompts.test.js +0 -49
- package/dist/phases/pr-review/__tests__/review-comments.test.d.ts +0 -1
- package/dist/phases/pr-review/__tests__/review-comments.test.js +0 -110
- package/dist/phases/pr-shared/__tests__/agent-utils.test.d.ts +0 -1
- package/dist/phases/pr-shared/__tests__/agent-utils.test.js +0 -91
- package/dist/phases/pr-shared/__tests__/context.test.d.ts +0 -1
- package/dist/phases/pr-shared/__tests__/context.test.js +0 -94
- package/dist/phases/pr-splitting/__tests__/import-dep-validator.test.d.ts +0 -1
- package/dist/phases/pr-splitting/__tests__/import-dep-validator.test.js +0 -331
- package/dist/phases/release-sync/__tests__/github.test.d.ts +0 -9
- package/dist/phases/release-sync/__tests__/github.test.js +0 -123
- package/dist/phases/release-sync/__tests__/snapshot.test.d.ts +0 -8
- package/dist/phases/release-sync/__tests__/snapshot.test.js +0 -93
- package/dist/phases/smoke-test/__tests__/agent.test.d.ts +0 -4
- package/dist/phases/smoke-test/__tests__/agent.test.js +0 -85
- package/dist/services/coaching/__tests__/coaching-agent.test.d.ts +0 -1
- package/dist/services/coaching/__tests__/coaching-agent.test.js +0 -74
- package/dist/services/coaching/__tests__/coaching-loop.test.d.ts +0 -1
- package/dist/services/coaching/__tests__/coaching-loop.test.js +0 -59
- package/dist/services/coaching/__tests__/self-rating.test.d.ts +0 -1
- package/dist/services/coaching/__tests__/self-rating.test.js +0 -188
- package/dist/services/phase-hooks/__tests__/bindings-fetcher.test.d.ts +0 -1
- package/dist/services/phase-hooks/__tests__/bindings-fetcher.test.js +0 -122
- package/dist/services/phase-hooks/__tests__/hook-executor.test.d.ts +0 -1
- package/dist/services/phase-hooks/__tests__/hook-executor.test.js +0 -321
- package/dist/services/phase-hooks/__tests__/hook-runner.test.d.ts +0 -1
- package/dist/services/phase-hooks/__tests__/hook-runner.test.js +0 -261
- package/dist/services/phase-hooks/__tests__/plugin-loader.test.d.ts +0 -1
- package/dist/services/phase-hooks/__tests__/plugin-loader.test.js +0 -158
- package/dist/services/video/__tests__/video-pipeline.test.d.ts +0 -6
- package/dist/services/video/__tests__/video-pipeline.test.js +0 -249
- package/dist/workspace/__tests__/workspace-manager.test.d.ts +0 -7
- package/dist/workspace/__tests__/workspace-manager.test.js +0 -52
|
@@ -1,433 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Integration tests for the runBuild orchestration function.
|
|
3
|
-
* Uses dependency injection (BuildDeps) to mock external calls
|
|
4
|
-
* while testing the full decision-making pipeline.
|
|
5
|
-
*/
|
|
6
|
-
import assert from 'node:assert';
|
|
7
|
-
import { mkdirSync, rmSync, writeFileSync } from 'node:fs';
|
|
8
|
-
import { tmpdir } from 'node:os';
|
|
9
|
-
import { join } from 'node:path';
|
|
10
|
-
import { afterEach, beforeEach, describe, it } from 'node:test';
|
|
11
|
-
import { runBuild } from '../index.js';
|
|
12
|
-
// ── Helpers ────────────────────────────────────────────────────────
|
|
13
|
-
function createTmpDir() {
|
|
14
|
-
const dir = join(tmpdir(), `edsger-run-build-test-${Date.now()}-${Math.random().toString(36).slice(2)}`);
|
|
15
|
-
mkdirSync(dir, { recursive: true });
|
|
16
|
-
return dir;
|
|
17
|
-
}
|
|
18
|
-
function makeAppleConfig(overrides = {}) {
|
|
19
|
-
return {
|
|
20
|
-
id: 'config-1',
|
|
21
|
-
product_id: 'prod-1',
|
|
22
|
-
store_type: 'apple_app_store',
|
|
23
|
-
credentials: {
|
|
24
|
-
key_id: 'KEY123',
|
|
25
|
-
issuer_id: 'ISSUER456',
|
|
26
|
-
private_key: '-----BEGIN PRIVATE KEY-----\ntest\n-----END PRIVATE KEY-----',
|
|
27
|
-
},
|
|
28
|
-
app_identifier: 'com.example.app',
|
|
29
|
-
listings: {},
|
|
30
|
-
screenshots: {},
|
|
31
|
-
build_config: {},
|
|
32
|
-
current_version: null,
|
|
33
|
-
submission_status: 'none',
|
|
34
|
-
submitted_at: null,
|
|
35
|
-
released_at: null,
|
|
36
|
-
rejection_reason: null,
|
|
37
|
-
is_active: true,
|
|
38
|
-
created_by: 'user-1',
|
|
39
|
-
created_at: '2024-01-01',
|
|
40
|
-
updated_at: '2024-01-01',
|
|
41
|
-
...overrides,
|
|
42
|
-
};
|
|
43
|
-
}
|
|
44
|
-
function makeOptions(overrides = {}) {
|
|
45
|
-
return {
|
|
46
|
-
buildProductId: 'prod-1',
|
|
47
|
-
verbose: false,
|
|
48
|
-
...overrides,
|
|
49
|
-
};
|
|
50
|
-
}
|
|
51
|
-
const DEFAULT_PRODUCT = {
|
|
52
|
-
id: 'prod-1',
|
|
53
|
-
name: 'My App',
|
|
54
|
-
description: 'A test application',
|
|
55
|
-
};
|
|
56
|
-
/** Default mock build plan for a React Native project */
|
|
57
|
-
const DEFAULT_PLAN = {
|
|
58
|
-
projectType: 'react-native',
|
|
59
|
-
installSteps: [
|
|
60
|
-
{ cmd: 'npm', args: ['ci'], cwd: '' },
|
|
61
|
-
{ cmd: 'pod', args: ['install'], cwd: 'ios' },
|
|
62
|
-
],
|
|
63
|
-
projectPath: 'ios/MyApp.xcworkspace',
|
|
64
|
-
schemeHint: 'MyApp',
|
|
65
|
-
reasoning: 'Mock plan',
|
|
66
|
-
};
|
|
67
|
-
function createMockDeps(overrides = {}) {
|
|
68
|
-
const calls = [];
|
|
69
|
-
const savedConfigs = [];
|
|
70
|
-
const tmpDir = overrides.tmpDir ?? createTmpDir();
|
|
71
|
-
// Create a fake .ipa in the export directory so the build flow completes
|
|
72
|
-
const exportDir = join(tmpDir, 'build', 'export');
|
|
73
|
-
mkdirSync(exportDir, { recursive: true });
|
|
74
|
-
writeFileSync(join(exportDir, 'MyApp.ipa'), 'fake-ipa-data');
|
|
75
|
-
// Create the AI-suggested project path so existsSync passes
|
|
76
|
-
mkdirSync(join(tmpDir, 'ios', 'MyApp.xcworkspace'), { recursive: true });
|
|
77
|
-
const deps = {
|
|
78
|
-
fetchConfigs: overrides.fetchConfigs ??
|
|
79
|
-
(async () => overrides.configs ?? [makeAppleConfig()]),
|
|
80
|
-
fetchProduct: overrides.fetchProduct ??
|
|
81
|
-
(async () => overrides.product ?? DEFAULT_PRODUCT),
|
|
82
|
-
fetchGitHub: overrides.fetchGitHub ??
|
|
83
|
-
(async () => ({
|
|
84
|
-
configured: true,
|
|
85
|
-
token: 'ghp_test',
|
|
86
|
-
owner: 'org',
|
|
87
|
-
repo: 'app',
|
|
88
|
-
})),
|
|
89
|
-
cloneRepo: overrides.cloneRepo ?? (() => ({ repoPath: tmpDir })),
|
|
90
|
-
getWorkspaceRoot: overrides.getWorkspaceRoot ?? (() => tmpDir),
|
|
91
|
-
saveBuildConfig: overrides.saveBuildConfig ??
|
|
92
|
-
(async (_id, bc) => {
|
|
93
|
-
savedConfigs.push(bc);
|
|
94
|
-
return null;
|
|
95
|
-
}),
|
|
96
|
-
checkoutDefaultBranch: overrides.checkoutDefaultBranch ?? (() => { }),
|
|
97
|
-
analyzeBuildPlan: overrides.analyzeBuildPlan ?? (async () => DEFAULT_PLAN),
|
|
98
|
-
findProjects: overrides.findProjects ??
|
|
99
|
-
(() => overrides.projects ?? [
|
|
100
|
-
{ path: 'ios/MyApp.xcworkspace', type: 'workspace' },
|
|
101
|
-
]),
|
|
102
|
-
findSchemes: overrides.findSchemes ?? (() => overrides.schemes ?? ['MyApp']),
|
|
103
|
-
runCommand: overrides.runCommand ??
|
|
104
|
-
(async (cmd, args, opts) => {
|
|
105
|
-
calls.push({ cmd, args, opts });
|
|
106
|
-
}),
|
|
107
|
-
isXcodeAvailable: overrides.isXcodeAvailable ?? (() => true),
|
|
108
|
-
};
|
|
109
|
-
return { deps, calls, savedConfigs };
|
|
110
|
-
}
|
|
111
|
-
// ── Tests ──────────────────────────────────────────────────────────
|
|
112
|
-
describe('runBuild orchestration', () => {
|
|
113
|
-
let tmpDir;
|
|
114
|
-
beforeEach(() => {
|
|
115
|
-
tmpDir = createTmpDir();
|
|
116
|
-
});
|
|
117
|
-
afterEach(() => {
|
|
118
|
-
rmSync(tmpDir, { recursive: true, force: true });
|
|
119
|
-
});
|
|
120
|
-
// -- Error paths --
|
|
121
|
-
it('throws when xcodebuild is not available', async () => {
|
|
122
|
-
const { deps } = createMockDeps({
|
|
123
|
-
tmpDir,
|
|
124
|
-
isXcodeAvailable: () => false,
|
|
125
|
-
});
|
|
126
|
-
await assert.rejects(() => runBuild(makeOptions(), deps), (err) => {
|
|
127
|
-
assert.ok(err.message.includes('xcodebuild is not available'));
|
|
128
|
-
return true;
|
|
129
|
-
});
|
|
130
|
-
});
|
|
131
|
-
it('throws when no Apple App Store config exists', async () => {
|
|
132
|
-
const { deps } = createMockDeps({
|
|
133
|
-
tmpDir,
|
|
134
|
-
configs: [],
|
|
135
|
-
});
|
|
136
|
-
await assert.rejects(() => runBuild(makeOptions(), deps), (err) => {
|
|
137
|
-
assert.ok(err.message.includes('No Apple App Store configuration'));
|
|
138
|
-
return true;
|
|
139
|
-
});
|
|
140
|
-
});
|
|
141
|
-
it('throws when GitHub is not configured', async () => {
|
|
142
|
-
const { deps } = createMockDeps({
|
|
143
|
-
tmpDir,
|
|
144
|
-
fetchGitHub: async () => ({
|
|
145
|
-
configured: false,
|
|
146
|
-
message: 'No repo connected',
|
|
147
|
-
}),
|
|
148
|
-
});
|
|
149
|
-
await assert.rejects(() => runBuild(makeOptions(), deps), (err) => {
|
|
150
|
-
assert.ok(err.message.includes('GitHub not configured'));
|
|
151
|
-
return true;
|
|
152
|
-
});
|
|
153
|
-
});
|
|
154
|
-
it('throws when AI plan fails and no projects found in fallback scan', async () => {
|
|
155
|
-
const { deps } = createMockDeps({
|
|
156
|
-
tmpDir,
|
|
157
|
-
analyzeBuildPlan: async () => null,
|
|
158
|
-
projects: [],
|
|
159
|
-
});
|
|
160
|
-
await assert.rejects(() => runBuild(makeOptions(), deps), (err) => {
|
|
161
|
-
assert.ok(err.message.includes('No Xcode projects found'));
|
|
162
|
-
return true;
|
|
163
|
-
});
|
|
164
|
-
});
|
|
165
|
-
it('throws when no schemes found', async () => {
|
|
166
|
-
const { deps } = createMockDeps({
|
|
167
|
-
tmpDir,
|
|
168
|
-
analyzeBuildPlan: async () => ({
|
|
169
|
-
...DEFAULT_PLAN,
|
|
170
|
-
schemeHint: '', // no hint → must discover
|
|
171
|
-
}),
|
|
172
|
-
schemes: [],
|
|
173
|
-
});
|
|
174
|
-
await assert.rejects(() => runBuild(makeOptions(), deps), (err) => {
|
|
175
|
-
assert.ok(err.message.includes('No schemes found'));
|
|
176
|
-
return true;
|
|
177
|
-
});
|
|
178
|
-
});
|
|
179
|
-
// -- AI plan execution --
|
|
180
|
-
it('executes install steps from AI plan in order', async () => {
|
|
181
|
-
const { deps, calls } = createMockDeps({
|
|
182
|
-
tmpDir,
|
|
183
|
-
analyzeBuildPlan: async () => ({
|
|
184
|
-
projectType: 'react-native',
|
|
185
|
-
installSteps: [
|
|
186
|
-
{ cmd: 'npm', args: ['ci'], cwd: '' },
|
|
187
|
-
{ cmd: 'pod', args: ['install'], cwd: 'ios' },
|
|
188
|
-
],
|
|
189
|
-
projectPath: 'ios/MyApp.xcworkspace',
|
|
190
|
-
schemeHint: 'MyApp',
|
|
191
|
-
reasoning: 'React Native project',
|
|
192
|
-
}),
|
|
193
|
-
});
|
|
194
|
-
await runBuild(makeOptions(), deps);
|
|
195
|
-
// Install steps should run before xcodebuild
|
|
196
|
-
const npmIdx = calls.findIndex((c) => c.cmd === 'npm');
|
|
197
|
-
const podIdx = calls.findIndex((c) => c.cmd === 'pod');
|
|
198
|
-
const xcodeIdx = calls.findIndex((c) => c.cmd === 'xcodebuild');
|
|
199
|
-
assert.ok(npmIdx >= 0, 'Expected npm call');
|
|
200
|
-
assert.ok(podIdx >= 0, 'Expected pod call');
|
|
201
|
-
assert.ok(xcodeIdx >= 0, 'Expected xcodebuild call');
|
|
202
|
-
assert.ok(npmIdx < podIdx, 'npm should run before pod');
|
|
203
|
-
assert.ok(podIdx < xcodeIdx, 'pod should run before xcodebuild');
|
|
204
|
-
// Verify cwd
|
|
205
|
-
assert.strictEqual(calls[npmIdx].opts?.cwd, tmpDir);
|
|
206
|
-
assert.strictEqual(calls[podIdx].opts?.cwd, join(tmpDir, 'ios'));
|
|
207
|
-
});
|
|
208
|
-
it('uses AI-suggested project path and scheme', async () => {
|
|
209
|
-
mkdirSync(join(tmpDir, 'apps', 'mobile', 'ios', 'Mobile.xcworkspace'), {
|
|
210
|
-
recursive: true,
|
|
211
|
-
});
|
|
212
|
-
const { deps, calls, savedConfigs } = createMockDeps({
|
|
213
|
-
tmpDir,
|
|
214
|
-
analyzeBuildPlan: async () => ({
|
|
215
|
-
projectType: 'react-native',
|
|
216
|
-
installSteps: [],
|
|
217
|
-
projectPath: 'apps/mobile/ios/Mobile.xcworkspace',
|
|
218
|
-
schemeHint: 'MobileApp',
|
|
219
|
-
reasoning: 'Monorepo — mobile app matches product',
|
|
220
|
-
}),
|
|
221
|
-
});
|
|
222
|
-
await runBuild(makeOptions(), deps);
|
|
223
|
-
const archiveCall = calls.find((c) => c.cmd === 'xcodebuild' && c.args.includes('archive'));
|
|
224
|
-
assert.ok(archiveCall);
|
|
225
|
-
assert.ok(archiveCall.args.some((a) => a.includes('apps/mobile/ios/Mobile.xcworkspace')));
|
|
226
|
-
const schemeIdx = archiveCall.args.indexOf('-scheme');
|
|
227
|
-
assert.strictEqual(archiveCall.args[schemeIdx + 1], 'MobileApp');
|
|
228
|
-
// Verify persisted to DB
|
|
229
|
-
assert.strictEqual(savedConfigs.length, 1);
|
|
230
|
-
assert.strictEqual(savedConfigs[0].project_path, 'apps/mobile/ios/Mobile.xcworkspace');
|
|
231
|
-
assert.strictEqual(savedConfigs[0].scheme, 'MobileApp');
|
|
232
|
-
});
|
|
233
|
-
it('falls back to scan when AI-suggested project path does not exist', async () => {
|
|
234
|
-
const { deps, calls } = createMockDeps({
|
|
235
|
-
tmpDir,
|
|
236
|
-
analyzeBuildPlan: async () => ({
|
|
237
|
-
...DEFAULT_PLAN,
|
|
238
|
-
projectPath: 'nonexistent/App.xcworkspace', // does not exist on disk
|
|
239
|
-
}),
|
|
240
|
-
projects: [{ path: 'ios/MyApp.xcworkspace', type: 'workspace' }],
|
|
241
|
-
});
|
|
242
|
-
await runBuild(makeOptions(), deps);
|
|
243
|
-
const archiveCall = calls.find((c) => c.cmd === 'xcodebuild' && c.args.includes('archive'));
|
|
244
|
-
assert.ok(archiveCall);
|
|
245
|
-
// Should have fallen back to scanned project
|
|
246
|
-
assert.ok(archiveCall.args.some((a) => a.includes('ios/MyApp.xcworkspace')));
|
|
247
|
-
});
|
|
248
|
-
it('discovers scheme via xcodebuild -list when AI has no hint', async () => {
|
|
249
|
-
const { deps, calls } = createMockDeps({
|
|
250
|
-
tmpDir,
|
|
251
|
-
analyzeBuildPlan: async () => ({
|
|
252
|
-
...DEFAULT_PLAN,
|
|
253
|
-
schemeHint: '', // empty → discover
|
|
254
|
-
}),
|
|
255
|
-
schemes: ['DiscoveredScheme'],
|
|
256
|
-
});
|
|
257
|
-
await runBuild(makeOptions(), deps);
|
|
258
|
-
const archiveCall = calls.find((c) => c.cmd === 'xcodebuild' && c.args.includes('archive'));
|
|
259
|
-
assert.ok(archiveCall);
|
|
260
|
-
const schemeIdx = archiveCall.args.indexOf('-scheme');
|
|
261
|
-
assert.strictEqual(archiveCall.args[schemeIdx + 1], 'DiscoveredScheme');
|
|
262
|
-
});
|
|
263
|
-
it('falls back to scan when AI returns null (no plan)', async () => {
|
|
264
|
-
const { deps, calls } = createMockDeps({
|
|
265
|
-
tmpDir,
|
|
266
|
-
analyzeBuildPlan: async () => null,
|
|
267
|
-
projects: [{ path: 'FallbackApp.xcworkspace', type: 'workspace' }],
|
|
268
|
-
schemes: ['FallbackScheme'],
|
|
269
|
-
});
|
|
270
|
-
await runBuild(makeOptions(), deps);
|
|
271
|
-
const archiveCall = calls.find((c) => c.cmd === 'xcodebuild' && c.args.includes('archive'));
|
|
272
|
-
assert.ok(archiveCall);
|
|
273
|
-
assert.ok(archiveCall.args.some((a) => a.includes('FallbackApp.xcworkspace')));
|
|
274
|
-
const schemeIdx = archiveCall.args.indexOf('-scheme');
|
|
275
|
-
assert.strictEqual(archiveCall.args[schemeIdx + 1], 'FallbackScheme');
|
|
276
|
-
});
|
|
277
|
-
it('CLI --scheme overrides AI scheme hint', async () => {
|
|
278
|
-
const { deps, calls } = createMockDeps({ tmpDir });
|
|
279
|
-
await runBuild(makeOptions({ scheme: 'ManualScheme' }), deps);
|
|
280
|
-
const archiveCall = calls.find((c) => c.cmd === 'xcodebuild' && c.args.includes('archive'));
|
|
281
|
-
assert.ok(archiveCall);
|
|
282
|
-
const schemeIdx = archiveCall.args.indexOf('-scheme');
|
|
283
|
-
assert.strictEqual(archiveCall.args[schemeIdx + 1], 'ManualScheme');
|
|
284
|
-
});
|
|
285
|
-
// -- Saved config paths (skip AI) --
|
|
286
|
-
it('skips AI when project_path and scheme are saved', async () => {
|
|
287
|
-
let aiCalled = false;
|
|
288
|
-
const config = makeAppleConfig({
|
|
289
|
-
build_config: { project_path: 'ios/App.xcworkspace', scheme: 'App' },
|
|
290
|
-
});
|
|
291
|
-
const { deps, calls } = createMockDeps({
|
|
292
|
-
tmpDir,
|
|
293
|
-
configs: [config],
|
|
294
|
-
analyzeBuildPlan: async () => {
|
|
295
|
-
aiCalled = true;
|
|
296
|
-
return null;
|
|
297
|
-
},
|
|
298
|
-
});
|
|
299
|
-
await runBuild(makeOptions(), deps);
|
|
300
|
-
assert.ok(!aiCalled, 'AI should NOT be called when config is saved');
|
|
301
|
-
const archiveCall = calls.find((c) => c.cmd === 'xcodebuild' && c.args.includes('archive'));
|
|
302
|
-
assert.ok(archiveCall);
|
|
303
|
-
assert.ok(archiveCall.args.some((a) => a.includes('ios/App.xcworkspace')));
|
|
304
|
-
});
|
|
305
|
-
it('re-runs AI on --reselect even with saved config', async () => {
|
|
306
|
-
let aiCalled = false;
|
|
307
|
-
const config = makeAppleConfig({
|
|
308
|
-
build_config: {
|
|
309
|
-
project_path: 'old/Old.xcworkspace',
|
|
310
|
-
scheme: 'OldScheme',
|
|
311
|
-
configuration: 'Release',
|
|
312
|
-
},
|
|
313
|
-
});
|
|
314
|
-
mkdirSync(join(tmpDir, 'new', 'New.xcworkspace'), { recursive: true });
|
|
315
|
-
const { deps, savedConfigs } = createMockDeps({
|
|
316
|
-
tmpDir,
|
|
317
|
-
configs: [config],
|
|
318
|
-
analyzeBuildPlan: async () => {
|
|
319
|
-
aiCalled = true;
|
|
320
|
-
return {
|
|
321
|
-
projectType: 'native',
|
|
322
|
-
installSteps: [],
|
|
323
|
-
projectPath: 'new/New.xcworkspace',
|
|
324
|
-
schemeHint: 'NewScheme',
|
|
325
|
-
reasoning: 'Redetected',
|
|
326
|
-
};
|
|
327
|
-
},
|
|
328
|
-
});
|
|
329
|
-
await runBuild(makeOptions({ reselect: true }), deps);
|
|
330
|
-
assert.ok(aiCalled, 'AI should be called on --reselect');
|
|
331
|
-
assert.strictEqual(savedConfigs.length, 1);
|
|
332
|
-
assert.strictEqual(savedConfigs[0].project_path, 'new/New.xcworkspace');
|
|
333
|
-
assert.strictEqual(savedConfigs[0].scheme, 'NewScheme');
|
|
334
|
-
});
|
|
335
|
-
it('does not re-save config when nothing changed', async () => {
|
|
336
|
-
const config = makeAppleConfig({
|
|
337
|
-
build_config: {
|
|
338
|
-
project_path: 'ios/MyApp.xcworkspace',
|
|
339
|
-
scheme: 'MyApp',
|
|
340
|
-
configuration: 'Release',
|
|
341
|
-
},
|
|
342
|
-
});
|
|
343
|
-
const { deps, savedConfigs } = createMockDeps({
|
|
344
|
-
tmpDir,
|
|
345
|
-
configs: [config],
|
|
346
|
-
});
|
|
347
|
-
await runBuild(makeOptions(), deps);
|
|
348
|
-
assert.strictEqual(savedConfigs.length, 0);
|
|
349
|
-
});
|
|
350
|
-
// -- Archive / export / upload --
|
|
351
|
-
it('runs full archive + export pipeline', async () => {
|
|
352
|
-
const config = makeAppleConfig({
|
|
353
|
-
build_config: {
|
|
354
|
-
project_path: 'A.xcworkspace',
|
|
355
|
-
scheme: 'A',
|
|
356
|
-
team_id: 'TEAM1',
|
|
357
|
-
export_method: 'app-store',
|
|
358
|
-
},
|
|
359
|
-
});
|
|
360
|
-
const { deps, calls } = createMockDeps({ tmpDir, configs: [config] });
|
|
361
|
-
await runBuild(makeOptions(), deps);
|
|
362
|
-
const xcodeCalls = calls.filter((c) => c.cmd === 'xcodebuild');
|
|
363
|
-
assert.strictEqual(xcodeCalls.length, 2);
|
|
364
|
-
assert.ok(xcodeCalls[0].args.includes('archive'));
|
|
365
|
-
assert.ok(xcodeCalls[1].args.includes('-exportArchive'));
|
|
366
|
-
});
|
|
367
|
-
it('passes --upload to trigger upload after build', async () => {
|
|
368
|
-
const config = makeAppleConfig({
|
|
369
|
-
build_config: {
|
|
370
|
-
project_path: 'A.xcworkspace',
|
|
371
|
-
scheme: 'A',
|
|
372
|
-
configuration: 'Release',
|
|
373
|
-
team_id: 'TEAM1',
|
|
374
|
-
export_method: 'app-store',
|
|
375
|
-
},
|
|
376
|
-
});
|
|
377
|
-
const { deps, calls } = createMockDeps({ tmpDir, configs: [config] });
|
|
378
|
-
await runBuild(makeOptions({ upload: true }), deps);
|
|
379
|
-
const uploadCall = calls.find((c) => c.cmd === 'xcrun' && c.args.includes('altool'));
|
|
380
|
-
assert.ok(uploadCall, 'Expected xcrun altool upload call');
|
|
381
|
-
assert.ok(uploadCall.args.includes('--upload-app'));
|
|
382
|
-
});
|
|
383
|
-
it('uses notarytool for macOS platform upload', async () => {
|
|
384
|
-
const config = makeAppleConfig({
|
|
385
|
-
build_config: {
|
|
386
|
-
project_path: 'A.xcworkspace',
|
|
387
|
-
scheme: 'A',
|
|
388
|
-
configuration: 'Release',
|
|
389
|
-
team_id: 'TEAM1',
|
|
390
|
-
export_method: 'app-store',
|
|
391
|
-
},
|
|
392
|
-
});
|
|
393
|
-
const { deps, calls } = createMockDeps({ tmpDir, configs: [config] });
|
|
394
|
-
await runBuild(makeOptions({ upload: true, platform: 'macos' }), deps);
|
|
395
|
-
const uploadCall = calls.find((c) => c.cmd === 'xcrun' && c.args.includes('notarytool'));
|
|
396
|
-
assert.ok(uploadCall, 'Expected xcrun notarytool call for macOS');
|
|
397
|
-
});
|
|
398
|
-
it('passes platform destination to xcodebuild', async () => {
|
|
399
|
-
const config = makeAppleConfig({
|
|
400
|
-
build_config: { project_path: 'A.xcworkspace', scheme: 'A' },
|
|
401
|
-
});
|
|
402
|
-
const { deps, calls } = createMockDeps({ tmpDir, configs: [config] });
|
|
403
|
-
await runBuild(makeOptions({ platform: 'macos' }), deps);
|
|
404
|
-
const archiveCall = calls.find((c) => c.cmd === 'xcodebuild' && c.args.includes('archive'));
|
|
405
|
-
assert.ok(archiveCall);
|
|
406
|
-
const destIdx = archiveCall.args.indexOf('-destination');
|
|
407
|
-
assert.strictEqual(archiveCall.args[destIdx + 1], 'generic/platform=macOS');
|
|
408
|
-
});
|
|
409
|
-
it('skips archive when --skipArchive is set', async () => {
|
|
410
|
-
const config = makeAppleConfig({
|
|
411
|
-
build_config: { project_path: 'A.xcworkspace', scheme: 'A' },
|
|
412
|
-
});
|
|
413
|
-
const { deps, calls } = createMockDeps({ tmpDir, configs: [config] });
|
|
414
|
-
await runBuild(makeOptions({ skipArchive: true }), deps);
|
|
415
|
-
const xcodeCalls = calls.filter((c) => c.cmd === 'xcodebuild');
|
|
416
|
-
assert.strictEqual(xcodeCalls.length, 0);
|
|
417
|
-
});
|
|
418
|
-
it('adds DEVELOPMENT_TEAM when team_id is set', async () => {
|
|
419
|
-
const config = makeAppleConfig({
|
|
420
|
-
build_config: {
|
|
421
|
-
project_path: 'A.xcworkspace',
|
|
422
|
-
scheme: 'A',
|
|
423
|
-
configuration: 'Release',
|
|
424
|
-
team_id: 'TEAM123',
|
|
425
|
-
},
|
|
426
|
-
});
|
|
427
|
-
const { deps, calls } = createMockDeps({ tmpDir, configs: [config] });
|
|
428
|
-
await runBuild(makeOptions(), deps);
|
|
429
|
-
const archiveCall = calls.find((c) => c.cmd === 'xcodebuild' && c.args.includes('archive'));
|
|
430
|
-
assert.ok(archiveCall);
|
|
431
|
-
assert.ok(archiveCall.args.some((a) => a === 'DEVELOPMENT_TEAM=TEAM123'));
|
|
432
|
-
});
|
|
433
|
-
});
|
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Unit tests for intelligence command display helpers.
|
|
3
|
-
*/
|
|
4
|
-
import assert from 'node:assert';
|
|
5
|
-
import { describe, it } from 'node:test';
|
|
6
|
-
// ============================================================
|
|
7
|
-
// IntelCommandOptions type validation
|
|
8
|
-
// ============================================================
|
|
9
|
-
void describe('IntelCommandOptions', () => {
|
|
10
|
-
void it('should accept minimal options', () => {
|
|
11
|
-
const opts = { productId: 'prod-001' };
|
|
12
|
-
assert.strictEqual(opts.productId, 'prod-001');
|
|
13
|
-
assert.strictEqual(opts.reportType, undefined);
|
|
14
|
-
assert.strictEqual(opts.verbose, undefined);
|
|
15
|
-
});
|
|
16
|
-
void it('should accept full options', () => {
|
|
17
|
-
const opts = {
|
|
18
|
-
productId: 'prod-001',
|
|
19
|
-
reportType: 'competitive',
|
|
20
|
-
verbose: true,
|
|
21
|
-
guidance: 'Focus on pricing',
|
|
22
|
-
reportId: 'report-1',
|
|
23
|
-
listCompetitors: false,
|
|
24
|
-
confirmCompetitor: undefined,
|
|
25
|
-
dismissCompetitor: undefined,
|
|
26
|
-
};
|
|
27
|
-
assert.strictEqual(opts.productId, 'prod-001');
|
|
28
|
-
assert.strictEqual(opts.reportType, 'competitive');
|
|
29
|
-
assert.strictEqual(opts.guidance, 'Focus on pricing');
|
|
30
|
-
});
|
|
31
|
-
void it('should accept competitor management options', () => {
|
|
32
|
-
const listOpts = {
|
|
33
|
-
productId: 'prod-001',
|
|
34
|
-
listCompetitors: true,
|
|
35
|
-
};
|
|
36
|
-
assert.strictEqual(listOpts.listCompetitors, true);
|
|
37
|
-
const confirmOpts = {
|
|
38
|
-
productId: 'prod-001',
|
|
39
|
-
confirmCompetitor: 'comp-123',
|
|
40
|
-
};
|
|
41
|
-
assert.strictEqual(confirmOpts.confirmCompetitor, 'comp-123');
|
|
42
|
-
const dismissOpts = {
|
|
43
|
-
productId: 'prod-001',
|
|
44
|
-
dismissCompetitor: 'comp-456',
|
|
45
|
-
};
|
|
46
|
-
assert.strictEqual(dismissOpts.dismissCompetitor, 'comp-456');
|
|
47
|
-
});
|
|
48
|
-
});
|