UNPKG

shopify

Version:

A CLI tool to build for the Shopify platform

310 lines (307 loc) • 10.1 kB
import { captureCommandWithExitCode, execCommand, fileExists, readFile } from "./chunk-E4GUDBAR.js"; import { isAbsolutePath, joinPath, relativePath } from "./chunk-EG6MBBEN.js"; import { init_cjs_shims } from "./chunk-PKR7KJ6P.js"; // ../cli-kit/dist/public/node/doctor/framework.js init_cjs_shims(); var DoctorSuite = class { constructor() { this.assertions = [], this.registeredTests = []; } /** * Run the entire test suite. * * @param context - The doctor context for this suite run. */ async runSuite(context) { this.context = context, this.registeredTests = []; let results = []; this.tests(); for (let registeredTest of this.registeredTests) { this.assertions = []; let startTime = Date.now(); try { await registeredTest.fn(), results.push({ name: registeredTest.name, status: this.hasFailures() ? "failed" : "passed", duration: Date.now() - startTime, assertions: [...this.assertions] }); } catch (error) { results.push({ name: registeredTest.name, status: "failed", duration: Date.now() - startTime, assertions: [...this.assertions], error: error instanceof Error ? error : new Error(String(error)) }); } } return results; } /** * Register a test with a name and function. * * @param name - The test name. * @param fn - The async test function. */ test(name, fn) { this.registeredTests.push({ name, fn }); } /** * Override this method to register tests using this.test(). */ tests() { } // ============================================ // Command execution // ============================================ /** * Run a CLI command and return the result. * * @param command - The CLI command to run. * @param options - Optional cwd and env overrides. * @example * const result = await this.run('shopify theme init my-theme') * const result = await this.run('shopify theme push --json') */ async run(command, options) { let cwd = options?.cwd ?? this.context.workingDirectory, result = await captureCommandWithExitCode(command, { cwd, env: options?.env }); return { command, exitCode: result.exitCode, stdout: result.stdout, stderr: result.stderr, output: String(result.stdout) + String(result.stderr), success: result.exitCode === 0 }; } /** * Run a command without capturing output (for interactive commands). * Returns only success/failure. * * @param command - The CLI command to run. * @param options - Optional cwd and env overrides. */ async runInteractive(command, options) { let cwd = options?.cwd ?? this.context.workingDirectory, exitCode = 0; try { await execCommand(command, { cwd, env: options?.env, stdin: "inherit" }); } catch { exitCode = 1; } return { command, exitCode, stdout: "", stderr: "", output: "", success: exitCode === 0 }; } // ============================================ // Assertions // ============================================ /** * Assert that a command succeeded (exit code 0). * * @param result - The command result to check. * @param message - Optional custom assertion message. */ assertSuccess(result, message) { this.assertions.push({ description: message ?? `Command succeeded: ${result.command}`, passed: result.success, expected: "exit code 0", actual: `exit code ${result.exitCode}` }); } /** * Assert that a command failed with an error matching the pattern. * * @param result - The command result to check. * @param pattern - Optional regex or string pattern to match against output. * @param message - Optional custom assertion message. */ assertError(result, pattern, message) { let failed = !result.success; if (pattern) { let matches = (typeof pattern == "string" ? new RegExp(pattern) : pattern).test(result.output), actualValue; failed ? matches ? actualValue = "matched" : actualValue = `output: ${result.output.slice(0, 200)}` : actualValue = "command succeeded", this.assertions.push({ description: message ?? `Command failed with expected error: ${pattern}`, passed: failed && matches, expected: `failure with error matching ${pattern}`, actual: actualValue }); } else this.assertions.push({ description: message ?? `Command failed: ${result.command}`, passed: failed, expected: "non-zero exit code", actual: `exit code ${result.exitCode}` }); } /** * Assert that a file exists and optionally matches content. * * @param path - The file path to check. * @param contentPattern - Optional regex or string to match file content. * @param message - Optional custom assertion message. */ async assertFile(path, contentPattern, message) { let fullPath = isAbsolutePath(path) ? path : joinPath(this.context.workingDirectory, path), displayPath = relativePath(this.context.workingDirectory, fullPath); if (!await fileExists(fullPath)) { this.assertions.push({ description: message ?? `File exists: ${displayPath}`, passed: !1, expected: "file exists", actual: "file not found" }); return; } if (contentPattern) { let content = await readFile(fullPath), matches = (typeof contentPattern == "string" ? new RegExp(contentPattern) : contentPattern).test(content); this.assertions.push({ description: message ?? `File ${displayPath} matches ${contentPattern}`, passed: matches, expected: `content matching ${contentPattern}`, actual: matches ? "matched" : `content: ${content.slice(0, 200)}...` }); } else this.assertions.push({ description: message ?? `File exists: ${displayPath}`, passed: !0, expected: "file exists", actual: "file exists" }); } /** * Assert that a file does not exist. * * @param path - The file path to check. * @param message - Optional custom assertion message. */ async assertNoFile(path, message) { let fullPath = isAbsolutePath(path) ? path : joinPath(this.context.workingDirectory, path), displayPath = relativePath(this.context.workingDirectory, fullPath), exists = await fileExists(fullPath); this.assertions.push({ description: message ?? `File does not exist: ${displayPath}`, passed: !exists, expected: "file does not exist", actual: exists ? "file exists" : "file does not exist" }); } /** * Assert that a directory exists. * * @param path - The directory path to check. * @param message - Optional custom assertion message. */ async assertDirectory(path, message) { let fullPath = isAbsolutePath(path) ? path : joinPath(this.context.workingDirectory, path), displayPath = relativePath(this.context.workingDirectory, fullPath), exists = await fileExists(fullPath); this.assertions.push({ description: message ?? `Directory exists: ${displayPath}`, passed: exists, expected: "directory exists", actual: exists ? "directory exists" : "directory not found" }); } /** * Assert that output contains a pattern. * * @param result - The command result to check. * @param pattern - Regex or string pattern to match against output. * @param message - Optional custom assertion message. */ assertOutput(result, pattern, message) { let matches = (typeof pattern == "string" ? new RegExp(pattern) : pattern).test(result.output); this.assertions.push({ description: message ?? `Output matches ${pattern}`, passed: matches, expected: `output matching ${pattern}`, actual: matches ? "matched" : `output: ${result.output.slice(0, 200)}` }); } /** * Assert that output contains valid JSON and optionally validate it. * * @param result - The command result to parse. * @param validator - Optional function to validate the parsed JSON. * @param message - Optional custom assertion message. */ assertJson(result, validator, message) { try { let json = JSON.parse(result.stdout); if (validator) { let valid = validator(json); this.assertions.push({ description: message ?? "Output is valid JSON matching validator", passed: valid, expected: "valid JSON matching validator", actual: valid ? "matched" : "validator returned false" }); } else this.assertions.push({ description: message ?? "Output is valid JSON", passed: !0, expected: "valid JSON", actual: "valid JSON" }); return json; } catch { this.assertions.push({ description: message ?? "Output is valid JSON", passed: !1, expected: "valid JSON", actual: `invalid JSON: ${result.stdout.slice(0, 100)}` }); return; } } /** * Assert a boolean condition. * * @param condition - The boolean condition to assert. * @param message - The assertion description. */ assert(condition, message) { this.assertions.push({ description: message, passed: condition, expected: "true", actual: String(condition) }); } /** * Assert two values are equal. * * @param actual - The actual value. * @param expected - The expected value. * @param message - The assertion description. */ assertEqual(actual, expected, message) { this.assertions.push({ description: message, passed: actual === expected, expected: String(expected), actual: String(actual) }); } hasFailures() { return this.assertions.some((assertion) => !assertion.passed); } }; DoctorSuite.description = "Doctor test suite"; export { DoctorSuite }; //# sourceMappingURL=chunk-OIUDJFV2.js.map