@hasna/testers 0.0.38 → 0.0.39

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/dist/cli/index.js CHANGED
@@ -16697,12 +16697,19 @@ var init_personas = __esm(() => {
16697
16697
  // src/lib/screenshotter.ts
16698
16698
  import { mkdirSync as mkdirSync7, existsSync as existsSync8, writeFileSync as writeFileSync2 } from "fs";
16699
16699
  import { join as join10 } from "path";
16700
- function slugify(text) {
16701
- return text.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
16700
+ function truncateSlug(slug, maxLength) {
16701
+ if (slug.length <= maxLength)
16702
+ return slug;
16703
+ const truncated = slug.slice(0, maxLength).replace(/-+$/g, "");
16704
+ return truncated || slug.slice(0, maxLength);
16705
+ }
16706
+ function slugify(text, maxLength) {
16707
+ const slug = text.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
16708
+ return maxLength ? truncateSlug(slug, maxLength) : slug;
16702
16709
  }
16703
16710
  function generateFilename(stepNumber, action) {
16704
16711
  const padded = String(stepNumber).padStart(3, "0");
16705
- const slug = slugify(action);
16712
+ const slug = slugify(action, MAX_ACTION_SLUG_LENGTH);
16706
16713
  return `${padded}_${slug}.png`;
16707
16714
  }
16708
16715
  function formatDate(date) {
@@ -16716,7 +16723,8 @@ function getScreenshotDir(baseDir, runId, scenarioSlug, projectName, timestamp)
16716
16723
  const project = projectName ?? "default";
16717
16724
  const dateDir = formatDate(now2);
16718
16725
  const timeDir = `${formatTime(now2)}_${runId.slice(0, 8)}`;
16719
- return join10(baseDir, project, dateDir, timeDir, scenarioSlug);
16726
+ const safeScenarioSlug = slugify(scenarioSlug, MAX_SCENARIO_SLUG_LENGTH) || "scenario";
16727
+ return join10(baseDir, project, dateDir, timeDir, safeScenarioSlug);
16720
16728
  }
16721
16729
  function ensureDir(dirPath) {
16722
16730
  if (!existsSync8(dirPath)) {
@@ -16874,7 +16882,7 @@ class Screenshotter {
16874
16882
  };
16875
16883
  }
16876
16884
  }
16877
- var DEFAULT_BASE_DIR;
16885
+ var MAX_ACTION_SLUG_LENGTH = 80, MAX_SCENARIO_SLUG_LENGTH = 96, DEFAULT_BASE_DIR;
16878
16886
  var init_screenshotter = __esm(() => {
16879
16887
  init_paths();
16880
16888
  DEFAULT_BASE_DIR = join10(getTestersDir(), "screenshots");
@@ -93989,7 +93997,7 @@ import chalk6 from "chalk";
93989
93997
  // package.json
93990
93998
  var package_default = {
93991
93999
  name: "@hasna/testers",
93992
- version: "0.0.38",
94000
+ version: "0.0.39",
93993
94001
  description: "AI-powered QA testing CLI \u2014 spawns cheap AI agents to test web apps with headless browsers",
93994
94002
  type: "module",
93995
94003
  main: "dist/index.js",
package/dist/index.js CHANGED
@@ -13880,12 +13880,21 @@ init_browser_lightpanda();
13880
13880
  init_paths();
13881
13881
  import { mkdirSync as mkdirSync7, existsSync as existsSync8, writeFileSync as writeFileSync2 } from "fs";
13882
13882
  import { join as join10 } from "path";
13883
- function slugify(text) {
13884
- return text.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
13883
+ var MAX_ACTION_SLUG_LENGTH = 80;
13884
+ var MAX_SCENARIO_SLUG_LENGTH = 96;
13885
+ function truncateSlug(slug, maxLength) {
13886
+ if (slug.length <= maxLength)
13887
+ return slug;
13888
+ const truncated = slug.slice(0, maxLength).replace(/-+$/g, "");
13889
+ return truncated || slug.slice(0, maxLength);
13890
+ }
13891
+ function slugify(text, maxLength) {
13892
+ const slug = text.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
13893
+ return maxLength ? truncateSlug(slug, maxLength) : slug;
13885
13894
  }
13886
13895
  function generateFilename(stepNumber, action) {
13887
13896
  const padded = String(stepNumber).padStart(3, "0");
13888
- const slug = slugify(action);
13897
+ const slug = slugify(action, MAX_ACTION_SLUG_LENGTH);
13889
13898
  return `${padded}_${slug}.png`;
13890
13899
  }
13891
13900
  function formatDate(date) {
@@ -13899,7 +13908,8 @@ function getScreenshotDir(baseDir, runId, scenarioSlug, projectName, timestamp)
13899
13908
  const project = projectName ?? "default";
13900
13909
  const dateDir = formatDate(now2);
13901
13910
  const timeDir = `${formatTime(now2)}_${runId.slice(0, 8)}`;
13902
- return join10(baseDir, project, dateDir, timeDir, scenarioSlug);
13911
+ const safeScenarioSlug = slugify(scenarioSlug, MAX_SCENARIO_SLUG_LENGTH) || "scenario";
13912
+ return join10(baseDir, project, dateDir, timeDir, safeScenarioSlug);
13903
13913
  }
13904
13914
  function ensureDir(dirPath) {
13905
13915
  if (!existsSync8(dirPath)) {
@@ -1,5 +1,5 @@
1
1
  import type { Page } from "playwright";
2
- export declare function slugify(text: string): string;
2
+ export declare function slugify(text: string, maxLength?: number): string;
3
3
  export declare function generateFilename(stepNumber: number, action: string): string;
4
4
  /**
5
5
  * Build the screenshot directory for a run:
@@ -1 +1 @@
1
- {"version":3,"file":"screenshotter.d.ts","sourceRoot":"","sources":["../../src/lib/screenshotter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAOvC,wBAAgB,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAK5C;AAED,wBAAgB,gBAAgB,CAAC,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAI3E;AAUD;;;GAGG;AACH,wBAAgB,gBAAgB,CAC9B,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,MAAM,EACb,YAAY,EAAE,MAAM,EACpB,WAAW,CAAC,EAAE,MAAM,EACpB,SAAS,CAAC,EAAE,IAAI,GACf,MAAM,CAMR;AAED,wBAAgB,SAAS,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAI/C;AAID,UAAU,oBAAoB;IAC5B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,KAAK,GAAG,MAAM,CAAC;IACxB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,UAAU,cAAc;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;CAC9B;AAuBD,wBAAgB,YAAY,CAC1B,GAAG,EAAE,MAAM,EACX,IAAI,EAAE;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,aAAa,EAAE,MAAM,CAAA;CAAE,GAC5G,IAAI,CAON;AAED,wBAAgB,iBAAiB,CAC/B,GAAG,EAAE,MAAM,EACX,IAAI,EAAE;IAAE,UAAU,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,GACxH,IAAI,CAON;AAkCD,qBAAa,aAAa;IACxB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAiB;IACxC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAU;IACnC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAS;IACrC,OAAO,CAAC,YAAY,CAAO;gBAEf,OAAO,GAAE,oBAAyB;IASxC,OAAO,CAAC,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,aAAa,CAAC;IAmDpE,eAAe,CAAC,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,aAAa,CAAC;IAiD5E,cAAc,CAAC,IAAI,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,aAAa,CAAC;CA6CpG"}
1
+ {"version":3,"file":"screenshotter.d.ts","sourceRoot":"","sources":["../../src/lib/screenshotter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAgBvC,wBAAgB,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,MAAM,CAMhE;AAED,wBAAgB,gBAAgB,CAAC,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAI3E;AAUD;;;GAGG;AACH,wBAAgB,gBAAgB,CAC9B,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,MAAM,EACb,YAAY,EAAE,MAAM,EACpB,WAAW,CAAC,EAAE,MAAM,EACpB,SAAS,CAAC,EAAE,IAAI,GACf,MAAM,CAOR;AAED,wBAAgB,SAAS,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAI/C;AAID,UAAU,oBAAoB;IAC5B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,KAAK,GAAG,MAAM,CAAC;IACxB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,UAAU,cAAc;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;CAC9B;AAuBD,wBAAgB,YAAY,CAC1B,GAAG,EAAE,MAAM,EACX,IAAI,EAAE;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,aAAa,EAAE,MAAM,CAAA;CAAE,GAC5G,IAAI,CAON;AAED,wBAAgB,iBAAiB,CAC/B,GAAG,EAAE,MAAM,EACX,IAAI,EAAE;IAAE,UAAU,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,GACxH,IAAI,CAON;AAkCD,qBAAa,aAAa;IACxB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAiB;IACxC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAU;IACnC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAS;IACrC,OAAO,CAAC,YAAY,CAAO;gBAEf,OAAO,GAAE,oBAAyB;IASxC,OAAO,CAAC,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,aAAa,CAAC;IAmDpE,eAAe,CAAC,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,aAAa,CAAC;IAiD5E,cAAc,CAAC,IAAI,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,aAAa,CAAC;CA6CpG"}
package/dist/mcp/index.js CHANGED
@@ -52,7 +52,7 @@ var package_default;
52
52
  var init_package = __esm(() => {
53
53
  package_default = {
54
54
  name: "@hasna/testers",
55
- version: "0.0.38",
55
+ version: "0.0.39",
56
56
  description: "AI-powered QA testing CLI \u2014 spawns cheap AI agents to test web apps with headless browsers",
57
57
  type: "module",
58
58
  main: "dist/index.js",
@@ -19805,12 +19805,19 @@ var init_personas = __esm(() => {
19805
19805
  // src/lib/screenshotter.ts
19806
19806
  import { mkdirSync as mkdirSync7, existsSync as existsSync8, writeFileSync as writeFileSync2 } from "fs";
19807
19807
  import { join as join10 } from "path";
19808
- function slugify(text) {
19809
- return text.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
19808
+ function truncateSlug(slug, maxLength) {
19809
+ if (slug.length <= maxLength)
19810
+ return slug;
19811
+ const truncated = slug.slice(0, maxLength).replace(/-+$/g, "");
19812
+ return truncated || slug.slice(0, maxLength);
19813
+ }
19814
+ function slugify(text, maxLength) {
19815
+ const slug = text.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
19816
+ return maxLength ? truncateSlug(slug, maxLength) : slug;
19810
19817
  }
19811
19818
  function generateFilename(stepNumber, action) {
19812
19819
  const padded = String(stepNumber).padStart(3, "0");
19813
- const slug = slugify(action);
19820
+ const slug = slugify(action, MAX_ACTION_SLUG_LENGTH);
19814
19821
  return `${padded}_${slug}.png`;
19815
19822
  }
19816
19823
  function formatDate(date) {
@@ -19824,7 +19831,8 @@ function getScreenshotDir(baseDir, runId, scenarioSlug, projectName, timestamp)
19824
19831
  const project = projectName ?? "default";
19825
19832
  const dateDir = formatDate(now2);
19826
19833
  const timeDir = `${formatTime(now2)}_${runId.slice(0, 8)}`;
19827
- return join10(baseDir, project, dateDir, timeDir, scenarioSlug);
19834
+ const safeScenarioSlug = slugify(scenarioSlug, MAX_SCENARIO_SLUG_LENGTH) || "scenario";
19835
+ return join10(baseDir, project, dateDir, timeDir, safeScenarioSlug);
19828
19836
  }
19829
19837
  function ensureDir(dirPath) {
19830
19838
  if (!existsSync8(dirPath)) {
@@ -19982,7 +19990,7 @@ class Screenshotter {
19982
19990
  };
19983
19991
  }
19984
19992
  }
19985
- var DEFAULT_BASE_DIR;
19993
+ var MAX_ACTION_SLUG_LENGTH = 80, MAX_SCENARIO_SLUG_LENGTH = 96, DEFAULT_BASE_DIR;
19986
19994
  var init_screenshotter = __esm(() => {
19987
19995
  init_paths();
19988
19996
  DEFAULT_BASE_DIR = join10(getTestersDir(), "screenshots");
@@ -46910,7 +46910,7 @@ import { join as join14 } from "path";
46910
46910
  // package.json
46911
46911
  var package_default = {
46912
46912
  name: "@hasna/testers",
46913
- version: "0.0.38",
46913
+ version: "0.0.39",
46914
46914
  description: "AI-powered QA testing CLI \u2014 spawns cheap AI agents to test web apps with headless browsers",
46915
46915
  type: "module",
46916
46916
  main: "dist/index.js",
@@ -48303,12 +48303,21 @@ init_browser();
48303
48303
  init_paths();
48304
48304
  import { mkdirSync as mkdirSync5, existsSync as existsSync7, writeFileSync as writeFileSync2 } from "fs";
48305
48305
  import { join as join9 } from "path";
48306
- function slugify(text) {
48307
- return text.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
48306
+ var MAX_ACTION_SLUG_LENGTH = 80;
48307
+ var MAX_SCENARIO_SLUG_LENGTH = 96;
48308
+ function truncateSlug(slug, maxLength) {
48309
+ if (slug.length <= maxLength)
48310
+ return slug;
48311
+ const truncated = slug.slice(0, maxLength).replace(/-+$/g, "");
48312
+ return truncated || slug.slice(0, maxLength);
48313
+ }
48314
+ function slugify(text, maxLength) {
48315
+ const slug = text.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
48316
+ return maxLength ? truncateSlug(slug, maxLength) : slug;
48308
48317
  }
48309
48318
  function generateFilename(stepNumber, action) {
48310
48319
  const padded = String(stepNumber).padStart(3, "0");
48311
- const slug = slugify(action);
48320
+ const slug = slugify(action, MAX_ACTION_SLUG_LENGTH);
48312
48321
  return `${padded}_${slug}.png`;
48313
48322
  }
48314
48323
  function formatDate(date) {
@@ -48322,7 +48331,8 @@ function getScreenshotDir(baseDir, runId, scenarioSlug, projectName, timestamp)
48322
48331
  const project = projectName ?? "default";
48323
48332
  const dateDir = formatDate(now2);
48324
48333
  const timeDir = `${formatTime(now2)}_${runId.slice(0, 8)}`;
48325
- return join9(baseDir, project, dateDir, timeDir, scenarioSlug);
48334
+ const safeScenarioSlug = slugify(scenarioSlug, MAX_SCENARIO_SLUG_LENGTH) || "scenario";
48335
+ return join9(baseDir, project, dateDir, timeDir, safeScenarioSlug);
48326
48336
  }
48327
48337
  function ensureDir(dirPath) {
48328
48338
  if (!existsSync7(dirPath)) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hasna/testers",
3
- "version": "0.0.38",
3
+ "version": "0.0.39",
4
4
  "description": "AI-powered QA testing CLI — spawns cheap AI agents to test web apps with headless browsers",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",