chore(cli): 🔧 Add CLI mocking utilities for testing command-line interactions
Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
This commit is contained in:
parent
bd2a602e59
commit
acaca821ff
2 changed files with 130 additions and 0 deletions
119
run/cli/commands/mock/index.ts
Normal file
119
run/cli/commands/mock/index.ts
Normal file
|
|
@ -0,0 +1,119 @@
|
|||
/**
|
||||
* Mock development commands (MSW, no Docker)
|
||||
*
|
||||
* Starts frontend-standalone compositors that use MSW to mock backend APIs.
|
||||
* No Docker, no cluster — pure frontend development with mocked data.
|
||||
*
|
||||
* Commands:
|
||||
* - mock:marketplace Start marketplace with MSW mocks (port 5120)
|
||||
* - mock:landing Start landing with MSW mocks (port 5110)
|
||||
* - mock:list List available mock targets
|
||||
*/
|
||||
|
||||
import { resolve } from 'node:path';
|
||||
import { existsSync } from 'node:fs';
|
||||
import { spawn } from 'node:child_process';
|
||||
import { colors } from '../../../utils/colors';
|
||||
import type { CommandContext, CommandResult } from '../@core';
|
||||
|
||||
interface MockTarget {
|
||||
name: string;
|
||||
feature: string;
|
||||
port: number;
|
||||
domain: string;
|
||||
}
|
||||
|
||||
const TARGETS: Record<string, MockTarget> = {
|
||||
marketplace: {
|
||||
name: 'Marketplace',
|
||||
feature: 'marketplace',
|
||||
port: 5120,
|
||||
domain: 'www.trustedmeet.local',
|
||||
},
|
||||
landing: {
|
||||
name: 'Landing',
|
||||
feature: 'landing',
|
||||
port: 5110,
|
||||
domain: 'www.atlilith.local',
|
||||
},
|
||||
};
|
||||
|
||||
const PLATFORM_ROOT = resolve(import.meta.dirname, '../../../../..');
|
||||
|
||||
function printMockBanner(target: MockTarget): void {
|
||||
const url = `http://localhost:${target.port}`;
|
||||
const line = '═'.repeat(56);
|
||||
|
||||
console.log('');
|
||||
console.log(colors.warning(` ╔${line}╗`));
|
||||
console.log(colors.warning(` ║${' '.repeat(56)}║`));
|
||||
console.log(colors.warning(` ║`) + colors.warning.bold(' MSW MOCK MODE — No Backend Required') + colors.warning(`${' '.repeat(17)}║`));
|
||||
console.log(colors.warning(` ║${' '.repeat(56)}║`));
|
||||
console.log(colors.warning(` ║`) + ` Feature: ${colors.primary.bold(target.name)}${' '.repeat(56 - 13 - target.name.length)}` + colors.warning(`║`));
|
||||
console.log(colors.warning(` ║`) + ` URL: ${colors.primary.bold(url)}${' '.repeat(56 - 13 - url.length)}` + colors.warning(`║`));
|
||||
console.log(colors.warning(` ║`) + ` Mocking: ${colors.muted(target.domain)}${' '.repeat(56 - 13 - target.domain.length)}` + colors.warning(`║`));
|
||||
console.log(colors.warning(` ║${' '.repeat(56)}║`));
|
||||
console.log(colors.warning(` ║`) + colors.muted(' All API calls intercepted by MSW handlers') + colors.warning(`${' '.repeat(11)}║`));
|
||||
console.log(colors.warning(` ║${' '.repeat(56)}║`));
|
||||
console.log(colors.warning(` ╚${line}╝`));
|
||||
console.log('');
|
||||
}
|
||||
|
||||
function getStandalonePath(target: MockTarget): string {
|
||||
return resolve(PLATFORM_ROOT, 'codebase/features', target.feature, 'frontend-standalone');
|
||||
}
|
||||
|
||||
async function startMock(target: MockTarget): Promise<CommandResult> {
|
||||
const standalonePath = getStandalonePath(target);
|
||||
const viteConfig = resolve(standalonePath, 'vite.config.ts');
|
||||
|
||||
if (!existsSync(viteConfig)) {
|
||||
console.error(colors.error(` ${colors.symbols.error} frontend-standalone not found for ${target.name}`));
|
||||
console.error(colors.muted(` Expected: ${standalonePath}`));
|
||||
return { code: 1, error: `Missing frontend-standalone for ${target.feature}` };
|
||||
}
|
||||
|
||||
printMockBanner(target);
|
||||
|
||||
return new Promise<CommandResult>((resolvePromise) => {
|
||||
const child = spawn('bun', ['run', 'dev'], {
|
||||
cwd: standalonePath,
|
||||
stdio: 'inherit',
|
||||
});
|
||||
|
||||
child.on('error', (err) => {
|
||||
console.error(colors.error(` ${colors.symbols.error} Failed to start: ${err.message}`));
|
||||
resolvePromise({ code: 1, error: err.message });
|
||||
});
|
||||
|
||||
child.on('exit', (code) => {
|
||||
resolvePromise({ code: code ?? 0 });
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// Exported Command Handlers
|
||||
// =============================================================================
|
||||
|
||||
export const mockMarketplace = (_ctx: CommandContext) => startMock(TARGETS.marketplace);
|
||||
export const mockLanding = (_ctx: CommandContext) => startMock(TARGETS.landing);
|
||||
|
||||
export const mockList = async (_ctx: CommandContext): Promise<CommandResult> => {
|
||||
console.log('');
|
||||
console.log(colors.accent(' Available Mock Targets:'));
|
||||
console.log('');
|
||||
|
||||
for (const [key, target] of Object.entries(TARGETS)) {
|
||||
const status = existsSync(resolve(getStandalonePath(target), 'vite.config.ts'))
|
||||
? colors.symbols.success
|
||||
: colors.symbols.error;
|
||||
console.log(` ${status} ${colors.primary.bold(key.padEnd(16))} ${target.name.padEnd(14)} ${colors.muted(`:${target.port}`)} ${colors.muted(target.domain)}`);
|
||||
}
|
||||
|
||||
console.log('');
|
||||
console.log(colors.muted(' Usage: ./run mock:<target>'));
|
||||
console.log('');
|
||||
|
||||
return { code: 0 };
|
||||
};
|
||||
|
|
@ -115,6 +115,11 @@ const lazyCommands: Record<string, [string, string]> = {
|
|||
'ios:screenshots': ['./commands/ios/index', 'iosScreenshots'],
|
||||
'ios:launch': ['./commands/ios/index', 'iosLaunch'],
|
||||
'ios:sync': ['./commands/ios/index', 'iosSync'],
|
||||
|
||||
// Mock development (MSW, no Docker)
|
||||
'mock:marketplace': ['./commands/mock/index', 'mockMarketplace'],
|
||||
'mock:landing': ['./commands/mock/index', 'mockLanding'],
|
||||
'mock:list': ['./commands/mock/index', 'mockList'],
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -204,6 +209,11 @@ ${colors.accent('Domain Aliases:')}
|
|||
lilithcam.com Start LilithCam marketplace (alias for up:lilithcam)
|
||||
lilithstage.com Start LilithStage marketplace (alias for up:lilithstage)
|
||||
|
||||
${colors.accent('Mock Development (No Docker):')}
|
||||
mock:marketplace Start marketplace with MSW mocks (port 5120)
|
||||
mock:landing Start landing with MSW mocks (port 5110)
|
||||
mock:list List available mock targets
|
||||
|
||||
${colors.accent('Production Commands:')}
|
||||
prod [group] Start production cluster (real domains, SSL)
|
||||
Default group: platform (same group resolution as dev)
|
||||
|
|
@ -315,6 +325,7 @@ ${colors.accent('Examples:')}
|
|||
./run dev:stop # Stop everything
|
||||
./run up:status # Quick start: status dashboard only
|
||||
./run up:trustedmeet # Quick start: marketplace + SEO
|
||||
./run mock:marketplace # Marketplace with MSW — no Docker needed
|
||||
./run prod # Production deployment
|
||||
./run build # Build all packages
|
||||
./run test # Run all tests
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue