| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199 |
- import { spawn } from 'child_process';
- import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from 'fs';
- import { tmpdir } from 'os';
- import { join } from 'path';
- export interface CliTestProject {
- projectDir: string;
- cleanup: () => void;
- writeFile: (relativePath: string, content: string) => void;
- readFile: (relativePath: string) => string;
- fileExists: (relativePath: string) => boolean;
- runCliCommand: (
- args: string[],
- options?: { expectError?: boolean },
- ) => Promise<{ stdout: string; stderr: string; exitCode: number }>;
- }
- /**
- * Creates a temporary test project for CLI testing
- */
- export function createTestProject(projectName: string = 'test-project'): CliTestProject {
- const projectDir = join(
- tmpdir(),
- 'vendure-cli-e2e',
- `${projectName}-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
- );
- // Create project directory
- mkdirSync(projectDir, { recursive: true });
- // Create basic package.json
- const packageJson = {
- name: projectName,
- version: '1.0.0',
- private: true,
- dependencies: {
- '@vendure/core': '3.3.7',
- '@vendure/common': '3.3.7',
- },
- devDependencies: {
- typescript: '5.8.2',
- },
- };
- writeFileSync(join(projectDir, 'package.json'), JSON.stringify(packageJson, null, 2));
- // Create basic vendure-config.ts
- const vendureConfig = `
- import { VendureConfig, NoopLogger } from '@vendure/core';
- export const config: VendureConfig = {
- apiOptions: {
- port: 3000,
- adminApiPath: 'admin-api',
- shopApiPath: 'shop-api',
- },
- logger: new NoopLogger(),
- authOptions: {
- tokenMethod: ['bearer', 'cookie'],
- superadminCredentials: {
- identifier: 'superadmin',
- password: 'superadmin',
- },
- cookieOptions: {
- secret: 'cookie-secret',
- },
- },
- dbConnectionOptions: {
- type: 'sqlite',
- database: ':memory:',
- synchronize: true,
- },
- };
- `;
- writeFileSync(join(projectDir, 'vendure-config.ts'), vendureConfig);
- return {
- projectDir,
- cleanup: () => {
- try {
- rmSync(projectDir, { recursive: true, force: true });
- } catch (error) {
- // Ignore cleanup errors - use stderr to avoid no-console lint error
- const errorMessage = error instanceof Error ? error.message : String(error);
- process.stderr.write(`Failed to cleanup test project at ${projectDir}: ${errorMessage}\n`);
- }
- },
- writeFile: (relativePath: string, content: string) => {
- const fullPath = join(projectDir, relativePath);
- const dir = join(fullPath, '..');
- mkdirSync(dir, { recursive: true });
- writeFileSync(fullPath, content);
- },
- readFile: (relativePath: string) => {
- return readFileSync(join(projectDir, relativePath), 'utf-8');
- },
- fileExists: (relativePath: string) => {
- return existsSync(join(projectDir, relativePath));
- },
- runCliCommand: async (args: string[], options: { expectError?: boolean } = {}) => {
- return new Promise((resolve, reject) => {
- // Use the built CLI from the dist directory
- const cliPath = join(__dirname, '..', 'dist', 'cli.js');
- const child = spawn('node', [cliPath, ...args], {
- cwd: projectDir,
- env: {
- ...process.env,
- // Ensure we don't inherit any CLI environment variables
- VENDURE_RUNNING_IN_CLI: undefined,
- },
- stdio: ['pipe', 'pipe', 'pipe'],
- });
- let stdout = '';
- let stderr = '';
- child.stdout?.on('data', data => {
- stdout += data.toString();
- });
- child.stderr?.on('data', data => {
- stderr += data.toString();
- });
- child.on('close', code => {
- const exitCode = code ?? 0;
- if (!options.expectError && exitCode !== 0) {
- reject(new Error(`CLI command failed with exit code ${exitCode}. stderr: ${stderr}`));
- } else {
- resolve({ stdout, stderr, exitCode });
- }
- });
- child.on('error', error => {
- reject(error);
- });
- // Send empty stdin to handle any prompts (for non-interactive testing)
- child.stdin?.end();
- });
- },
- };
- }
- /**
- * Creates a tsconfig.json with the problematic patterns that caused the original bug
- */
- export function createProblematicTsConfig(): string {
- return `{
- // TypeScript configuration file with comments
- "compilerOptions": {
- "target": "ES2021",
- "module": "commonjs",
- "lib": ["ES2021"],
- "outDir": "./dist",
- "rootDir": "./src",
- "baseUrl": "./",
- /* Path mappings that contain /* patterns in strings - this was breaking the old regex implementation */
- "paths": {
- "@/vdb/*": ["./node_modules/@vendure/dashboard/src/lib/*"],
- "@/components/*": ["src/components/*"],
- "@/api/*": ["./api/*/index.ts"],
- "@/plugins/*": ["./src/plugins/*/src/index.ts"],
- "special": "path/with/*/wildcards"
- },
- "strict": true,
- "esModuleInterop": true,
- "skipLibCheck": true,
- "forceConsistentCasingInFileNames": true,
- "experimentalDecorators": true,
- "emitDecoratorMetadata": true
- },
- "include": ["src/**/*", "vendure-config.ts"],
- "exclude": ["node_modules", "dist"]
- }`;
- }
- /**
- * Waits for a condition to be true with timeout
- */
- export async function waitFor(
- condition: () => boolean | Promise<boolean>,
- timeoutMs: number = 5000,
- intervalMs: number = 100,
- ): Promise<void> {
- const startTime = Date.now();
- while (Date.now() - startTime < timeoutMs) {
- if (await condition()) {
- return;
- }
- await new Promise(resolve => setTimeout(resolve, intervalMs));
- }
- throw new Error(`Condition not met within ${timeoutMs}ms`);
- }
|