@bughunters/vision 1.0.0 → 1.0.2

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
@@ -42,17 +42,19 @@ BUGHUNTERS_VISION_TOKEN=bhv_your_token_here
42
42
 
43
43
  ```typescript
44
44
  import { defineConfig, devices } from '@playwright/test';
45
- import type { VisionFixtureOptions } from '@bughunters/vision';
46
45
 
47
- export default defineConfig<VisionFixtureOptions>({
46
+ export default defineConfig({
48
47
  testDir: './tests',
49
48
  reporter: [
50
49
  ['list'],
51
50
  ['@bughunters/vision/reporter', { snapshotsDir: './bhv-snapshots', reportDir: './bhv-report' }],
52
51
  ],
53
52
  use: {
54
- bvMode: 'ai', // 'ai' | 'strict' | 'off'
55
- },
53
+ // BugHunters Vision options can also be set via env variables
54
+ bvMode: 'ai', // 'ai' | 'strict' | 'off'
55
+ bvSnapshotsDir: './bhv-snapshots',
56
+ bvUpdateBaseline: false,
57
+ } as Record<string, unknown>,
56
58
  projects: [
57
59
  { name: 'chromium', use: { ...devices['Desktop Chrome'] } },
58
60
  ],
@@ -62,19 +64,26 @@ export default defineConfig<VisionFixtureOptions>({
62
64
  ### 4. Write a visual test
63
65
 
64
66
  ```typescript
65
- import { test } from '@bughunters/vision';
67
+ import { test } from '@playwright/test';
68
+ import { vision } from '@bughunters/vision';
66
69
 
67
- test('homepage looks correct', async ({ page, visionCheck }) => {
70
+ test('homepage looks correct', async ({ page }) => {
68
71
  await page.goto('https://example.com');
69
72
  await page.waitForLoadState('networkidle');
70
- await visionCheck('homepage');
73
+ await vision.check(page, 'homepage');
71
74
  });
72
75
  ```
73
76
 
74
- You can pass an optional AI hint as the second argument:
77
+ You can pass an optional AI hint as the third argument:
75
78
 
76
79
  ```typescript
77
- await visionCheck('dashboard', 'Ignore the live timestamp in the top-right corner.');
80
+ await vision.check(page, 'dashboard', 'Ignore the live timestamp in the top-right corner.');
81
+ ```
82
+
83
+ `vision.check()` accepts a `Page` or a `Locator` — useful for scoped element checks:
84
+
85
+ ```typescript
86
+ await vision.check(page.locator('#sidebar'), 'sidebar');
78
87
  ```
79
88
 
80
89
  ### 5. Run your tests
@@ -104,16 +113,18 @@ The report includes side-by-side Baseline / Current / Diff views with full-scree
104
113
 
105
114
  ## Configuration reference
106
115
 
107
- All options can be set in `playwright.config.ts` under `use:`, or overridden via environment variables.
116
+ Options can be set in `playwright.config.ts` under `use:` **or** via environment variables. The `use:` block takes precedence; env vars are the fallback.
108
117
 
109
118
  | Option in `use:` | Environment variable | Default | Description |
110
119
  |---|---|---|---|
111
120
  | `bvMode` | `BHV_MODE` | `'ai'` | Comparison mode: `ai` · `strict` · `off` |
112
121
  | `bvSnapshotsDir` | `BHV_SNAPSHOTS_DIR` | `'./bhv-snapshots'` | Where to store PNG snapshots |
113
- | `bvUpdateBaseline` | `BHV_UPDATE_BASELINE` | `false` | Force-overwrite all baselines on this run |
122
+ | `bvUpdateBaseline` | `BHV_UPDATE_BASELINE` | `false` | Set to `true` to force-overwrite all baselines |
114
123
  | `bvApiUrl` | `BUGHUNTERS_VISION_API_URL` | *(production)* | Override API endpoint (e.g. for self-hosted) |
115
124
  | `bvToken` | `BUGHUNTERS_VISION_TOKEN` | — | Your API token — always use env var, never commit |
116
125
 
126
+ > **Note:** Because `vision` is a standalone object (not a Playwright fixture), the `use:` options are not TypeScript-typed. Add `as Record<string, unknown>` to the `use` block to suppress TS errors.
127
+
117
128
  ### Comparison modes
118
129
 
119
130
  | Mode | Behaviour |
package/dist/check.d.ts CHANGED
@@ -1,15 +1,8 @@
1
- import type { Page, TestInfo } from '@playwright/test';
2
- import type { VisionMode } from './types';
1
+ import type { Page, Locator } from '@playwright/test';
3
2
  export interface RunVisionCheckOptions {
4
- page: Page;
3
+ target: Page | Locator;
5
4
  name: string;
6
5
  prompt?: string;
7
- apiUrl: string;
8
- mode: VisionMode;
9
- snapshotsDir: string;
10
- updateBaseline: boolean;
11
- token?: string;
12
- testInfo?: TestInfo;
13
6
  }
14
7
  export declare function getLastRemainingBalance(): string | null;
15
8
  export declare function runVisionCheck(opts: RunVisionCheckOptions): Promise<void>;
@@ -1 +1 @@
1
- {"version":3,"file":"check.d.ts","sourceRoot":"","sources":["../src/check.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAGvD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAE1C,MAAM,WAAW,qBAAqB;IACpC,IAAI,EAAE,IAAI,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,UAAU,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,cAAc,EAAE,OAAO,CAAC;IACxB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,QAAQ,CAAC;CACrB;AAKD,wBAAgB,uBAAuB,IAAI,MAAM,GAAG,IAAI,CAEvD;AA+GD,wBAAsB,cAAc,CAAC,IAAI,EAAE,qBAAqB,GAAG,OAAO,CAAC,IAAI,CAAC,CA4I/E"}
1
+ {"version":3,"file":"check.d.ts","sourceRoot":"","sources":["../src/check.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,IAAI,EAAE,OAAO,EAAY,MAAM,kBAAkB,CAAC;AAOhE,MAAM,WAAW,qBAAqB;IACpC,MAAM,EAAE,IAAI,GAAG,OAAO,CAAC;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAKD,wBAAgB,uBAAuB,IAAI,MAAM,GAAG,IAAI,CAEvD;AA+GD,wBAAsB,cAAc,CAAC,IAAI,EAAE,qBAAqB,GAAG,OAAO,CAAC,IAAI,CAAC,CAmJ/E"}
package/dist/check.js CHANGED
@@ -40,6 +40,7 @@ const path = __importStar(require("path"));
40
40
  const test_1 = require("@playwright/test");
41
41
  const pixel_match_1 = require("./pixel-match");
42
42
  const results_1 = require("./results");
43
+ const PRODUCTION_API_URL = 'https://bugvision-backend.vercel.app';
43
44
  // Module-level balance tracker — updated after each AI call, read by reporter.onEnd()
44
45
  let _lastRemainingBalance = null;
45
46
  function getLastRemainingBalance() {
@@ -137,7 +138,19 @@ async function pushToPlaywright(testInfo, status, reason, resolvedDir, baselineF
137
138
  }
138
139
  }
139
140
  async function runVisionCheck(opts) {
140
- const { page, name, prompt = 'Check for any visual regressions.', apiUrl, mode, snapshotsDir, updateBaseline, token, testInfo, } = opts;
141
+ const { target, name, prompt = 'Check for any visual regressions.' } = opts;
142
+ // Read config from testInfo.project.use (if available) or fall back to env vars
143
+ let testInfo;
144
+ try {
145
+ testInfo = test_1.test.info();
146
+ }
147
+ catch { /* called outside test context */ }
148
+ const projectUse = (testInfo?.project?.use ?? {});
149
+ const apiUrl = projectUse['bvApiUrl'] ?? process.env.BUGHUNTERS_VISION_API_URL ?? process.env.BHV_API_URL ?? PRODUCTION_API_URL;
150
+ const mode = (projectUse['bvMode'] ?? process.env.BHV_MODE ?? 'ai');
151
+ const snapshotsDir = projectUse['bvSnapshotsDir'] ?? process.env.BHV_SNAPSHOTS_DIR ?? './bhv-snapshots';
152
+ const updateBaseline = projectUse['bvUpdateBaseline'] ?? (process.env.BHV_UPDATE_BASELINE === 'true');
153
+ const token = projectUse['bvToken'] ?? process.env.BUGHUNTERS_VISION_TOKEN ?? undefined;
141
154
  // Mode: off → skip all visual testing immediately
142
155
  if (mode === 'off') {
143
156
  console.log(`\n⏭ [BugHunters Vision] "${name}" — skipped (BHV_MODE=off)\n`);
@@ -146,7 +159,7 @@ async function runVisionCheck(opts) {
146
159
  const resolvedDir = path.resolve(process.cwd(), snapshotsDir);
147
160
  fs.mkdirSync(resolvedDir, { recursive: true });
148
161
  const baselineFile = path.join(resolvedDir, `${name}.baseline.png`);
149
- const currentBuffer = await page.screenshot({ fullPage: false, type: 'png' });
162
+ const currentBuffer = await target.screenshot();
150
163
  if (!fs.existsSync(baselineFile) || updateBaseline) {
151
164
  // First run — save as baseline
152
165
  fs.writeFileSync(baselineFile, currentBuffer);
package/dist/index.d.ts CHANGED
@@ -1,6 +1,9 @@
1
- export { test, expect } from './fixture';
2
- export type { VisionFixtureOptions } from './fixture';
1
+ import type { Page, Locator } from '@playwright/test';
3
2
  export type { VisionMode, TestResult, Meta } from './types';
4
3
  export { BugHuntersVisionReporter } from './reporter';
5
4
  export type { BugHuntersVisionReporterOptions } from './reporter';
5
+ export { getLastRemainingBalance } from './check';
6
+ export declare const vision: {
7
+ check(target: Page | Locator, name: string, prompt?: string): Promise<void>;
8
+ };
6
9
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,WAAW,CAAC;AACzC,YAAY,EAAE,oBAAoB,EAAE,MAAM,WAAW,CAAC;AACtD,YAAY,EAAE,UAAU,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,SAAS,CAAC;AAC5D,OAAO,EAAE,wBAAwB,EAAE,MAAM,YAAY,CAAC;AACtD,YAAY,EAAE,+BAA+B,EAAE,MAAM,YAAY,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAGtD,YAAY,EAAE,UAAU,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,SAAS,CAAC;AAC5D,OAAO,EAAE,wBAAwB,EAAE,MAAM,YAAY,CAAC;AACtD,YAAY,EAAE,+BAA+B,EAAE,MAAM,YAAY,CAAC;AAClE,OAAO,EAAE,uBAAuB,EAAE,MAAM,SAAS,CAAC;AAElD,eAAO,MAAM,MAAM;kBACG,IAAI,GAAG,OAAO,QAAQ,MAAM,WAAW,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAGlF,CAAC"}
package/dist/index.js CHANGED
@@ -1,8 +1,13 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.BugHuntersVisionReporter = exports.expect = exports.test = void 0;
4
- var fixture_1 = require("./fixture");
5
- Object.defineProperty(exports, "test", { enumerable: true, get: function () { return fixture_1.test; } });
6
- Object.defineProperty(exports, "expect", { enumerable: true, get: function () { return fixture_1.expect; } });
3
+ exports.vision = exports.getLastRemainingBalance = exports.BugHuntersVisionReporter = void 0;
4
+ const check_1 = require("./check");
7
5
  var reporter_1 = require("./reporter");
8
6
  Object.defineProperty(exports, "BugHuntersVisionReporter", { enumerable: true, get: function () { return reporter_1.BugHuntersVisionReporter; } });
7
+ var check_2 = require("./check");
8
+ Object.defineProperty(exports, "getLastRemainingBalance", { enumerable: true, get: function () { return check_2.getLastRemainingBalance; } });
9
+ exports.vision = {
10
+ async check(target, name, prompt) {
11
+ return (0, check_1.runVisionCheck)({ target, name, prompt });
12
+ },
13
+ };
package/package.json CHANGED
@@ -1,16 +1,20 @@
1
1
  {
2
2
  "name": "@bughunters/vision",
3
- "version": "1.0.0",
3
+ "version": "1.0.2",
4
4
  "description": "InfoSec-friendly AI Visual Testing plugin for Playwright.",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",
7
7
  "exports": {
8
8
  ".": {
9
9
  "require": "./dist/index.js",
10
+ "import": "./dist/index.js",
11
+ "default": "./dist/index.js",
10
12
  "types": "./dist/index.d.ts"
11
13
  },
12
14
  "./reporter": {
13
15
  "require": "./dist/reporter.js",
16
+ "import": "./dist/reporter.js",
17
+ "default": "./dist/reporter.js",
14
18
  "types": "./dist/reporter.d.ts"
15
19
  }
16
20
  },
package/dist/fixture.d.ts DELETED
@@ -1,21 +0,0 @@
1
- import { expect } from '@playwright/test';
2
- import type { VisionMode } from './types';
3
- export interface VisionFixtureOptions {
4
- /** BugHunters Vision API URL (default: https://bugvision-backend.vercel.app) */
5
- bvApiUrl: string;
6
- /** Comparison mode: ai | strict | off (default: ai) */
7
- bvMode: VisionMode;
8
- /** Where to store baseline + current screenshots (default: ./bhv-snapshots) */
9
- bvSnapshotsDir: string;
10
- /** Force-overwrite all baselines on this run (default: false) */
11
- bvUpdateBaseline: boolean;
12
- /** BugHunters Vision API token (env: BUGHUNTERS_VISION_TOKEN) */
13
- bvToken: string;
14
- }
15
- type VisionFixtures = {
16
- /** Run a visual snapshot check for the current page state */
17
- visionCheck: (name: string, prompt?: string) => Promise<void>;
18
- };
19
- export declare const test: import("@playwright/test").TestType<import("@playwright/test").PlaywrightTestArgs & import("@playwright/test").PlaywrightTestOptions & VisionFixtures & VisionFixtureOptions, import("@playwright/test").PlaywrightWorkerArgs & import("@playwright/test").PlaywrightWorkerOptions>;
20
- export { expect };
21
- //# sourceMappingURL=fixture.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"fixture.d.ts","sourceRoot":"","sources":["../src/fixture.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAExD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAK1C,MAAM,WAAW,oBAAoB;IACnC,gFAAgF;IAChF,QAAQ,EAAE,MAAM,CAAC;IACjB,uDAAuD;IACvD,MAAM,EAAE,UAAU,CAAC;IACnB,+EAA+E;IAC/E,cAAc,EAAE,MAAM,CAAC;IACvB,iEAAiE;IACjE,gBAAgB,EAAE,OAAO,CAAC;IAC1B,iEAAiE;IACjE,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,KAAK,cAAc,GAAG;IACpB,6DAA6D;IAC7D,WAAW,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CAC/D,CAAC;AAEF,eAAO,MAAM,IAAI,qRAoBf,CAAC;AAEH,OAAO,EAAE,MAAM,EAAE,CAAC"}
package/dist/fixture.js DELETED
@@ -1,25 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.expect = exports.test = void 0;
4
- const test_1 = require("@playwright/test");
5
- Object.defineProperty(exports, "expect", { enumerable: true, get: function () { return test_1.expect; } });
6
- const check_1 = require("./check");
7
- const PRODUCTION_API_URL = 'https://bugvision-backend.vercel.app';
8
- exports.test = test_1.test.extend({
9
- bvApiUrl: [process.env.BUGHUNTERS_VISION_API_URL ?? process.env.BHV_API_URL ?? PRODUCTION_API_URL, { option: true }],
10
- bvMode: [(process.env.BHV_MODE ?? 'ai'), { option: true }],
11
- bvSnapshotsDir: [process.env.BHV_SNAPSHOTS_DIR ?? './bhv-snapshots', { option: true }],
12
- bvUpdateBaseline: [process.env.BHV_UPDATE_BASELINE === 'true', { option: true }],
13
- bvToken: [process.env.BUGHUNTERS_VISION_TOKEN ?? '', { option: true }],
14
- visionCheck: async ({ page, bvApiUrl, bvMode, bvSnapshotsDir, bvUpdateBaseline, bvToken }, use, testInfo) => {
15
- await use((name, prompt) => (0, check_1.runVisionCheck)({
16
- page, name, prompt,
17
- apiUrl: bvApiUrl,
18
- mode: bvMode,
19
- snapshotsDir: bvSnapshotsDir,
20
- updateBaseline: bvUpdateBaseline,
21
- token: bvToken || undefined,
22
- testInfo,
23
- }));
24
- },
25
- });