blastproof 0.18.0 → 0.19.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -1
- package/dist/cli.js +39 -3
- package/dist/cli.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -133,7 +133,7 @@ steps:
|
|
|
133
133
|
- complete checkout
|
|
134
134
|
```
|
|
135
135
|
|
|
136
|
-
`priority` is P0–P2 (default P1). `tags`, `setup` steps and `auth` are optional — `auth: false` runs the test signed out, which a login test needs. `routes` declares the URLs a test covers, which is what `--impacted` selects on; write route strings consistently, since they compare by exact equality (`/cart` ≠ `/cart/`). `run` warns to stderr — non-fatal — when a test declares a route no `routes:` mapping declares, since that route contributes nothing to `--impacted` selection.
|
|
136
|
+
`priority` is P0–P2 (default P1). `tags`, `setup` steps and `auth` are optional — `auth: false` runs the test signed out, which a login test needs, and a selection in which *every* test declares it skips the login altogether. `routes` declares the URLs a test covers, which is what `--impacted` selects on; write route strings consistently, since they compare by exact equality (`/cart` ≠ `/cart/`). `run` warns to stderr — non-fatal — when a test declares a route no `routes:` mapping declares, since that route contributes nothing to `--impacted` selection.
|
|
137
137
|
|
|
138
138
|
### Say what each step should produce
|
|
139
139
|
|
|
@@ -227,6 +227,10 @@ The key is the file glob and the value is the routes it can affect — the oppos
|
|
|
227
227
|
|
|
228
228
|
Every changed file lands in one of three buckets: it matches `routes:` and contributes them, matches `ignore:` and is knowingly irrelevant, or **matches neither — nobody has said what it affects**. `--fail-on-unmapped` blocks on that third case, naming the files and both ways to resolve them.
|
|
229
229
|
|
|
230
|
+
**The diff is `git diff <base>...HEAD`, so it compares commits.** Work you have not committed — edited, staged or untracked — is not in it. That is the right comparison in CI, where everything is committed by the time the workflow runs, and the wrong one on the machine you are writing the change on: a run against a dirty working tree selects from your last commit, not from your editor.
|
|
231
|
+
|
|
232
|
+
It says so rather than leaving you to notice. Any working-tree change the diff excluded is named on stderr, with the routes it maps to, on `run --impacted` and `plan --base` alike — non-fatal, and it changes no exit code and nothing about what is selected. Files your `ignore:` globs already cover stay silent, and so do files already in the diff, whose routes are selected either way.
|
|
233
|
+
|
|
230
234
|
**Nothing is ignored by default**, on purpose: a default that guesses on your behalf would hide the first files worth thinking about. The flag is additive — a run can meet `--min-score` and still be blocked here, because "the tests I ran passed" and "something changed that nobody classified" are different claims.
|
|
231
235
|
|
|
232
236
|
Its limit is worth knowing: it catches files that are *unclassified*, not *misclassified*. A shared module mapped to one route when it can break five still slips through. Impact by import graph is the fix, and blastproof does not do it yet.
|
package/dist/cli.js
CHANGED
|
@@ -1316,6 +1316,20 @@ async function getChangedFiles(baseRef, cwd = process.cwd()) {
|
|
|
1316
1316
|
throw toDiffError(error, baseRef);
|
|
1317
1317
|
}
|
|
1318
1318
|
}
|
|
1319
|
+
async function getUncommittedFiles(cwd = process.cwd()) {
|
|
1320
|
+
try {
|
|
1321
|
+
const status = await simpleGit(cwd).status();
|
|
1322
|
+
const paths = /* @__PURE__ */ new Set();
|
|
1323
|
+
for (const file of status.files) {
|
|
1324
|
+
paths.add(file.path.replace(/\\/g, "/"));
|
|
1325
|
+
const from = file.from;
|
|
1326
|
+
if (from) paths.add(from.replace(/\\/g, "/"));
|
|
1327
|
+
}
|
|
1328
|
+
return [...paths].sort();
|
|
1329
|
+
} catch {
|
|
1330
|
+
return [];
|
|
1331
|
+
}
|
|
1332
|
+
}
|
|
1319
1333
|
|
|
1320
1334
|
// src/impact.ts
|
|
1321
1335
|
import picomatch from "picomatch";
|
|
@@ -1354,6 +1368,25 @@ function mapImpact(changedFiles, routes, ignore = []) {
|
|
|
1354
1368
|
};
|
|
1355
1369
|
}
|
|
1356
1370
|
|
|
1371
|
+
// src/report/uncommitted.ts
|
|
1372
|
+
function printUncommitted(uncommitted, changedFiles, config, baseRef) {
|
|
1373
|
+
const inDiff = new Set(changedFiles.map((file) => file.replace(/\\/g, "/")));
|
|
1374
|
+
const routes = config.routes ?? {};
|
|
1375
|
+
const ignore = config.ignore ?? [];
|
|
1376
|
+
const findings = uncommitted.filter((file) => !inDiff.has(file)).map((file) => ({ file, impact: mapImpact([file], routes, ignore) })).filter(({ impact }) => impact.ignoredFiles.length === 0);
|
|
1377
|
+
if (findings.length === 0) return;
|
|
1378
|
+
console.error(
|
|
1379
|
+
`
|
|
1380
|
+
warning: ${findings.length} file(s) changed in the working tree are not in the diff against '${baseRef}':`
|
|
1381
|
+
);
|
|
1382
|
+
for (const { file, impact } of findings) {
|
|
1383
|
+
console.error(
|
|
1384
|
+
impact.affectedRoutes.length > 0 ? ` ${file} -> ${impact.affectedRoutes.join(", ")}` : ` ${file} (matched by no routes: or ignore: glob)`
|
|
1385
|
+
);
|
|
1386
|
+
}
|
|
1387
|
+
console.error("Nothing above was considered. Commit or stash them to include them.");
|
|
1388
|
+
}
|
|
1389
|
+
|
|
1357
1390
|
// src/llm/brain.ts
|
|
1358
1391
|
import { generateObject } from "ai";
|
|
1359
1392
|
|
|
@@ -2574,7 +2607,8 @@ Dry run: ${selected.length} test(s) selected, base_url=${config.base_url}`);
|
|
|
2574
2607
|
for (const test of selected) {
|
|
2575
2608
|
console.log(` ${test.summary} [${test.priority}] (${path9.relative(cwd, test.path)})`);
|
|
2576
2609
|
}
|
|
2577
|
-
const
|
|
2610
|
+
const needsLogin = selected.some((test) => test.auth);
|
|
2611
|
+
const authSteps = needsLogin ? config.auth?.steps ?? [] : [];
|
|
2578
2612
|
const withAuth = authSteps.length > 0 ? [...selected, { steps: authSteps }] : selected;
|
|
2579
2613
|
const ceiling = estimateMaxModelCalls(
|
|
2580
2614
|
withAuth,
|
|
@@ -2619,6 +2653,7 @@ async function runCommand(options) {
|
|
|
2619
2653
|
throw error;
|
|
2620
2654
|
}
|
|
2621
2655
|
impact = mapImpact(changedFiles, config.routes ?? {}, config.ignore ?? []);
|
|
2656
|
+
printUncommitted(await getUncommittedFiles(options.cwd), changedFiles, config, base);
|
|
2622
2657
|
}
|
|
2623
2658
|
const testsDir = path9.join(options.cwd, TESTS_RELATIVE_DIR);
|
|
2624
2659
|
let files;
|
|
@@ -2711,7 +2746,7 @@ ${results.length} test file(s) could not be parsed:`);
|
|
|
2711
2746
|
let incomplete;
|
|
2712
2747
|
try {
|
|
2713
2748
|
let session;
|
|
2714
|
-
if (config.auth) {
|
|
2749
|
+
if (config.auth && selected.some((test) => test.auth)) {
|
|
2715
2750
|
try {
|
|
2716
2751
|
console.log("Authenticating...");
|
|
2717
2752
|
session = await authenticate({
|
|
@@ -2832,6 +2867,7 @@ async function resolveTargets(options, config, suite) {
|
|
|
2832
2867
|
const base = options.base ?? "main";
|
|
2833
2868
|
const changedFiles = await getChangedFiles(base, options.cwd);
|
|
2834
2869
|
const impact = mapImpact(changedFiles, config.routes ?? {}, config.ignore ?? []);
|
|
2870
|
+
printUncommitted(await getUncommittedFiles(options.cwd), changedFiles, config, base);
|
|
2835
2871
|
const filesForRoute = (route) => changedFiles.filter(
|
|
2836
2872
|
(file) => mapImpact([file], config.routes ?? {}, config.ignore ?? []).affectedRoutes.includes(route)
|
|
2837
2873
|
);
|
|
@@ -3109,7 +3145,7 @@ function parsePositiveNumber(flag) {
|
|
|
3109
3145
|
};
|
|
3110
3146
|
}
|
|
3111
3147
|
var program = new Command();
|
|
3112
|
-
program.name("blastproof").description("Open-source AI testing agent: plain-English YAML tests executed agentically on a real browser.").version("0.
|
|
3148
|
+
program.name("blastproof").description("Open-source AI testing agent: plain-English YAML tests executed agentically on a real browser.").version("0.19.0");
|
|
3113
3149
|
program.command("init").description("Scaffold .blastproof/ (config, tests, sample tests) in the current directory").action(async () => {
|
|
3114
3150
|
try {
|
|
3115
3151
|
const result = await initProject(process.cwd());
|