blastproof 0.16.0 → 0.17.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 CHANGED
@@ -77,6 +77,8 @@ Three questions. The first one decides most cases.
77
77
 
78
78
  **A hard requirement, not a preference.** blastproof finds elements the way a screen reader does — by role, by label, by visible text. That is what removes selectors and survives redesigns. The cost is that there is deliberately no CSS or XPath fallback, so anything the accessibility tree cannot describe cannot be driven at all.
79
79
 
80
+ The name is matched **exactly first**, then by substring if nothing matches exactly — so a control named `Add` is found even when `Add New` sits above it. When several controls answer to the same name, the first one on the page wins. Give each control an accessible name no other control on the page shares; a page that cannot offer one cannot be driven unambiguously, and no matching rule fixes that.
81
+
80
82
  | works | cannot be driven |
81
83
  | --- | --- |
82
84
  | `<button>Add to cart</button>` | a `<div>` with a click handler |
package/dist/cli.js CHANGED
@@ -424,13 +424,18 @@ function describeTarget(target) {
424
424
  async function resolveTarget(page, target, resolveTimeoutMs = 2e3) {
425
425
  const candidates = [];
426
426
  if (target.role) {
427
+ if (target.name) {
428
+ candidates.push(page.getByRole(target.role, { name: target.name, exact: true }));
429
+ }
427
430
  candidates.push(page.getByRole(target.role, target.name ? { name: target.name } : {}));
428
431
  }
429
432
  if (target.name) {
433
+ candidates.push(page.getByLabel(target.name, { exact: true }));
430
434
  candidates.push(page.getByLabel(target.name));
431
435
  }
432
436
  const text = target.text ?? target.name;
433
437
  if (text) {
438
+ candidates.push(page.getByText(text, { exact: true }));
434
439
  candidates.push(page.getByText(text));
435
440
  }
436
441
  for (const candidate of candidates) {
@@ -2952,13 +2957,11 @@ async function planCommand(options) {
2952
2957
  written.push(path10.relative(options.cwd, file));
2953
2958
  console.log(` wrote ${path10.relative(options.cwd, file)}`);
2954
2959
  } catch (error) {
2955
- if (error instanceof PlannerError) {
2956
- generated.pop();
2957
- failed.push({ route, reason: error.message });
2958
- console.log(` X failed: ${error.message}`);
2959
- continue;
2960
- }
2961
- throw error;
2960
+ generated.pop();
2961
+ const reason = error instanceof Error ? error.message : String(error);
2962
+ failed.push({ route, reason });
2963
+ console.log(` X failed: ${reason}`);
2964
+ continue;
2962
2965
  }
2963
2966
  } else {
2964
2967
  console.log(`
@@ -3079,7 +3082,7 @@ function parsePositiveNumber(flag) {
3079
3082
  };
3080
3083
  }
3081
3084
  var program = new Command();
3082
- program.name("blastproof").description("Open-source AI testing agent: plain-English YAML tests executed agentically on a real browser.").version("0.16.0");
3085
+ program.name("blastproof").description("Open-source AI testing agent: plain-English YAML tests executed agentically on a real browser.").version("0.17.0");
3083
3086
  program.command("init").description("Scaffold .blastproof/ (config, tests, sample tests) in the current directory").action(async () => {
3084
3087
  try {
3085
3088
  const result = await initProject(process.cwd());