shopify
Version:
A CLI tool to build for the Shopify platform
310 lines (307 loc) • 10.1 kB
JavaScript
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