prowl-tools 0.1.3

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/lib.js ADDED
@@ -0,0 +1,55 @@
1
+ import {
2
+ DEFAULT_FLAKY_THRESHOLD,
3
+ analyzePage,
4
+ buildHealCandidates,
5
+ clusterFailures,
6
+ computeFlakeScore,
7
+ extractFailures,
8
+ extractSelectorIntent,
9
+ generateHunt,
10
+ healSelector,
11
+ interpolateHunt,
12
+ rankFlaky,
13
+ readHistory,
14
+ readHuntHistory,
15
+ runHunt,
16
+ runSuite,
17
+ updateBacklogFromSuite
18
+ } from "./chunk-T7YLXF6X.js";
19
+ import {
20
+ configSchema,
21
+ huntSchema,
22
+ listHunts,
23
+ loadConfig,
24
+ loadHunt,
25
+ loadHuntMeta,
26
+ loadHuntTags,
27
+ stepSchema
28
+ } from "./chunk-NXXGJOBG.js";
29
+ export {
30
+ DEFAULT_FLAKY_THRESHOLD,
31
+ analyzePage,
32
+ buildHealCandidates,
33
+ clusterFailures,
34
+ computeFlakeScore,
35
+ configSchema,
36
+ extractFailures,
37
+ extractSelectorIntent,
38
+ generateHunt,
39
+ healSelector,
40
+ huntSchema,
41
+ interpolateHunt,
42
+ listHunts,
43
+ loadConfig,
44
+ loadHunt,
45
+ loadHuntMeta,
46
+ loadHuntTags,
47
+ rankFlaky,
48
+ readHistory,
49
+ readHuntHistory,
50
+ runHunt,
51
+ runSuite,
52
+ stepSchema,
53
+ updateBacklogFromSuite
54
+ };
55
+ //# sourceMappingURL=lib.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -0,0 +1,27 @@
1
+ import {
2
+ CONFIG_DIR,
3
+ LEGACY_CONFIG_DIR,
4
+ ensureAllowedDomain,
5
+ findConfigPath,
6
+ listHunts,
7
+ loadConfig,
8
+ loadHunt,
9
+ loadHuntMeta,
10
+ loadHuntTags,
11
+ resolveViewport,
12
+ warnLegacyConfigDir
13
+ } from "./chunk-NXXGJOBG.js";
14
+ export {
15
+ CONFIG_DIR,
16
+ LEGACY_CONFIG_DIR,
17
+ ensureAllowedDomain,
18
+ findConfigPath,
19
+ listHunts,
20
+ loadConfig,
21
+ loadHunt,
22
+ loadHuntMeta,
23
+ loadHuntTags,
24
+ resolveViewport,
25
+ warnLegacyConfigDir
26
+ };
27
+ //# sourceMappingURL=loader-5RDNTJHH.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -0,0 +1,46 @@
1
+ // src/runner/visual.ts
2
+ import fs from "fs";
3
+ import path from "path";
4
+ import { PNG } from "pngjs";
5
+ import pixelmatch from "pixelmatch";
6
+ async function compareScreenshots(baselinePath, currentPath, diffPath, threshold) {
7
+ const baselineData = PNG.sync.read(fs.readFileSync(baselinePath));
8
+ const currentData = PNG.sync.read(fs.readFileSync(currentPath));
9
+ const { width, height } = baselineData;
10
+ if (currentData.width !== width || currentData.height !== height) {
11
+ const diff2 = new PNG({ width, height });
12
+ fs.writeFileSync(diffPath, PNG.sync.write(diff2));
13
+ return {
14
+ match: false,
15
+ diffPercentage: 1,
16
+ diffImagePath: diffPath
17
+ };
18
+ }
19
+ const diff = new PNG({ width, height });
20
+ const diffPixels = pixelmatch(
21
+ baselineData.data,
22
+ currentData.data,
23
+ diff.data,
24
+ width,
25
+ height,
26
+ { threshold: 0.1 }
27
+ );
28
+ const totalPixels = width * height;
29
+ const diffPercentage = diffPixels / totalPixels;
30
+ fs.writeFileSync(diffPath, PNG.sync.write(diff));
31
+ return {
32
+ match: diffPercentage <= threshold,
33
+ diffPercentage,
34
+ diffImagePath: diffPath
35
+ };
36
+ }
37
+ function ensureBaselineDir(configDir) {
38
+ const baselineDir = path.join(configDir, "baselines");
39
+ fs.mkdirSync(baselineDir, { recursive: true });
40
+ return baselineDir;
41
+ }
42
+ export {
43
+ compareScreenshots,
44
+ ensureBaselineDir
45
+ };
46
+ //# sourceMappingURL=visual-FSARM2JS.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/runner/visual.ts"],"sourcesContent":["import fs from \"node:fs\";\nimport path from \"node:path\";\nimport { PNG } from \"pngjs\";\nimport pixelmatch from \"pixelmatch\";\n\nexport type ComparisonResult = {\n match: boolean;\n diffPercentage: number;\n diffImagePath?: string;\n};\n\nexport async function compareScreenshots(\n baselinePath: string,\n currentPath: string,\n diffPath: string,\n threshold: number\n): Promise<ComparisonResult> {\n const baselineData = PNG.sync.read(fs.readFileSync(baselinePath));\n const currentData = PNG.sync.read(fs.readFileSync(currentPath));\n\n const { width, height } = baselineData;\n\n if (currentData.width !== width || currentData.height !== height) {\n const diff = new PNG({ width, height });\n fs.writeFileSync(diffPath, PNG.sync.write(diff));\n return {\n match: false,\n diffPercentage: 1,\n diffImagePath: diffPath\n };\n }\n\n const diff = new PNG({ width, height });\n const diffPixels = pixelmatch(\n baselineData.data,\n currentData.data,\n diff.data,\n width,\n height,\n { threshold: 0.1 }\n );\n\n const totalPixels = width * height;\n const diffPercentage = diffPixels / totalPixels;\n\n fs.writeFileSync(diffPath, PNG.sync.write(diff));\n\n return {\n match: diffPercentage <= threshold,\n diffPercentage,\n diffImagePath: diffPath\n };\n}\n\nexport function ensureBaselineDir(configDir: string): string {\n const baselineDir = path.join(configDir, \"baselines\");\n fs.mkdirSync(baselineDir, { recursive: true });\n return baselineDir;\n}\n"],"mappings":";AAAA,OAAO,QAAQ;AACf,OAAO,UAAU;AACjB,SAAS,WAAW;AACpB,OAAO,gBAAgB;AAQvB,eAAsB,mBACpB,cACA,aACA,UACA,WAC2B;AAC3B,QAAM,eAAe,IAAI,KAAK,KAAK,GAAG,aAAa,YAAY,CAAC;AAChE,QAAM,cAAc,IAAI,KAAK,KAAK,GAAG,aAAa,WAAW,CAAC;AAE9D,QAAM,EAAE,OAAO,OAAO,IAAI;AAE1B,MAAI,YAAY,UAAU,SAAS,YAAY,WAAW,QAAQ;AAChE,UAAMA,QAAO,IAAI,IAAI,EAAE,OAAO,OAAO,CAAC;AACtC,OAAG,cAAc,UAAU,IAAI,KAAK,MAAMA,KAAI,CAAC;AAC/C,WAAO;AAAA,MACL,OAAO;AAAA,MACP,gBAAgB;AAAA,MAChB,eAAe;AAAA,IACjB;AAAA,EACF;AAEA,QAAM,OAAO,IAAI,IAAI,EAAE,OAAO,OAAO,CAAC;AACtC,QAAM,aAAa;AAAA,IACjB,aAAa;AAAA,IACb,YAAY;AAAA,IACZ,KAAK;AAAA,IACL;AAAA,IACA;AAAA,IACA,EAAE,WAAW,IAAI;AAAA,EACnB;AAEA,QAAM,cAAc,QAAQ;AAC5B,QAAM,iBAAiB,aAAa;AAEpC,KAAG,cAAc,UAAU,IAAI,KAAK,MAAM,IAAI,CAAC;AAE/C,SAAO;AAAA,IACL,OAAO,kBAAkB;AAAA,IACzB;AAAA,IACA,eAAe;AAAA,EACjB;AACF;AAEO,SAAS,kBAAkB,WAA2B;AAC3D,QAAM,cAAc,KAAK,KAAK,WAAW,WAAW;AACpD,KAAG,UAAU,aAAa,EAAE,WAAW,KAAK,CAAC;AAC7C,SAAO;AACT;","names":["diff"]}
@@ -0,0 +1,41 @@
1
+ # Prowl Configuration
2
+ # This file controls how Prowl runs your hunts.
3
+ # All options shown with their default values.
4
+
5
+ # Target application — the base URL for all hunt navigation
6
+ target:
7
+ url: "http://localhost:3000"
8
+
9
+ # Browser settings
10
+ browser:
11
+ headless: true # Set to false or use --headed to see the browser
12
+ slowMo: 0 # Milliseconds to wait between actions (useful for debugging)
13
+ timeout: 30000 # Default timeout for page operations (30s)
14
+
15
+ # Artifact generation — what gets saved per run
16
+ artifacts:
17
+ screenshots: "on-failure" # "on-failure" or "all" (screenshot every step)
18
+ networkHar: false # Save network activity as HAR file
19
+ console: true # Save browser console output
20
+
21
+ # Hunt-level assertions — applied after every hunt's steps complete
22
+ assertions:
23
+ noConsoleErrors: true # Fail if browser console has errors
24
+ noNetworkErrors: true # Fail if any HTTP response >= 400
25
+ maxTotalTimeMs: 30000 # Max total time for all steps (30s)
26
+ networkIgnorePatterns: [] # URL patterns to ignore for network error checks
27
+
28
+ # Safety guardrails — prevent accidental damage
29
+ guardrails:
30
+ maxSteps: 50 # Maximum steps per hunt
31
+ allowedDomains: # Only navigate to these domains
32
+ - "localhost"
33
+ - "127.0.0.1"
34
+ - "0.0.0.0"
35
+ forbiddenSelectors: # Selectors that steps cannot interact with
36
+ - "[data-danger]"
37
+ - ".delete-btn"
38
+
39
+ # Auth state — captured by `prowl login`, used to skip login in hunts
40
+ auth:
41
+ storageStatePath: ".prowl/auth-state.json"
@@ -0,0 +1,20 @@
1
+ # Hello Hunt — Your First Prowl Test
2
+ # ---
3
+ # This minimal hunt verifies your target app is running and the page loads.
4
+ # Customize the URL path and expected text to match your app.
5
+ #
6
+ # Run it: prowl run hello
7
+ #
8
+ # Browse more hunt templates at https://hub.prowl.tools
9
+
10
+ name: hello
11
+ description: Verify the target app loads successfully
12
+
13
+ steps:
14
+ - navigate: "/"
15
+
16
+ - waitForNetworkIdle:
17
+ timeout: 5000
18
+
19
+ assertions:
20
+ - noConsoleErrors: true
package/package.json ADDED
@@ -0,0 +1,83 @@
1
+ {
2
+ "name": "prowl-tools",
3
+ "version": "0.1.3",
4
+ "description": "CLI-first QA testing tool for deterministic Playwright flows.",
5
+ "type": "module",
6
+ "license": "Apache-2.0",
7
+ "author": "Michael Tookes",
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "https://github.com/prowl-tools/prowl.git"
11
+ },
12
+ "homepage": "https://prowl.tools",
13
+ "bugs": {
14
+ "url": "https://github.com/prowl-tools/prowl/issues"
15
+ },
16
+ "keywords": [
17
+ "testing",
18
+ "qa",
19
+ "playwright",
20
+ "yaml",
21
+ "e2e",
22
+ "browser-testing",
23
+ "automation",
24
+ "web-testing",
25
+ "cli"
26
+ ],
27
+ "main": "dist/lib.cjs",
28
+ "module": "dist/lib.js",
29
+ "types": "dist/lib.d.ts",
30
+ "exports": {
31
+ ".": {
32
+ "import": {
33
+ "types": "./dist/lib.d.ts",
34
+ "default": "./dist/lib.js"
35
+ },
36
+ "require": {
37
+ "types": "./dist/lib.d.cts",
38
+ "default": "./dist/lib.cjs"
39
+ }
40
+ }
41
+ },
42
+ "bin": {
43
+ "prowl": "dist/index.js"
44
+ },
45
+ "files": [
46
+ "dist",
47
+ "examples",
48
+ "LICENSE",
49
+ "README.md",
50
+ "NOTICE"
51
+ ],
52
+ "engines": {
53
+ "node": ">=20.0.0"
54
+ },
55
+ "scripts": {
56
+ "build": "tsup",
57
+ "lint": "eslint .",
58
+ "test": "vitest run",
59
+ "test:watch": "vitest"
60
+ },
61
+ "dependencies": {
62
+ "@modelcontextprotocol/sdk": "^1.29.0",
63
+ "chalk": "^5.3.0",
64
+ "commander": "^12.1.0",
65
+ "dotenv": "^16.6.1",
66
+ "ora": "^8.1.1",
67
+ "pixelmatch": "^7.1.0",
68
+ "playwright": "^1.50.1",
69
+ "pngjs": "^7.0.0",
70
+ "yaml": "^2.6.1",
71
+ "zod": "^3.23.8"
72
+ },
73
+ "devDependencies": {
74
+ "@types/node": "^22.13.1",
75
+ "@types/pngjs": "^6.0.5",
76
+ "@typescript-eslint/eslint-plugin": "^7.18.0",
77
+ "@typescript-eslint/parser": "^7.18.0",
78
+ "eslint": "^8.57.1",
79
+ "tsup": "^8.3.5",
80
+ "typescript": "^5.7.3",
81
+ "vitest": "^2.1.8"
82
+ }
83
+ }