trellis
Version:
Agentic State Engine — event-sourced causal graph with branching, decision traces, and realtime sync for AI-native applications
185 lines (182 loc) • 5.57 kB
JavaScript
import {
__esm
} from "./chunk-2ESYSVXG.js";
// src/vcs/test-manifest.ts
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
import { dirname, join } from "path";
function testManifestPath(rootPath) {
return join(rootPath, MANIFEST_REL);
}
function loadTestManifest(rootPath) {
const path = testManifestPath(rootPath);
if (!existsSync(path)) return null;
try {
const raw = JSON.parse(readFileSync(path, "utf-8"));
if (!raw?.suites || typeof raw.suites !== "object") {
throw new Error('tests.json: missing "suites" object');
}
return raw;
} catch (err) {
const msg = err instanceof Error ? err.message : String(err);
throw new Error(`Invalid ${MANIFEST_REL}: ${msg}`);
}
}
function requireTestManifest(rootPath) {
const manifest = loadTestManifest(rootPath);
if (!manifest) {
throw new Error(
`No test manifest at ${MANIFEST_REL}. Add suites or pass an explicit command on criteria.`
);
}
return manifest;
}
function resolveSuite(manifest, suiteId) {
const suite = manifest.suites[suiteId];
if (!suite?.command) {
const known = Object.keys(manifest.suites).sort().join(", ") || "(none)";
throw new Error(
`Unknown test suite "${suiteId}". Known suites: ${known}`
);
}
return suite;
}
function suiteTimeoutMs(suite) {
return suite.timeoutMs ?? DEFAULT_TIMEOUT_MS;
}
function listSuiteIds(manifest) {
return Object.keys(manifest.suites).sort();
}
function getPromoteRequiredSuites(manifest) {
const fromPromote = manifest.promote?.require?.filter(Boolean) ?? [];
if (fromPromote.length > 0) return fromPromote;
if (manifest.defaultSuite) return [manifest.defaultSuite];
return [];
}
function inferCheckSuiteId(manifest) {
for (const id of listSuiteIds(manifest)) {
const cmd = manifest.suites[id]?.command ?? "";
if (CHECK_CMD_INFER_RE.test(cmd)) return id;
}
return void 0;
}
function inferE2eSuiteId(manifest) {
for (const id of listSuiteIds(manifest)) {
const cmd = manifest.suites[id]?.command ?? "";
if (E2E_CMD_INFER_RE.test(cmd)) return id;
}
return void 0;
}
function resolveReviewLadder(manifest) {
const checkSuiteId = manifest.review?.check ?? inferCheckSuiteId(manifest);
const e2eSuiteId = manifest.review?.e2e ?? inferE2eSuiteId(manifest);
if (!checkSuiteId) {
throw new Error(
`${MANIFEST_REL}: review.check suite missing and no check-like suite found (pnpm check, svelte-check, etc.)`
);
}
if (!e2eSuiteId) {
throw new Error(
`${MANIFEST_REL}: review.e2e suite missing and no e2e-like suite found (test:e2e, playwright)`
);
}
return {
checkSuiteId,
e2eSuiteId,
check: resolveSuite(manifest, checkSuiteId),
e2e: resolveSuite(manifest, e2eSuiteId)
};
}
function resolveCriterionCommand(manifest, criterion) {
if (criterion.command) return criterion.command;
if (!criterion.suite) return void 0;
if (!manifest) {
throw new Error(
`Criterion references suite "${criterion.suite}" but ${MANIFEST_REL} is missing`
);
}
return resolveSuite(manifest, criterion.suite).command;
}
function resolveIssueStartCriteria(manifest, labels) {
if (!manifest) return [];
const seen = /* @__PURE__ */ new Set();
const out = [];
const add = (templates) => {
if (!templates) return;
for (const t of templates) {
const key = `${t.suite ?? ""}\0${t.command ?? ""}\0${t.description}`;
if (seen.has(key)) continue;
seen.add(key);
out.push(t);
}
};
add(manifest.issueStart?.default);
for (const label of labels) {
add(manifest.issueLabels?.[label]);
}
return out;
}
function ensureDefaultTestManifest(rootPath, template = DEFAULT_TEST_MANIFEST) {
const path = testManifestPath(rootPath);
if (existsSync(path)) return false;
mkdirSync(dirname(path), { recursive: true });
writeFileSync(path, `${JSON.stringify(template, null, 2)}
`, "utf-8");
return true;
}
var CHECK_CMD_INFER_RE, E2E_CMD_INFER_RE, MANIFEST_REL, DEFAULT_TIMEOUT_MS, DEFAULT_TEST_MANIFEST;
var init_test_manifest = __esm({
"src/vcs/test-manifest.ts"() {
CHECK_CMD_INFER_RE = /\b(pnpm\s+check|npm\s+run\s+check|svelte-check|vue-tsc)\b/i;
E2E_CMD_INFER_RE = /\b(test:e2e|playwright)\b/i;
MANIFEST_REL = join(".trellis", "tests.json");
DEFAULT_TIMEOUT_MS = 12e4;
DEFAULT_TEST_MANIFEST = {
version: 1,
defaultSuite: "unit",
suites: {
unit: {
description: "Unit tests",
command: "npm test"
},
e2e: {
description: "End-to-end tests",
command: "npx playwright test",
timeoutMs: 6e5
},
"browser-smoke": {
description: "Live-tab smoke via Trellis extension",
kind: "browser",
command: "trellis browser verify browser-smoke",
stepsFile: ".trellis/browser-suites/browser-smoke.json",
timeoutMs: 3e4
}
},
promote: { require: ["unit"] },
issueStart: {
default: [{ description: "Unit tests pass", suite: "unit" }]
},
issueLabels: {
"needs-e2e": [{ description: "E2E suite passes", suite: "e2e" }]
},
review: {
check: "unit",
e2e: "e2e"
}
};
}
});
export {
testManifestPath,
loadTestManifest,
requireTestManifest,
resolveSuite,
suiteTimeoutMs,
listSuiteIds,
getPromoteRequiredSuites,
resolveReviewLadder,
resolveCriterionCommand,
resolveIssueStartCriteria,
DEFAULT_TEST_MANIFEST,
ensureDefaultTestManifest,
init_test_manifest
};