@friggframework/devtools 2.0.0-next.47 → 2.0.0-next.48
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/frigg-cli/README.md +1290 -0
- package/frigg-cli/__tests__/unit/commands/build.test.js +279 -0
- package/frigg-cli/__tests__/unit/commands/db-setup.test.js +548 -0
- package/frigg-cli/__tests__/unit/commands/deploy.test.js +320 -0
- package/frigg-cli/__tests__/unit/commands/doctor.test.js +309 -0
- package/frigg-cli/__tests__/unit/commands/install.test.js +400 -0
- package/frigg-cli/__tests__/unit/commands/ui.test.js +346 -0
- package/frigg-cli/__tests__/unit/dependencies.test.js +74 -0
- package/frigg-cli/__tests__/unit/utils/database-validator.test.js +366 -0
- package/frigg-cli/__tests__/unit/utils/error-messages.test.js +304 -0
- package/frigg-cli/__tests__/unit/version-detection.test.js +171 -0
- package/frigg-cli/__tests__/utils/mock-factory.js +270 -0
- package/frigg-cli/__tests__/utils/prisma-mock.js +194 -0
- package/frigg-cli/__tests__/utils/test-fixtures.js +463 -0
- package/frigg-cli/__tests__/utils/test-setup.js +287 -0
- package/frigg-cli/build-command/index.js +66 -0
- package/frigg-cli/db-setup-command/index.js +193 -0
- package/frigg-cli/deploy-command/SPEC-DEPLOY-DRY-RUN.md +981 -0
- package/frigg-cli/deploy-command/index.js +302 -0
- package/frigg-cli/doctor-command/index.js +335 -0
- package/frigg-cli/generate-command/__tests__/generate-command.test.js +301 -0
- package/frigg-cli/generate-command/azure-generator.js +43 -0
- package/frigg-cli/generate-command/gcp-generator.js +47 -0
- package/frigg-cli/generate-command/index.js +332 -0
- package/frigg-cli/generate-command/terraform-generator.js +555 -0
- package/frigg-cli/generate-iam-command.js +118 -0
- package/frigg-cli/index.js +173 -0
- package/frigg-cli/index.test.js +158 -0
- package/frigg-cli/init-command/backend-first-handler.js +756 -0
- package/frigg-cli/init-command/index.js +93 -0
- package/frigg-cli/init-command/template-handler.js +143 -0
- package/frigg-cli/install-command/backend-js.js +33 -0
- package/frigg-cli/install-command/commit-changes.js +16 -0
- package/frigg-cli/install-command/environment-variables.js +127 -0
- package/frigg-cli/install-command/environment-variables.test.js +136 -0
- package/frigg-cli/install-command/index.js +54 -0
- package/frigg-cli/install-command/install-package.js +13 -0
- package/frigg-cli/install-command/integration-file.js +30 -0
- package/frigg-cli/install-command/logger.js +12 -0
- package/frigg-cli/install-command/template.js +90 -0
- package/frigg-cli/install-command/validate-package.js +75 -0
- package/frigg-cli/jest.config.js +124 -0
- package/frigg-cli/package.json +63 -0
- package/frigg-cli/repair-command/index.js +564 -0
- package/frigg-cli/start-command/index.js +149 -0
- package/frigg-cli/start-command/start-command.test.js +297 -0
- package/frigg-cli/test/init-command.test.js +180 -0
- package/frigg-cli/test/npm-registry.test.js +319 -0
- package/frigg-cli/ui-command/index.js +154 -0
- package/frigg-cli/utils/app-resolver.js +319 -0
- package/frigg-cli/utils/backend-path.js +25 -0
- package/frigg-cli/utils/database-validator.js +154 -0
- package/frigg-cli/utils/error-messages.js +257 -0
- package/frigg-cli/utils/npm-registry.js +167 -0
- package/frigg-cli/utils/process-manager.js +199 -0
- package/frigg-cli/utils/repo-detection.js +405 -0
- package/infrastructure/create-frigg-infrastructure.js +125 -12
- package/infrastructure/docs/PRE-DEPLOYMENT-HEALTH-CHECK-SPEC.md +1317 -0
- package/infrastructure/domains/shared/resource-discovery.enhanced.test.js +306 -0
- package/infrastructure/domains/shared/resource-discovery.js +31 -2
- package/infrastructure/domains/shared/utilities/base-definition-factory.js +1 -1
- package/infrastructure/domains/shared/utilities/prisma-layer-manager.js +109 -5
- package/infrastructure/domains/shared/utilities/prisma-layer-manager.test.js +310 -4
- package/infrastructure/domains/shared/validation/plugin-validator.js +187 -0
- package/infrastructure/domains/shared/validation/plugin-validator.test.js +323 -0
- package/infrastructure/infrastructure-composer.js +22 -0
- package/layers/prisma/.build-complete +3 -0
- package/package.json +18 -7
- package/management-ui/package-lock.json +0 -16517
|
@@ -0,0 +1,346 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Test suite for ui command
|
|
3
|
+
*
|
|
4
|
+
* Tests the management UI startup functionality including:
|
|
5
|
+
* - Repository detection and discovery
|
|
6
|
+
* - Development mode (backend + frontend servers)
|
|
7
|
+
* - Production mode (integrated server)
|
|
8
|
+
* - Browser opening
|
|
9
|
+
* - Error handling (port conflicts, missing repos)
|
|
10
|
+
* - Process management
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
// Mock dependencies BEFORE requiring modules
|
|
14
|
+
jest.mock('open', () => jest.fn());
|
|
15
|
+
jest.mock('../../../utils/process-manager');
|
|
16
|
+
jest.mock('../../../utils/repo-detection', () => ({
|
|
17
|
+
getCurrentRepositoryInfo: jest.fn(),
|
|
18
|
+
discoverFriggRepositories: jest.fn(),
|
|
19
|
+
promptRepositorySelection: jest.fn(),
|
|
20
|
+
formatRepositoryInfo: jest.fn()
|
|
21
|
+
}));
|
|
22
|
+
jest.mock('fs', () => ({
|
|
23
|
+
existsSync: jest.fn()
|
|
24
|
+
}));
|
|
25
|
+
|
|
26
|
+
// Require after mocks
|
|
27
|
+
const open = require('open');
|
|
28
|
+
const ProcessManager = require('../../../utils/process-manager');
|
|
29
|
+
const {
|
|
30
|
+
getCurrentRepositoryInfo,
|
|
31
|
+
discoverFriggRepositories,
|
|
32
|
+
formatRepositoryInfo
|
|
33
|
+
} = require('../../../utils/repo-detection');
|
|
34
|
+
const { uiCommand } = require('../../../ui-command');
|
|
35
|
+
|
|
36
|
+
describe('CLI Command: ui', () => {
|
|
37
|
+
let consoleLogSpy;
|
|
38
|
+
let consoleErrorSpy;
|
|
39
|
+
let processExitSpy;
|
|
40
|
+
let mockProcessManager;
|
|
41
|
+
let originalEnv;
|
|
42
|
+
let stdinResumeSpy;
|
|
43
|
+
|
|
44
|
+
beforeEach(() => {
|
|
45
|
+
jest.clearAllMocks();
|
|
46
|
+
|
|
47
|
+
// Mock console methods
|
|
48
|
+
consoleLogSpy = jest.spyOn(console, 'log').mockImplementation();
|
|
49
|
+
consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation();
|
|
50
|
+
|
|
51
|
+
// Mock process.exit to prevent actual exit
|
|
52
|
+
processExitSpy = jest.spyOn(process, 'exit').mockImplementation();
|
|
53
|
+
|
|
54
|
+
// Mock process.stdin.resume
|
|
55
|
+
stdinResumeSpy = jest.spyOn(process.stdin, 'resume').mockImplementation();
|
|
56
|
+
|
|
57
|
+
// Store and reset NODE_ENV
|
|
58
|
+
originalEnv = process.env.NODE_ENV;
|
|
59
|
+
delete process.env.NODE_ENV;
|
|
60
|
+
|
|
61
|
+
// Setup ProcessManager mock
|
|
62
|
+
mockProcessManager = {
|
|
63
|
+
spawnProcess: jest.fn(),
|
|
64
|
+
printStatus: jest.fn()
|
|
65
|
+
};
|
|
66
|
+
ProcessManager.mockImplementation(() => mockProcessManager);
|
|
67
|
+
|
|
68
|
+
// Setup default repo-detection mocks
|
|
69
|
+
getCurrentRepositoryInfo.mockResolvedValue({
|
|
70
|
+
path: '/mock/frigg-repo',
|
|
71
|
+
name: 'test-repo',
|
|
72
|
+
currentSubPath: null
|
|
73
|
+
});
|
|
74
|
+
formatRepositoryInfo.mockReturnValue('test-repo');
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
afterEach(() => {
|
|
78
|
+
consoleLogSpy.mockRestore();
|
|
79
|
+
consoleErrorSpy.mockRestore();
|
|
80
|
+
processExitSpy.mockRestore();
|
|
81
|
+
stdinResumeSpy.mockRestore();
|
|
82
|
+
process.env.NODE_ENV = originalEnv;
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
describe('Repository Detection', () => {
|
|
86
|
+
it('should use specified repo path when provided', async () => {
|
|
87
|
+
await uiCommand({ repo: '/custom/repo', port: 3001, open: false });
|
|
88
|
+
|
|
89
|
+
expect(consoleLogSpy).toHaveBeenCalledWith(
|
|
90
|
+
expect.stringContaining('Using specified repository')
|
|
91
|
+
);
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
it('should detect current Frigg repository', async () => {
|
|
95
|
+
await uiCommand({ port: 3001, open: false });
|
|
96
|
+
|
|
97
|
+
expect(getCurrentRepositoryInfo).toHaveBeenCalled();
|
|
98
|
+
expect(consoleLogSpy).toHaveBeenCalledWith(
|
|
99
|
+
expect.stringContaining('Found Frigg repository')
|
|
100
|
+
);
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
it('should show subdirectory when in subpath', async () => {
|
|
104
|
+
getCurrentRepositoryInfo.mockResolvedValue({
|
|
105
|
+
path: '/mock/frigg-repo',
|
|
106
|
+
name: 'test-repo',
|
|
107
|
+
currentSubPath: 'packages/backend'
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
await uiCommand({ port: 3001, open: false });
|
|
111
|
+
|
|
112
|
+
expect(consoleLogSpy).toHaveBeenCalledWith(
|
|
113
|
+
expect.stringContaining('Currently in subdirectory: packages/backend')
|
|
114
|
+
);
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
it('should discover repos when not in Frigg repo', async () => {
|
|
118
|
+
getCurrentRepositoryInfo.mockResolvedValue(null);
|
|
119
|
+
discoverFriggRepositories.mockResolvedValue([
|
|
120
|
+
{ path: '/repos/frigg-1', name: 'frigg-1' },
|
|
121
|
+
{ path: '/repos/frigg-2', name: 'frigg-2' }
|
|
122
|
+
]);
|
|
123
|
+
|
|
124
|
+
await uiCommand({ port: 3001, open: false });
|
|
125
|
+
|
|
126
|
+
expect(discoverFriggRepositories).toHaveBeenCalled();
|
|
127
|
+
expect(consoleLogSpy).toHaveBeenCalledWith(
|
|
128
|
+
expect.stringContaining('Found 2 Frigg repositories')
|
|
129
|
+
);
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
it('should exit when no repos found', async () => {
|
|
133
|
+
getCurrentRepositoryInfo.mockResolvedValue(null);
|
|
134
|
+
discoverFriggRepositories.mockResolvedValue([]);
|
|
135
|
+
|
|
136
|
+
await uiCommand({ port: 3001, open: false });
|
|
137
|
+
|
|
138
|
+
expect(consoleLogSpy).toHaveBeenCalledWith(
|
|
139
|
+
expect.stringContaining('No Frigg repositories found')
|
|
140
|
+
);
|
|
141
|
+
expect(processExitSpy).toHaveBeenCalledWith(1);
|
|
142
|
+
});
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
describe('Development Mode', () => {
|
|
146
|
+
it('should spawn backend and frontend in dev mode', async () => {
|
|
147
|
+
await uiCommand({ port: 3001, open: false, dev: true });
|
|
148
|
+
|
|
149
|
+
expect(mockProcessManager.spawnProcess).toHaveBeenCalledTimes(2);
|
|
150
|
+
expect(mockProcessManager.spawnProcess).toHaveBeenCalledWith(
|
|
151
|
+
'backend',
|
|
152
|
+
'npm',
|
|
153
|
+
['run', 'server'],
|
|
154
|
+
expect.objectContaining({
|
|
155
|
+
cwd: expect.stringContaining('management-ui'),
|
|
156
|
+
env: expect.objectContaining({
|
|
157
|
+
PORT: 3001,
|
|
158
|
+
VITE_API_URL: 'http://localhost:3001'
|
|
159
|
+
})
|
|
160
|
+
})
|
|
161
|
+
);
|
|
162
|
+
expect(mockProcessManager.spawnProcess).toHaveBeenCalledWith(
|
|
163
|
+
'frontend',
|
|
164
|
+
'npm',
|
|
165
|
+
['run', 'dev'],
|
|
166
|
+
expect.any(Object)
|
|
167
|
+
);
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
it('should use default port 3001', async () => {
|
|
171
|
+
await uiCommand({ open: false, dev: true });
|
|
172
|
+
|
|
173
|
+
expect(mockProcessManager.spawnProcess).toHaveBeenCalledWith(
|
|
174
|
+
expect.any(String),
|
|
175
|
+
expect.any(String),
|
|
176
|
+
expect.any(Array),
|
|
177
|
+
expect.objectContaining({
|
|
178
|
+
env: expect.objectContaining({ PORT: 3001 })
|
|
179
|
+
})
|
|
180
|
+
);
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
it('should use custom port when specified', async () => {
|
|
184
|
+
await uiCommand({ port: 4000, open: false, dev: true });
|
|
185
|
+
|
|
186
|
+
expect(mockProcessManager.spawnProcess).toHaveBeenCalledWith(
|
|
187
|
+
expect.any(String),
|
|
188
|
+
expect.any(String),
|
|
189
|
+
expect.any(Array),
|
|
190
|
+
expect.objectContaining({
|
|
191
|
+
env: expect.objectContaining({
|
|
192
|
+
PORT: 4000,
|
|
193
|
+
VITE_API_URL: 'http://localhost:4000'
|
|
194
|
+
})
|
|
195
|
+
})
|
|
196
|
+
);
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
it('should print status with correct URLs', async () => {
|
|
200
|
+
await uiCommand({ port: 3001, open: false, dev: true });
|
|
201
|
+
|
|
202
|
+
// Wait for startup delay (testing async behavior, not mocks)
|
|
203
|
+
await new Promise(resolve => setTimeout(resolve, 2100));
|
|
204
|
+
|
|
205
|
+
// Verify printStatus called with exact URLs
|
|
206
|
+
expect(mockProcessManager.printStatus).toHaveBeenCalledWith(
|
|
207
|
+
'http://localhost:5173',
|
|
208
|
+
'http://localhost:3001',
|
|
209
|
+
'test-repo'
|
|
210
|
+
);
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
it('should open browser when open=true', async () => {
|
|
214
|
+
await uiCommand({ port: 3001, open: true, dev: true });
|
|
215
|
+
|
|
216
|
+
// Wait for startup + browser delay (testing async behavior, not mocks)
|
|
217
|
+
await new Promise(resolve => setTimeout(resolve, 3100));
|
|
218
|
+
|
|
219
|
+
// Verify browser opened with correct URL
|
|
220
|
+
expect(open).toHaveBeenCalledWith('http://localhost:5173');
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
it('should NOT open browser when open=false', async () => {
|
|
224
|
+
await uiCommand({ port: 3001, open: false, dev: true });
|
|
225
|
+
|
|
226
|
+
// Wait for startup delay (testing async behavior, not mocks)
|
|
227
|
+
await new Promise(resolve => setTimeout(resolve, 3100));
|
|
228
|
+
|
|
229
|
+
// Verify browser was not opened
|
|
230
|
+
expect(open).not.toHaveBeenCalled();
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
it('should set PROJECT_ROOT environment variable', async () => {
|
|
234
|
+
getCurrentRepositoryInfo.mockResolvedValue({
|
|
235
|
+
path: '/custom/repo/path',
|
|
236
|
+
name: 'custom-repo',
|
|
237
|
+
currentSubPath: null
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
await uiCommand({ port: 3001, open: false, dev: true });
|
|
241
|
+
|
|
242
|
+
expect(mockProcessManager.spawnProcess).toHaveBeenCalledWith(
|
|
243
|
+
expect.any(String),
|
|
244
|
+
expect.any(String),
|
|
245
|
+
expect.any(Array),
|
|
246
|
+
expect.objectContaining({
|
|
247
|
+
env: expect.objectContaining({
|
|
248
|
+
PROJECT_ROOT: '/custom/repo/path'
|
|
249
|
+
})
|
|
250
|
+
})
|
|
251
|
+
);
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
it('should set REPOSITORY_INFO as JSON string with correct structure', async () => {
|
|
255
|
+
await uiCommand({ port: 3001, open: false, dev: true });
|
|
256
|
+
|
|
257
|
+
const call = mockProcessManager.spawnProcess.mock.calls[0];
|
|
258
|
+
const env = call[3].env;
|
|
259
|
+
|
|
260
|
+
// Verify REPOSITORY_INFO is valid JSON with correct structure
|
|
261
|
+
expect(env.REPOSITORY_INFO).toBeDefined();
|
|
262
|
+
const parsedInfo = JSON.parse(env.REPOSITORY_INFO);
|
|
263
|
+
|
|
264
|
+
// Verify actual data matches mock
|
|
265
|
+
expect(parsedInfo).toEqual({
|
|
266
|
+
path: '/mock/frigg-repo',
|
|
267
|
+
name: 'test-repo',
|
|
268
|
+
currentSubPath: null
|
|
269
|
+
});
|
|
270
|
+
});
|
|
271
|
+
|
|
272
|
+
it('should set AVAILABLE_REPOSITORIES with actual repository data', async () => {
|
|
273
|
+
getCurrentRepositoryInfo.mockResolvedValue(null);
|
|
274
|
+
discoverFriggRepositories.mockResolvedValue([
|
|
275
|
+
{ path: '/repos/frigg-1', name: 'frigg-1' }
|
|
276
|
+
]);
|
|
277
|
+
|
|
278
|
+
await uiCommand({ port: 3001, open: false, dev: true });
|
|
279
|
+
|
|
280
|
+
const call = mockProcessManager.spawnProcess.mock.calls[0];
|
|
281
|
+
const env = call[3].env;
|
|
282
|
+
|
|
283
|
+
// Verify AVAILABLE_REPOSITORIES is valid JSON with correct structure
|
|
284
|
+
expect(env.AVAILABLE_REPOSITORIES).toBeDefined();
|
|
285
|
+
const parsed = JSON.parse(env.AVAILABLE_REPOSITORIES);
|
|
286
|
+
|
|
287
|
+
// Verify actual data matches mock
|
|
288
|
+
expect(parsed).toEqual([
|
|
289
|
+
{ path: '/repos/frigg-1', name: 'frigg-1' }
|
|
290
|
+
]);
|
|
291
|
+
});
|
|
292
|
+
});
|
|
293
|
+
|
|
294
|
+
describe('Error Handling', () => {
|
|
295
|
+
it('should handle EADDRINUSE error', async () => {
|
|
296
|
+
mockProcessManager.spawnProcess.mockImplementation(() => {
|
|
297
|
+
const error = new Error('Port in use');
|
|
298
|
+
error.code = 'EADDRINUSE';
|
|
299
|
+
throw error;
|
|
300
|
+
});
|
|
301
|
+
|
|
302
|
+
await uiCommand({ port: 3001, open: false, dev: true });
|
|
303
|
+
|
|
304
|
+
expect(consoleErrorSpy).toHaveBeenCalledWith(
|
|
305
|
+
expect.stringContaining('Failed to start Management UI'),
|
|
306
|
+
expect.any(String)
|
|
307
|
+
);
|
|
308
|
+
expect(consoleLogSpy).toHaveBeenCalledWith(
|
|
309
|
+
expect.stringContaining('Port 3001 is already in use')
|
|
310
|
+
);
|
|
311
|
+
expect(processExitSpy).toHaveBeenCalledWith(1);
|
|
312
|
+
});
|
|
313
|
+
|
|
314
|
+
it('should handle generic errors', async () => {
|
|
315
|
+
mockProcessManager.spawnProcess.mockImplementation(() => {
|
|
316
|
+
throw new Error('Generic startup error');
|
|
317
|
+
});
|
|
318
|
+
|
|
319
|
+
await uiCommand({ port: 3001, open: false, dev: true });
|
|
320
|
+
|
|
321
|
+
expect(consoleErrorSpy).toHaveBeenCalledWith(
|
|
322
|
+
expect.stringContaining('Failed to start Management UI'),
|
|
323
|
+
'Generic startup error'
|
|
324
|
+
);
|
|
325
|
+
expect(processExitSpy).toHaveBeenCalledWith(1);
|
|
326
|
+
});
|
|
327
|
+
});
|
|
328
|
+
|
|
329
|
+
describe('Process Management', () => {
|
|
330
|
+
it('should create ProcessManager instance', async () => {
|
|
331
|
+
await uiCommand({ port: 3001, open: false, dev: true });
|
|
332
|
+
|
|
333
|
+
expect(ProcessManager).toHaveBeenCalled();
|
|
334
|
+
});
|
|
335
|
+
|
|
336
|
+
it('should keep process running with stdin.resume', async () => {
|
|
337
|
+
await uiCommand({ port: 3001, open: false, dev: true });
|
|
338
|
+
|
|
339
|
+
// Wait for startup delay (testing async behavior, not mocks)
|
|
340
|
+
await new Promise(resolve => setTimeout(resolve, 2100));
|
|
341
|
+
|
|
342
|
+
// Verify stdin.resume called to keep process alive
|
|
343
|
+
expect(stdinResumeSpy).toHaveBeenCalled();
|
|
344
|
+
});
|
|
345
|
+
});
|
|
346
|
+
});
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dependencies Test
|
|
3
|
+
*
|
|
4
|
+
* Ensures that required runtime dependencies are properly declared
|
|
5
|
+
* in package.json to avoid deployment issues.
|
|
6
|
+
*
|
|
7
|
+
* Issue #481 - Missing osls Dependency
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
const path = require('path');
|
|
11
|
+
const packageJson = require('../../package.json');
|
|
12
|
+
|
|
13
|
+
describe('frigg-cli dependencies', () => {
|
|
14
|
+
describe('osls dependency', () => {
|
|
15
|
+
it('should have osls declared as a dependency', () => {
|
|
16
|
+
expect(packageJson.dependencies).toBeDefined();
|
|
17
|
+
expect(packageJson.dependencies.osls).toBeDefined();
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
it('should specify a valid osls version', () => {
|
|
21
|
+
const oslsVersion = packageJson.dependencies.osls;
|
|
22
|
+
|
|
23
|
+
// Should be a semver range or specific version
|
|
24
|
+
expect(oslsVersion).toMatch(/^[\^~]?\d+\.\d+\.\d+/);
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
it('should use osls version 3.40.1 or higher', () => {
|
|
28
|
+
const oslsVersion = packageJson.dependencies.osls;
|
|
29
|
+
|
|
30
|
+
// Extract version number (remove ^ or ~ prefix)
|
|
31
|
+
const versionNumber = oslsVersion.replace(/^[\^~]/, '');
|
|
32
|
+
const [major, minor] = versionNumber.split('.').map(Number);
|
|
33
|
+
|
|
34
|
+
expect(major).toBeGreaterThanOrEqual(3);
|
|
35
|
+
if (major === 3) {
|
|
36
|
+
expect(minor).toBeGreaterThanOrEqual(40);
|
|
37
|
+
}
|
|
38
|
+
});
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
describe('critical runtime dependencies', () => {
|
|
42
|
+
it('should have cross-spawn for spawning osls subprocess', () => {
|
|
43
|
+
expect(packageJson.dependencies['cross-spawn']).toBeDefined();
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it('should have chalk for CLI output formatting', () => {
|
|
47
|
+
expect(packageJson.dependencies.chalk).toBeDefined();
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it('should have commander for CLI command parsing', () => {
|
|
51
|
+
expect(packageJson.dependencies.commander).toBeDefined();
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
it('should have @friggframework/devtools for infrastructure', () => {
|
|
55
|
+
expect(packageJson.dependencies['@friggframework/devtools']).toBeDefined();
|
|
56
|
+
});
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
describe('package.json structure', () => {
|
|
60
|
+
it('should have a bin entry for frigg command', () => {
|
|
61
|
+
expect(packageJson.bin).toBeDefined();
|
|
62
|
+
expect(packageJson.bin.frigg).toBe('./index.js');
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
it('should specify Node.js version requirement', () => {
|
|
66
|
+
expect(packageJson.engines).toBeDefined();
|
|
67
|
+
expect(packageJson.engines.node).toBeDefined();
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
it('should be published as @friggframework/frigg-cli', () => {
|
|
71
|
+
expect(packageJson.name).toBe('@friggframework/frigg-cli');
|
|
72
|
+
});
|
|
73
|
+
});
|
|
74
|
+
});
|