bruniai 0.1.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 +75 -0
- package/dist/compare-urls.d.ts +28 -0
- package/dist/compare-urls.d.ts.map +1 -0
- package/dist/compare-urls.js +82 -0
- package/dist/compare-urls.js.map +1 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +8 -0
- package/dist/index.js.map +1 -0
- package/dist/types.d.ts +50 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/package.json +45 -0
package/README.md
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
# bruniai
|
|
2
|
+
|
|
3
|
+
AI-powered visual regression testing tool - core comparison library.
|
|
4
|
+
|
|
5
|
+
This package provides the core comparison functionality for visual regression testing. It can be used standalone or as a dependency for building custom integrations (e.g., MCP servers, Next.js apps, etc.).
|
|
6
|
+
|
|
7
|
+
## Installation
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install bruniai
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Usage
|
|
14
|
+
|
|
15
|
+
```typescript
|
|
16
|
+
import { compareUrls } from "bruniai";
|
|
17
|
+
|
|
18
|
+
const result = await compareUrls({
|
|
19
|
+
baseUrl: "https://example.com",
|
|
20
|
+
previewUrl: "https://preview.example.com",
|
|
21
|
+
page: "/contact"
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
console.log(result.status); // "pass" | "fail" | "warning" | "none"
|
|
25
|
+
console.log(result.visual_analysis);
|
|
26
|
+
console.log(result.images.base_screenshot);
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## API
|
|
30
|
+
|
|
31
|
+
### `compareUrls(input: CompareUrlsInput): Promise<CompareUrlsOutput>`
|
|
32
|
+
|
|
33
|
+
Performs a visual comparison between two URLs.
|
|
34
|
+
|
|
35
|
+
**Parameters:**
|
|
36
|
+
- `baseUrl` (string): Base/reference URL to compare against
|
|
37
|
+
- `previewUrl` (string): Preview/changed URL to analyze
|
|
38
|
+
- `page` (string, optional): Page path to compare (default: "/")
|
|
39
|
+
- `prNumber` (string, optional): PR number for metadata
|
|
40
|
+
- `repository` (string, optional): Repository name for metadata
|
|
41
|
+
- `prTitle` (string, optional): PR title for context
|
|
42
|
+
- `prDescription` (string, optional): PR description for context
|
|
43
|
+
|
|
44
|
+
**Returns:**
|
|
45
|
+
- `status`: Overall comparison status ("pass" | "fail" | "warning" | "none")
|
|
46
|
+
- `visual_analysis`: Detailed visual analysis result from AI
|
|
47
|
+
- `sections_analysis`: Formatted sections analysis text
|
|
48
|
+
- `images`: Object containing paths to generated screenshots and diff images
|
|
49
|
+
|
|
50
|
+
## Requirements
|
|
51
|
+
|
|
52
|
+
- Node.js 18+
|
|
53
|
+
- OpenAI API key (set as `OPENAI_API_KEY` environment variable)
|
|
54
|
+
|
|
55
|
+
## Development
|
|
56
|
+
|
|
57
|
+
### Building
|
|
58
|
+
|
|
59
|
+
First, build the parent package to generate the dist files:
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
cd ../..
|
|
63
|
+
npm run build
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
Then build this package:
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
npm run build
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## License
|
|
73
|
+
|
|
74
|
+
MIT
|
|
75
|
+
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { CompareUrlsInput, CompareUrlsOutput } from "./types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Compare two URLs visually and return analysis results.
|
|
4
|
+
*
|
|
5
|
+
* This function performs a complete visual comparison workflow:
|
|
6
|
+
* - Creates a temporary directory for images
|
|
7
|
+
* - Initializes and manages Stagehand browser automation
|
|
8
|
+
* - Takes screenshots of both URLs
|
|
9
|
+
* - Generates diff images
|
|
10
|
+
* - Analyzes sections structure
|
|
11
|
+
* - Performs AI-powered visual analysis
|
|
12
|
+
* - Captures section screenshots
|
|
13
|
+
*
|
|
14
|
+
* @param input - Comparison input parameters
|
|
15
|
+
* @returns Complete analysis results with image paths
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* ```typescript
|
|
19
|
+
* const result = await compareUrls({
|
|
20
|
+
* baseUrl: "https://example.com",
|
|
21
|
+
* previewUrl: "https://preview.example.com",
|
|
22
|
+
* page: "/contact"
|
|
23
|
+
* });
|
|
24
|
+
* console.log(result.status); // "pass" | "fail" | "warning" | "none"
|
|
25
|
+
* ```
|
|
26
|
+
*/
|
|
27
|
+
export declare function compareUrls(input: CompareUrlsInput): Promise<CompareUrlsOutput>;
|
|
28
|
+
//# sourceMappingURL=compare-urls.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"compare-urls.d.ts","sourceRoot":"","sources":["../src/compare-urls.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAKtE;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,wBAAsB,WAAW,CAC/B,KAAK,EAAE,gBAAgB,GACtB,OAAO,CAAC,iBAAiB,CAAC,CA4D5B"}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { Stagehand } from "@browserbasehq/stagehand";
|
|
2
|
+
import { performComparison } from "../../../dist/comparison/core.js";
|
|
3
|
+
import { join } from "path";
|
|
4
|
+
import { mkdirSync, existsSync } from "fs";
|
|
5
|
+
import { tmpdir } from "os";
|
|
6
|
+
/**
|
|
7
|
+
* Compare two URLs visually and return analysis results.
|
|
8
|
+
*
|
|
9
|
+
* This function performs a complete visual comparison workflow:
|
|
10
|
+
* - Creates a temporary directory for images
|
|
11
|
+
* - Initializes and manages Stagehand browser automation
|
|
12
|
+
* - Takes screenshots of both URLs
|
|
13
|
+
* - Generates diff images
|
|
14
|
+
* - Analyzes sections structure
|
|
15
|
+
* - Performs AI-powered visual analysis
|
|
16
|
+
* - Captures section screenshots
|
|
17
|
+
*
|
|
18
|
+
* @param input - Comparison input parameters
|
|
19
|
+
* @returns Complete analysis results with image paths
|
|
20
|
+
*
|
|
21
|
+
* @example
|
|
22
|
+
* ```typescript
|
|
23
|
+
* const result = await compareUrls({
|
|
24
|
+
* baseUrl: "https://example.com",
|
|
25
|
+
* previewUrl: "https://preview.example.com",
|
|
26
|
+
* page: "/contact"
|
|
27
|
+
* });
|
|
28
|
+
* console.log(result.status); // "pass" | "fail" | "warning" | "none"
|
|
29
|
+
* ```
|
|
30
|
+
*/
|
|
31
|
+
export async function compareUrls(input) {
|
|
32
|
+
const { baseUrl, previewUrl, page = "/" } = input;
|
|
33
|
+
// Create temporary directory for images.
|
|
34
|
+
const imagesDir = join(tmpdir(), `bruniai-${Date.now()}`);
|
|
35
|
+
if (!existsSync(imagesDir)) {
|
|
36
|
+
mkdirSync(imagesDir, { recursive: true });
|
|
37
|
+
}
|
|
38
|
+
// Initialize Stagehand.
|
|
39
|
+
const stagehand = new Stagehand({
|
|
40
|
+
env: "LOCAL",
|
|
41
|
+
localBrowserLaunchOptions: {
|
|
42
|
+
headless: true,
|
|
43
|
+
},
|
|
44
|
+
});
|
|
45
|
+
try {
|
|
46
|
+
await stagehand.init();
|
|
47
|
+
// Perform the core comparison.
|
|
48
|
+
const result = await performComparison({
|
|
49
|
+
stagehand,
|
|
50
|
+
baseUrl,
|
|
51
|
+
previewUrl,
|
|
52
|
+
page,
|
|
53
|
+
imagesDir,
|
|
54
|
+
prNumber: input.prNumber,
|
|
55
|
+
repository: input.repository,
|
|
56
|
+
prTitle: input.prTitle,
|
|
57
|
+
prDescription: input.prDescription,
|
|
58
|
+
});
|
|
59
|
+
// Build output structure.
|
|
60
|
+
const output = {
|
|
61
|
+
status: result.visual_analysis.status,
|
|
62
|
+
visual_analysis: result.visual_analysis,
|
|
63
|
+
sections_analysis: result.sections_analysis,
|
|
64
|
+
images: {
|
|
65
|
+
base_screenshot: result.base_screenshot,
|
|
66
|
+
preview_screenshot: result.preview_screenshot,
|
|
67
|
+
diff_image: result.diff_image,
|
|
68
|
+
section_screenshots: Object.keys(result.section_screenshots).length > 0
|
|
69
|
+
? Object.fromEntries(Object.entries(result.section_screenshots).map(([key, value]) => [
|
|
70
|
+
key,
|
|
71
|
+
{ base: value.base, preview: value.preview },
|
|
72
|
+
]))
|
|
73
|
+
: undefined,
|
|
74
|
+
},
|
|
75
|
+
};
|
|
76
|
+
return output;
|
|
77
|
+
}
|
|
78
|
+
finally {
|
|
79
|
+
await stagehand.close();
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
//# sourceMappingURL=compare-urls.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"compare-urls.js","sourceRoot":"","sources":["../src/compare-urls.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,0BAA0B,CAAC;AACrD,OAAO,EAAE,iBAAiB,EAAE,MAAM,kCAAkC,CAAC;AAErE,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAC5B,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,IAAI,CAAC;AAC3C,OAAO,EAAE,MAAM,EAAE,MAAM,IAAI,CAAC;AAE5B;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,MAAM,CAAC,KAAK,UAAU,WAAW,CAC/B,KAAuB;IAEvB,MAAM,EAAE,OAAO,EAAE,UAAU,EAAE,IAAI,GAAG,GAAG,EAAE,GAAG,KAAK,CAAC;IAElD,yCAAyC;IACzC,MAAM,SAAS,GAAG,IAAI,CAAC,MAAM,EAAE,EAAE,WAAW,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;IAC1D,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;QAC3B,SAAS,CAAC,SAAS,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC5C,CAAC;IAED,wBAAwB;IACxB,MAAM,SAAS,GAAG,IAAI,SAAS,CAAC;QAC9B,GAAG,EAAE,OAAO;QACZ,yBAAyB,EAAE;YACzB,QAAQ,EAAE,IAAI;SACf;KACF,CAAC,CAAC;IAEH,IAAI,CAAC;QACH,MAAM,SAAS,CAAC,IAAI,EAAE,CAAC;QAEvB,+BAA+B;QAC/B,MAAM,MAAM,GAAG,MAAM,iBAAiB,CAAC;YACrC,SAAS;YACT,OAAO;YACP,UAAU;YACV,IAAI;YACJ,SAAS;YACT,QAAQ,EAAE,KAAK,CAAC,QAAQ;YACxB,UAAU,EAAE,KAAK,CAAC,UAAU;YAC5B,OAAO,EAAE,KAAK,CAAC,OAAO;YACtB,aAAa,EAAE,KAAK,CAAC,aAAa;SACnC,CAAC,CAAC;QAEH,0BAA0B;QAC1B,MAAM,MAAM,GAAsB;YAChC,MAAM,EAAE,MAAM,CAAC,eAAe,CAAC,MAAM;YACrC,eAAe,EAAE,MAAM,CAAC,eAAe;YACvC,iBAAiB,EAAE,MAAM,CAAC,iBAAiB;YAC3C,MAAM,EAAE;gBACN,eAAe,EAAE,MAAM,CAAC,eAAe;gBACvC,kBAAkB,EAAE,MAAM,CAAC,kBAAkB;gBAC7C,UAAU,EAAE,MAAM,CAAC,UAAU;gBAC7B,mBAAmB,EACjB,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,mBAAmB,CAAC,CAAC,MAAM,GAAG,CAAC;oBAChD,CAAC,CAAC,MAAM,CAAC,WAAW,CAChB,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,mBAAmB,CAAC,CAAC,GAAG,CAC5C,CAAC,CAAC,GAAG,EAAE,KAAK,CAAC,EAAE,EAAE,CAAC;wBAChB,GAAG;wBACH,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,OAAO,EAAE,KAAK,CAAC,OAAO,EAAE;qBAC7C,CACF,CACF;oBACH,CAAC,CAAC,SAAS;aAChB;SACF,CAAC;QAEF,OAAO,MAAM,CAAC;IAChB,CAAC;YAAS,CAAC;QACT,MAAM,SAAS,CAAC,KAAK,EAAE,CAAC;IAC1B,CAAC;AACH,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* BruniAI - AI-powered visual regression testing tool.
|
|
3
|
+
*
|
|
4
|
+
* Core comparison library for visual regression testing.
|
|
5
|
+
* Provides a simple API to compare two URLs and analyze visual differences.
|
|
6
|
+
*/
|
|
7
|
+
export { compareUrls } from "./compare-urls.js";
|
|
8
|
+
export type { CompareUrlsInput, CompareUrlsOutput, ComparisonImages, } from "./types.js";
|
|
9
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,YAAY,EACV,gBAAgB,EAChB,iBAAiB,EACjB,gBAAgB,GACjB,MAAM,YAAY,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* BruniAI - AI-powered visual regression testing tool.
|
|
3
|
+
*
|
|
4
|
+
* Core comparison library for visual regression testing.
|
|
5
|
+
* Provides a simple API to compare two URLs and analyze visual differences.
|
|
6
|
+
*/
|
|
7
|
+
export { compareUrls } from "./compare-urls.js";
|
|
8
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import type { VisualAnalysisResult } from "../../../dist/vision/types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Input parameters for compareUrls function.
|
|
4
|
+
*/
|
|
5
|
+
export interface CompareUrlsInput {
|
|
6
|
+
/** Base/reference URL to compare against. */
|
|
7
|
+
baseUrl: string;
|
|
8
|
+
/** Preview/changed URL to analyze. */
|
|
9
|
+
previewUrl: string;
|
|
10
|
+
/** Page path to compare (e.g., "/" or "/about"). Defaults to "/". */
|
|
11
|
+
page?: string;
|
|
12
|
+
/** Optional PR number for metadata. */
|
|
13
|
+
prNumber?: string;
|
|
14
|
+
/** Optional repository name for metadata. */
|
|
15
|
+
repository?: string;
|
|
16
|
+
/** Optional PR title for context. */
|
|
17
|
+
prTitle?: string;
|
|
18
|
+
/** Optional PR description for context. */
|
|
19
|
+
prDescription?: string;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Image paths returned from comparison.
|
|
23
|
+
*/
|
|
24
|
+
export interface ComparisonImages {
|
|
25
|
+
/** Path to base screenshot. */
|
|
26
|
+
base_screenshot: string;
|
|
27
|
+
/** Path to preview screenshot. */
|
|
28
|
+
preview_screenshot: string;
|
|
29
|
+
/** Path to diff image. */
|
|
30
|
+
diff_image: string;
|
|
31
|
+
/** Section screenshots keyed by section ID. */
|
|
32
|
+
section_screenshots?: Record<string, {
|
|
33
|
+
base: string;
|
|
34
|
+
preview: string;
|
|
35
|
+
}>;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Output structure for compareUrls function.
|
|
39
|
+
*/
|
|
40
|
+
export interface CompareUrlsOutput {
|
|
41
|
+
/** Overall comparison status. */
|
|
42
|
+
status: "pass" | "fail" | "warning" | "none";
|
|
43
|
+
/** Visual analysis result from AI. */
|
|
44
|
+
visual_analysis: VisualAnalysisResult;
|
|
45
|
+
/** Formatted sections analysis text. */
|
|
46
|
+
sections_analysis: string;
|
|
47
|
+
/** Generated images from comparison. */
|
|
48
|
+
images: ComparisonImages;
|
|
49
|
+
}
|
|
50
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,+BAA+B,CAAC;AAE1E;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,6CAA6C;IAC7C,OAAO,EAAE,MAAM,CAAC;IAChB,sCAAsC;IACtC,UAAU,EAAE,MAAM,CAAC;IACnB,qEAAqE;IACrE,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,uCAAuC;IACvC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,6CAA6C;IAC7C,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,qCAAqC;IACrC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,2CAA2C;IAC3C,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,+BAA+B;IAC/B,eAAe,EAAE,MAAM,CAAC;IACxB,kCAAkC;IAClC,kBAAkB,EAAE,MAAM,CAAC;IAC3B,0BAA0B;IAC1B,UAAU,EAAE,MAAM,CAAC;IACnB,+CAA+C;IAC/C,mBAAmB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACzE;AAED;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,iCAAiC;IACjC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,GAAG,MAAM,CAAC;IAC7C,sCAAsC;IACtC,eAAe,EAAE,oBAAoB,CAAC;IACtC,wCAAwC;IACxC,iBAAiB,EAAE,MAAM,CAAC;IAC1B,wCAAwC;IACxC,MAAM,EAAE,gBAAgB,CAAC;CAC1B"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
|
package/package.json
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "bruniai",
|
|
3
|
+
"version": "0.1.2",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"description": "AI-powered visual regression testing tool - core comparison library",
|
|
6
|
+
"author": "Joao Garin",
|
|
7
|
+
"license": "MIT",
|
|
8
|
+
"main": "./dist/index.js",
|
|
9
|
+
"types": "./dist/index.d.ts",
|
|
10
|
+
"exports": {
|
|
11
|
+
".": {
|
|
12
|
+
"import": "./dist/index.js",
|
|
13
|
+
"types": "./dist/index.d.ts"
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
"files": [
|
|
17
|
+
"dist",
|
|
18
|
+
"README.md"
|
|
19
|
+
],
|
|
20
|
+
"scripts": {
|
|
21
|
+
"build": "tsc",
|
|
22
|
+
"dev": "tsx src/index.ts"
|
|
23
|
+
},
|
|
24
|
+
"dependencies": {
|
|
25
|
+
"@browserbasehq/stagehand": "^3.0.1",
|
|
26
|
+
"dotenv": "^16.0.0",
|
|
27
|
+
"pixelmatch": "^7.1.0",
|
|
28
|
+
"playwright": "^1.42.1",
|
|
29
|
+
"pngjs": "^7.0.0",
|
|
30
|
+
"sharp": "^0.34.5",
|
|
31
|
+
"uuid": "^10.0.0",
|
|
32
|
+
"zod": "^3.23.8"
|
|
33
|
+
},
|
|
34
|
+
"devDependencies": {
|
|
35
|
+
"@types/node": "^20.11.24",
|
|
36
|
+
"@types/pixelmatch": "^5.2.6",
|
|
37
|
+
"@types/pngjs": "^6.0.5",
|
|
38
|
+
"@types/uuid": "^10.0.0",
|
|
39
|
+
"tsx": "^4.7.0",
|
|
40
|
+
"typescript": "^5.5.4"
|
|
41
|
+
},
|
|
42
|
+
"peerDependencies": {
|
|
43
|
+
"@browserbasehq/stagehand": "^3.0.1"
|
|
44
|
+
}
|
|
45
|
+
}
|