regressionbot 0.0.1

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 ADDED
@@ -0,0 +1,125 @@
1
+ # RegressionBot SDK
2
+
3
+ The declarative visual regression testing SDK.
4
+
5
+ ## Features
6
+
7
+ - **Fluent Manifest Builder**: Chainable methods to define your test scope.
8
+ - **Matrix Testing**: Test multiple devices and viewports in a single job.
9
+ - **Auto-Discovery**: Scan sitemaps with glob patterns and limits.
10
+ - **Project-Based Baselines**: Share visual history across different environments (Preview vs Prod).
11
+
12
+ ## Installation
13
+
14
+ ```bash
15
+ npm install regressionbot
16
+ ```
17
+
18
+ ## Usage
19
+
20
+ ### Basic Example
21
+
22
+ ```typescript
23
+ import { Visual } from 'regressionbot';
24
+
25
+ const visual = new Visual();
26
+
27
+ const job = await visual
28
+ .test('https://preview.myapp.com')
29
+ .forProject('my-app-web')
30
+ .run();
31
+
32
+ const status = await job.waitForCompletion();
33
+ console.log(`Stability Score: ${status.overallScore}/100`);
34
+ ```
35
+
36
+ ### Full Matrix Example
37
+
38
+ ```typescript
39
+ import { Visual } from 'regressionbot';
40
+
41
+ const visual = new Visual(process.env.API_KEY);
42
+
43
+ const job = await visual
44
+ .test(process.env.VERCEL_PREVIEW_URL) // The Candidate (Test Origin)
45
+ .against('https://production-app.com') // The Source of Truth (Base Origin)
46
+ .forProject('marketing-site-v2') // Context: Links to Baselines & History
47
+
48
+ // Matrix Configuration: Run all checks on both Desktop and Mobile
49
+ .on(['Desktop Chrome', 'iPhone 13'])
50
+
51
+ // Sitemap: Explicitly provide a sitemap location (optional)
52
+ .sitemap('https://production-app.com/sitemap_index.xml')
53
+
54
+ // Scope: Explicitly check critical paths
55
+ .check('/', 'Homepage')
56
+ .check('/pricing', 'Pricing Table')
57
+
58
+ // Discovery: Auto-discover up to 20 blog posts
59
+ .scan('/blog/**', { limit: 20 })
60
+
61
+ // Concurrency: Max parallel browser instances
62
+ .concurrency(10)
63
+
64
+ // Masking: Automatic and manual masking
65
+ .mask(['.ads', '#modal']) // Manual selectors
66
+ // Tip: Adding 'data-vr-mask' to your HTML elements masks them automatically!
67
+
68
+ // Execute: Compiles manifest and triggers the API
69
+ .run();
70
+
71
+ const result = await job.waitForCompletion();
72
+ const summary = await job.getSummary();
73
+
74
+ console.log(`Job ${job.jobId} finished. Overall Score: ${summary.overallScore}`);
75
+ ```
76
+
77
+ ## CLI Usage
78
+
79
+ The `regressionbot` CLI is the easiest way to interact with the API from your terminal or CI scripts.
80
+
81
+ ### Authentication
82
+
83
+ The CLI looks for the following environment variables:
84
+ - `REGRESSIONBOT_API_KEY`: Your project API key.
85
+ - `REGRESSIONBOT_API_URL`: (Optional) Override the default API endpoint.
86
+
87
+ ### Commands
88
+
89
+ #### 1. Quick Check
90
+ Test a single URL against its established baseline.
91
+ ```bash
92
+ npx regressionbot https://example.com --project my-site --on "Desktop Chrome, iPhone 12"
93
+ ```
94
+
95
+ #### 2. Sitemap Scan
96
+ Test an entire site using glob patterns.
97
+ ```bash
98
+ npx regressionbot https://example.com --scan "/**" --exclude "/admin/**" --concurrency 20
99
+ ```
100
+
101
+ #### 3. Job Summary
102
+ Get detailed results and diff URLs for a completed job.
103
+ ```bash
104
+ npx regressionbot summary <jobId>
105
+ ```
106
+
107
+ Add the `--download` flag to save regression images locally:
108
+ ```bash
109
+ npx regressionbot summary <jobId> --download
110
+ ```
111
+
112
+ #### 4. Approve Changes
113
+ Promote the current screenshots of a job to be the new baselines.
114
+ ```bash
115
+ npx regressionbot approve <jobId>
116
+ ```
117
+
118
+ ## Development
119
+
120
+ ### Versioning
121
+
122
+ ```bash
123
+ npm version patch # or minor/major
124
+ npm publish
125
+ ```
package/dist/cli.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
package/dist/cli.js ADDED
@@ -0,0 +1,236 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ const index_1 = require("./index");
5
+ function parseArgs(args) {
6
+ const options = { _: [] };
7
+ for (let i = 0; i < args.length; i++) {
8
+ const arg = args[i];
9
+ if (arg.startsWith('--')) {
10
+ const key = arg.slice(2);
11
+ const value = args[i + 1];
12
+ if (value && !value.startsWith('--')) {
13
+ options[key] = value;
14
+ i++;
15
+ }
16
+ else {
17
+ options[key] = true;
18
+ }
19
+ }
20
+ else {
21
+ options._.push(arg);
22
+ }
23
+ }
24
+ return options;
25
+ }
26
+ const argv = parseArgs(process.argv.slice(2));
27
+ const command = argv._[0];
28
+ const param = argv._[1];
29
+ const sdk = new index_1.Visual();
30
+ async function main() {
31
+ if (!command || command === 'help' || command === '--help' || command === '-h') {
32
+ showHelp();
33
+ return;
34
+ }
35
+ try {
36
+ if (command === 'status') {
37
+ if (!param)
38
+ throw new Error('Job ID is required for status command.');
39
+ await checkStatus(param);
40
+ }
41
+ else if (command === 'summary') {
42
+ if (!param)
43
+ throw new Error('Job ID is required for summary command.');
44
+ await showSummary(param, argv);
45
+ }
46
+ else if (command === 'approve') {
47
+ if (!param)
48
+ throw new Error('Job ID is required for approve command.');
49
+ await approveJob(param);
50
+ }
51
+ else if (command.startsWith('http')) {
52
+ // Implicit test command
53
+ await startJob(command, argv);
54
+ }
55
+ else {
56
+ console.error(`Unknown command: ${command}`);
57
+ showHelp();
58
+ process.exit(1);
59
+ }
60
+ }
61
+ catch (error) {
62
+ console.error(`
63
+ Error: ${error.message}`);
64
+ process.exit(1);
65
+ }
66
+ }
67
+ function showHelp() {
68
+ console.log(`
69
+ RegressionBot CLI
70
+
71
+ Usage:
72
+ npx regressionbot <url> Quick test a URL.
73
+ npx regressionbot status <jobId> Check the status of a specific job.
74
+ npx regressionbot summary <jobId> Get detailed results and diff URLs.
75
+ Use --download to save images locally.
76
+ npx regressionbot approve <jobId> Approve a job's results as new baselines.
77
+
78
+ Options for <url>:
79
+ --project <id> Required project ID.
80
+ --against <url> Base origin to compare against.
81
+ --sitemap <url> Explicit sitemap.xml location.
82
+ --on <devices> Comma-separated device names (e.g. "Desktop Chrome,iPhone 12").
83
+ --scan <pattern> Glob pattern to scan in sitemap (e.g. "/blog/**").
84
+ --exclude <patterns> Comma-separated glob patterns to exclude.
85
+ --concurrency <n> Max concurrent workers (default 10).
86
+ --auto-approve Automatically approve results as new baselines.
87
+ --mask <selectors> Comma-separated CSS selectors to hide (e.g. ".ad,#popup").
88
+
89
+ Environment Variables:
90
+ REGRESSIONBOT_API_KEY Override the API Key.
91
+ REGRESSIONBOT_API_URL Override the API URL.
92
+ `);
93
+ }
94
+ async function startJob(url, options) {
95
+ console.log(`🚀 Initializing visual test...`);
96
+ const projectId = options.project || new URL(url).hostname;
97
+ const devices = options.on ? options.on.split(',').map((s) => s.trim()) : ['Desktop Chrome'];
98
+ const builder = sdk.test(url)
99
+ .forProject(projectId)
100
+ .on(devices)
101
+ .concurrency(Number(options.concurrency) || 10);
102
+ if (options.against) {
103
+ builder.against(options.against);
104
+ }
105
+ if (options.sitemap) {
106
+ builder.sitemap(options.sitemap);
107
+ }
108
+ if (options.scan) {
109
+ const exclude = options.exclude ? options.exclude.split(',').map((s) => s.trim()) : [];
110
+ builder.scan(options.scan, { exclude });
111
+ }
112
+ if (options['auto-approve']) {
113
+ builder.autoApprove(true);
114
+ }
115
+ if (options.mask) {
116
+ const selectors = options.mask.split(',').map((s) => s.trim());
117
+ builder.mask(selectors);
118
+ }
119
+ const job = await builder.run();
120
+ console.log(`✅ Job started! ID: ${job.jobId}`);
121
+ console.log(`📊 Project: ${projectId}`);
122
+ console.log(`📱 Matrix: ${devices.join(', ')}`);
123
+ if (options.scan) {
124
+ console.log(`🔍 Scan: ${options.scan} (Exclude: ${options.exclude || 'none'})`);
125
+ }
126
+ console.log(`
127
+ Waiting for completion...
128
+ `);
129
+ const result = await job.waitForCompletion(2000, (status) => {
130
+ const progress = status.progress || { percent: '0' };
131
+ process.stdout.write(`\r Status: ${status.status} (${progress.percent}%)`);
132
+ });
133
+ console.log('\n\n✅ Job Completed.');
134
+ const summary = await job.getSummary();
135
+ console.log(`Overall Stability Score: ${summary.overallScore}/100`);
136
+ console.log(`Total Tasks: ${summary.totalUrls}`);
137
+ console.log(`Regressions: ${summary.regressionCount}`);
138
+ console.log(`New Baselines: ${summary.newBaselineCount}`);
139
+ console.log(`Errors: ${summary.errorCount}`);
140
+ if (summary.newBaselineCount > 0) {
141
+ console.log('\n✨ New Baselines Created:');
142
+ summary.newBaselines.forEach((nb) => {
143
+ console.log(`- ${nb.url} [${nb.variantName}]`);
144
+ });
145
+ }
146
+ if (summary.regressionCount > 0) {
147
+ console.log('\n❌ Regressions found:');
148
+ summary.regressions.forEach((r) => {
149
+ console.log(`- ${r.url} [${r.variantName}] (Score: ${r.score.toFixed(2)})`);
150
+ console.log(` Diff: ${r.diffUrl}`);
151
+ });
152
+ console.log(`\nTo approve these changes, run:\n npx regressionbot approve ${job.jobId}`);
153
+ process.exit(1);
154
+ }
155
+ else if (summary.errorCount > 0) {
156
+ console.log('\n⚠️ Errors encountered:');
157
+ summary.errors.forEach((e) => {
158
+ console.log(`- ${e.url}: ${e.errorMessage}`);
159
+ });
160
+ process.exit(1);
161
+ }
162
+ else {
163
+ console.log('\n✨ No regressions found. All good!');
164
+ }
165
+ }
166
+ async function checkStatus(jobId) {
167
+ const job = sdk.job(jobId);
168
+ const status = await job.getStatus();
169
+ console.log(JSON.stringify(status, null, 2));
170
+ }
171
+ async function showSummary(jobId, options = {}) {
172
+ const job = sdk.job(jobId);
173
+ const summary = await job.getSummary();
174
+ console.log(`
175
+ Job Summary: ${jobId}
176
+ Status: ${summary.status}
177
+ Overall Score: ${summary.overallScore}/100
178
+ Execution Time: ${summary.executionTime}s
179
+ Total Tasks: ${summary.totalUrls}
180
+ Regressions: ${summary.regressionCount}
181
+ Matches: ${summary.matchCount}
182
+ Errors: ${summary.errorCount}
183
+ `);
184
+ if (summary.collageUrl) {
185
+ console.log(`Collage: ${summary.collageUrl}`);
186
+ if (options.download) {
187
+ const fs = require('fs');
188
+ const path = require('path');
189
+ const dir = path.join(process.cwd(), 'regressions', jobId);
190
+ if (!fs.existsSync(dir))
191
+ fs.mkdirSync(dir, { recursive: true });
192
+ const res = await fetch(summary.collageUrl);
193
+ const buffer = Buffer.from(await res.arrayBuffer());
194
+ const filePath = path.join(dir, 'collage.jpg');
195
+ fs.writeFileSync(filePath, buffer);
196
+ console.log(`💾 Downloaded collage to: ${filePath}\n`);
197
+ }
198
+ }
199
+ if (summary.regressionCount > 0) {
200
+ console.log('❌ Regressions found:');
201
+ for (const r of summary.regressions) {
202
+ console.log(`- ${r.url} [${r.variantName}] (Score: ${r.score.toFixed(2)})`);
203
+ console.log(` Diff: ${r.diffUrl}`);
204
+ if (options.download) {
205
+ const fs = require('fs');
206
+ const path = require('path');
207
+ const dir = path.join(process.cwd(), 'regressions', jobId, r.variantName);
208
+ if (!fs.existsSync(dir))
209
+ fs.mkdirSync(dir, { recursive: true });
210
+ const download = async (url, name) => {
211
+ const res = await fetch(url);
212
+ const buffer = Buffer.from(await res.arrayBuffer());
213
+ const filePath = path.join(dir, name);
214
+ fs.writeFileSync(filePath, buffer);
215
+ console.log(` 💾 Downloaded: ${name}`);
216
+ };
217
+ await download(r.baselineUrl, 'baseline.png');
218
+ await download(r.currentUrl, 'current.png');
219
+ await download(r.diffUrl, 'diff.png');
220
+ }
221
+ }
222
+ }
223
+ if (summary.errorCount > 0) {
224
+ console.log('\n⚠️ Errors encountered:');
225
+ summary.errors.forEach((e) => {
226
+ console.log(`- ${e.url}: ${e.errorMessage}`);
227
+ });
228
+ }
229
+ }
230
+ async function approveJob(jobId) {
231
+ console.log(`Approving baselines for job: ${jobId}...`);
232
+ const job = sdk.job(jobId);
233
+ const res = await job.approve();
234
+ console.log(`Success! ${res.message}`);
235
+ }
236
+ main();
@@ -0,0 +1,56 @@
1
+ import { JobStatus, JobSummary } from './types';
2
+ export declare class Visual {
3
+ private apiKey;
4
+ private apiUrl;
5
+ constructor(apiKey?: string, apiUrl?: string);
6
+ /**
7
+ * Set the candidate URL/Origin to test.
8
+ */
9
+ test(origin: string): JobBuilder;
10
+ /**
11
+ * Get a handle to an existing job.
12
+ */
13
+ job(jobId: string): JobHandle;
14
+ _request<T>(path: string, method?: string, body?: any): Promise<T>;
15
+ }
16
+ export declare class JobBuilder {
17
+ private sdk;
18
+ private manifest;
19
+ constructor(sdk: Visual, testOrigin: string);
20
+ against(origin: string): this;
21
+ sitemap(url: string): this;
22
+ forProject(id: string): this;
23
+ /**
24
+ * Define the matrix: list of Playwright devices or viewport names.
25
+ */
26
+ on(variants: string[]): this;
27
+ /**
28
+ * Add a specific page to the test scope.
29
+ */
30
+ check(path: string, label?: string): this;
31
+ /**
32
+ * Add a discovery rule to scan the sitemap.
33
+ */
34
+ scan(pattern: string, options?: {
35
+ limit?: number;
36
+ exclude?: string[];
37
+ }): this;
38
+ concurrency(n: number): this;
39
+ autoApprove(val?: boolean): this;
40
+ mask(selectors: string[]): this;
41
+ /**
42
+ * Compiles the manifest and triggers the API.
43
+ */
44
+ run(): Promise<JobHandle>;
45
+ }
46
+ export declare class JobHandle {
47
+ private sdk;
48
+ jobId: string;
49
+ constructor(sdk: Visual, jobId: string);
50
+ getStatus(): Promise<JobStatus>;
51
+ getSummary(): Promise<JobSummary>;
52
+ approve(): Promise<{
53
+ message: string;
54
+ }>;
55
+ waitForCompletion(intervalMs?: number, callback?: (status: JobStatus) => void): Promise<JobStatus>;
56
+ }
package/dist/index.js ADDED
@@ -0,0 +1,164 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.JobHandle = exports.JobBuilder = exports.Visual = void 0;
4
+ class Visual {
5
+ apiKey;
6
+ apiUrl;
7
+ constructor(apiKey, apiUrl) {
8
+ this.apiKey = apiKey || process.env.REGRESSIONBOT_API_KEY || "";
9
+ this.apiUrl = apiUrl || process.env.REGRESSIONBOT_API_URL || "https://api.regressionbot.com";
10
+ if (!this.apiKey) {
11
+ console.warn("Warning: No API Key provided. Set REGRESSIONBOT_API_KEY environment variable or pass it to the constructor.");
12
+ }
13
+ if (this.apiUrl.endsWith('/')) {
14
+ this.apiUrl = this.apiUrl.slice(0, -1);
15
+ }
16
+ }
17
+ /**
18
+ * Set the candidate URL/Origin to test.
19
+ */
20
+ test(origin) {
21
+ return new JobBuilder(this, origin);
22
+ }
23
+ /**
24
+ * Get a handle to an existing job.
25
+ */
26
+ job(jobId) {
27
+ return new JobHandle(this, jobId);
28
+ }
29
+ // Internal fetch wrapper
30
+ async _request(path, method = 'GET', body) {
31
+ const headers = {
32
+ 'Content-Type': 'application/json',
33
+ 'x-api-key': this.apiKey
34
+ };
35
+ const response = await fetch(`${this.apiUrl}${path}`, {
36
+ method,
37
+ headers,
38
+ body: body ? JSON.stringify(body) : undefined
39
+ });
40
+ if (!response.ok) {
41
+ const errorText = await response.text();
42
+ throw new Error(`API Error ${response.status}: ${errorText}`);
43
+ }
44
+ return response.json();
45
+ }
46
+ }
47
+ exports.Visual = Visual;
48
+ class JobBuilder {
49
+ sdk;
50
+ manifest;
51
+ constructor(sdk, testOrigin) {
52
+ this.sdk = sdk;
53
+ this.manifest = {
54
+ testOrigin: testOrigin.replace(/\/$/, ''),
55
+ variants: [],
56
+ checks: [],
57
+ scans: [],
58
+ concurrency: 10
59
+ };
60
+ }
61
+ against(origin) {
62
+ this.manifest.baseOrigin = origin.replace(/\/$/, '');
63
+ return this;
64
+ }
65
+ sitemap(url) {
66
+ this.manifest.sitemapUrl = url;
67
+ return this;
68
+ }
69
+ forProject(id) {
70
+ this.manifest.projectId = id;
71
+ return this;
72
+ }
73
+ /**
74
+ * Define the matrix: list of Playwright devices or viewport names.
75
+ */
76
+ on(variants) {
77
+ this.manifest.variants.push(...variants);
78
+ return this;
79
+ }
80
+ /**
81
+ * Add a specific page to the test scope.
82
+ */
83
+ check(path, label) {
84
+ this.manifest.checks.push({ path, label });
85
+ return this;
86
+ }
87
+ /**
88
+ * Add a discovery rule to scan the sitemap.
89
+ */
90
+ scan(pattern, options) {
91
+ this.manifest.scans.push({ pattern, options });
92
+ return this;
93
+ }
94
+ concurrency(n) {
95
+ this.manifest.concurrency = n;
96
+ return this;
97
+ }
98
+ autoApprove(val = true) {
99
+ this.manifest.autoApprove = val;
100
+ return this;
101
+ }
102
+ mask(selectors) {
103
+ this.manifest.masks = selectors;
104
+ return this;
105
+ }
106
+ /**
107
+ * Compiles the manifest and triggers the API.
108
+ */
109
+ async run() {
110
+ if (!this.manifest.projectId) {
111
+ throw new Error('Project ID is required. Use .forProject("id")');
112
+ }
113
+ // If no checks/scans provided, default to root
114
+ if (this.manifest.checks.length === 0 && this.manifest.scans.length === 0) {
115
+ this.manifest.checks.push({ path: '/', label: 'Home' });
116
+ }
117
+ const payload = {
118
+ project: this.manifest.projectId,
119
+ testOrigin: this.manifest.testOrigin,
120
+ sitemapUrl: this.manifest.sitemapUrl,
121
+ baseOrigin: this.manifest.baseOrigin,
122
+ devices: this.manifest.variants,
123
+ paths: this.manifest.checks,
124
+ scans: this.manifest.scans,
125
+ concurrency: this.manifest.concurrency,
126
+ autoApprove: this.manifest.autoApprove,
127
+ masks: this.manifest.masks
128
+ };
129
+ const res = await this.sdk._request('/crawl', 'POST', payload);
130
+ return new JobHandle(this.sdk, res.jobId);
131
+ }
132
+ }
133
+ exports.JobBuilder = JobBuilder;
134
+ class JobHandle {
135
+ sdk;
136
+ jobId;
137
+ constructor(sdk, jobId) {
138
+ this.sdk = sdk;
139
+ this.jobId = jobId;
140
+ }
141
+ async getStatus() {
142
+ return this.sdk._request(`/job/${this.jobId}`);
143
+ }
144
+ async getSummary() {
145
+ return this.sdk._request(`/job/${this.jobId}/summary`);
146
+ }
147
+ async approve() {
148
+ return this.sdk._request('/approve', 'POST', { jobId: this.jobId });
149
+ }
150
+ async waitForCompletion(intervalMs = 2000, callback) {
151
+ await new Promise(resolve => setTimeout(resolve, 3000));
152
+ while (true) {
153
+ const status = await this.getStatus();
154
+ if (callback)
155
+ callback(status);
156
+ if (status.status === 'COMPLETED' || status.status === 'APPROVED')
157
+ return status;
158
+ if (status.status === 'FAILED')
159
+ throw new Error(`Job Failed: ${status.error}`);
160
+ await new Promise(resolve => setTimeout(resolve, intervalMs));
161
+ }
162
+ }
163
+ }
164
+ exports.JobHandle = JobHandle;
@@ -0,0 +1,89 @@
1
+ export interface VRConfig {
2
+ apiKey?: string;
3
+ apiUrl?: string;
4
+ }
5
+ export interface Viewport {
6
+ width: number;
7
+ height: number;
8
+ }
9
+ export declare const Viewports: {
10
+ readonly DESKTOP: {
11
+ readonly width: 1920;
12
+ readonly height: 1080;
13
+ };
14
+ readonly LAPTOP: {
15
+ readonly width: 1366;
16
+ readonly height: 768;
17
+ };
18
+ readonly TABLET: {
19
+ readonly width: 768;
20
+ readonly height: 1024;
21
+ };
22
+ readonly MOBILE: {
23
+ readonly width: 375;
24
+ readonly height: 667;
25
+ };
26
+ };
27
+ export interface JobResult {
28
+ url: string;
29
+ status: 'SUCCESS' | 'ERROR';
30
+ diffCount?: number;
31
+ diffPercentage?: number;
32
+ score?: number;
33
+ currentKey?: string;
34
+ baselineKey?: string;
35
+ diffKey?: string;
36
+ isNewBaseline?: boolean;
37
+ }
38
+ export interface JobStatus {
39
+ jobId: string;
40
+ status: 'PROCESSING' | 'COMPLETED' | 'APPROVED' | 'FAILED' | 'INITIALIZING';
41
+ error?: string;
42
+ progress?: {
43
+ total: number;
44
+ completed: number;
45
+ percent: string;
46
+ };
47
+ executionTime?: number;
48
+ results?: JobResult[];
49
+ createdAt?: string;
50
+ collageKey?: string;
51
+ }
52
+ export interface JobSummary {
53
+ jobId: string;
54
+ status: string;
55
+ totalUrls: number;
56
+ completedCount: number;
57
+ overallScore: number;
58
+ executionTime: number;
59
+ regressionCount: number;
60
+ matchCount: number;
61
+ newBaselineCount: number;
62
+ errorCount: number;
63
+ collageUrl?: string;
64
+ regressions: Array<{
65
+ url: string;
66
+ variantName: string;
67
+ diffCount: number;
68
+ score: number;
69
+ baselineUrl: string;
70
+ currentUrl: string;
71
+ diffUrl: string;
72
+ }>;
73
+ matches: Array<{
74
+ url: string;
75
+ variantName: string;
76
+ score: number;
77
+ baselineUrl: string;
78
+ currentUrl: string;
79
+ }>;
80
+ newBaselines: Array<{
81
+ url: string;
82
+ variantName: string;
83
+ }>;
84
+ errors: Array<{
85
+ url: string;
86
+ errorMessage: string;
87
+ score: number;
88
+ }>;
89
+ }
package/dist/types.js ADDED
@@ -0,0 +1,9 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Viewports = void 0;
4
+ exports.Viewports = {
5
+ DESKTOP: { width: 1920, height: 1080 },
6
+ LAPTOP: { width: 1366, height: 768 },
7
+ TABLET: { width: 768, height: 1024 },
8
+ MOBILE: { width: 375, height: 667 }
9
+ };
package/package.json ADDED
@@ -0,0 +1,39 @@
1
+ {
2
+ "name": "regressionbot",
3
+ "version": "0.0.1",
4
+ "description": "The official SDK for regressionbot.com - the simplest way to automate visual regression testing.",
5
+ "homepage": "https://regressionbot.com",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "git+https://github.com/cbestall/vr-api.git",
9
+ "directory": "sdk"
10
+ },
11
+ "bugs": {
12
+ "url": "https://github.com/cbestall/vr-api/issues"
13
+ },
14
+ "main": "dist/index.js",
15
+ "types": "dist/index.d.ts",
16
+ "bin": {
17
+ "regressionbot": "dist/cli.js"
18
+ },
19
+ "files": [
20
+ "dist"
21
+ ],
22
+ "scripts": {
23
+ "build": "tsc",
24
+ "dev": "tsc -w",
25
+ "prepublishOnly": "npm run build"
26
+ },
27
+ "keywords": [
28
+ "visual-regression",
29
+ "testing",
30
+ "sdk",
31
+ "cli"
32
+ ],
33
+ "author": "RegressionBot (https://regressionbot.com)",
34
+ "license": "ISC",
35
+ "devDependencies": {
36
+ "@types/node": "^20.0.0",
37
+ "typescript": "^5.0.0"
38
+ }
39
+ }