@clawdtesting/puppeteer 1.0.0
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 +257 -0
- package/dist/client.d.ts +27 -0
- package/dist/client.js +66 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +22 -0
- package/dist/session.d.ts +42 -0
- package/dist/session.js +107 -0
- package/dist/types.d.ts +47 -0
- package/dist/types.js +2 -0
- package/examples/basic.js +50 -0
- package/examples/session.js +72 -0
- package/package.json +36 -0
- package/src/client.ts +78 -0
- package/src/index.ts +3 -0
- package/src/session.ts +147 -0
- package/src/types.ts +51 -0
- package/tsconfig.json +18 -0
package/README.md
ADDED
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
# ClawdTest - Puppeteer SDK
|
|
2
|
+
|
|
3
|
+
Official Puppeteer SDK for ClawdTest Platform.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @clawdtesting/puppeteer puppeteer
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Quick Start
|
|
12
|
+
|
|
13
|
+
```javascript
|
|
14
|
+
const puppeteer = require('puppeteer');
|
|
15
|
+
const { ClawdTest } = require('@clawdtesting/puppeteer');
|
|
16
|
+
|
|
17
|
+
// Initialize client
|
|
18
|
+
const clawdTest = new ClawdTest({
|
|
19
|
+
apiKey: 'your-api-key',
|
|
20
|
+
apiUrl: 'http://localhost:3001/api/v1',
|
|
21
|
+
projectId: 'your-project-id',
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
(async () => {
|
|
25
|
+
// Launch browser
|
|
26
|
+
const browser = await puppeteer.launch();
|
|
27
|
+
const page = await browser.newPage();
|
|
28
|
+
|
|
29
|
+
// Navigate to page
|
|
30
|
+
await page.goto('https://example.com');
|
|
31
|
+
|
|
32
|
+
// Quick check (single screenshot)
|
|
33
|
+
const result = await clawdTest.check(page, 'Homepage Test', 'Landing Page');
|
|
34
|
+
|
|
35
|
+
console.log('Test result:', result.status); // 'passed', 'failed', or 'new'
|
|
36
|
+
|
|
37
|
+
await browser.close();
|
|
38
|
+
})();
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## Advanced Usage
|
|
42
|
+
|
|
43
|
+
### Session-based Testing
|
|
44
|
+
|
|
45
|
+
```javascript
|
|
46
|
+
const session = await clawdTest.session(page, {
|
|
47
|
+
testName: 'User Flow Test',
|
|
48
|
+
viewport: { width: 1920, height: 1080 },
|
|
49
|
+
baselineBranch: 'main',
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
// Take multiple screenshots
|
|
53
|
+
await page.goto('https://example.com');
|
|
54
|
+
await session.check('Homepage');
|
|
55
|
+
|
|
56
|
+
await page.click('#login-button');
|
|
57
|
+
await session.check('Login Modal');
|
|
58
|
+
|
|
59
|
+
await page.type('#username', 'user@example.com');
|
|
60
|
+
await page.type('#password', 'password123');
|
|
61
|
+
await session.check('Login Form Filled');
|
|
62
|
+
|
|
63
|
+
await page.click('#submit');
|
|
64
|
+
await session.checkAfterNavigation('Dashboard');
|
|
65
|
+
|
|
66
|
+
// Complete session
|
|
67
|
+
const sessionResult = await session.close();
|
|
68
|
+
|
|
69
|
+
console.log('Session completed:', {
|
|
70
|
+
status: sessionResult.status,
|
|
71
|
+
totalSteps: sessionResult.totalSteps,
|
|
72
|
+
passedSteps: sessionResult.passedSteps,
|
|
73
|
+
failedSteps: sessionResult.failedSteps,
|
|
74
|
+
newSteps: sessionResult.newSteps,
|
|
75
|
+
});
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
### Screenshot Options
|
|
79
|
+
|
|
80
|
+
```javascript
|
|
81
|
+
// Full page screenshot
|
|
82
|
+
await session.check({
|
|
83
|
+
stepName: 'Full Page',
|
|
84
|
+
fullPage: true,
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
// Specific element
|
|
88
|
+
await session.check({
|
|
89
|
+
stepName: 'Header',
|
|
90
|
+
selector: '#header',
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
// Custom clip region
|
|
94
|
+
await session.check({
|
|
95
|
+
stepName: 'Hero Section',
|
|
96
|
+
clip: { x: 0, y: 0, width: 1200, height: 600 },
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
// With metadata
|
|
100
|
+
await session.check({
|
|
101
|
+
stepName: 'Product Page',
|
|
102
|
+
metadata: {
|
|
103
|
+
productId: '12345',
|
|
104
|
+
category: 'electronics',
|
|
105
|
+
},
|
|
106
|
+
});
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
### Multiple Checks
|
|
110
|
+
|
|
111
|
+
```javascript
|
|
112
|
+
const results = await session.checkMultiple([
|
|
113
|
+
'Homepage',
|
|
114
|
+
{ stepName: 'Header', selector: '#header' },
|
|
115
|
+
{ stepName: 'Footer', selector: '#footer' },
|
|
116
|
+
{ stepName: 'Sidebar', selector: '.sidebar' },
|
|
117
|
+
]);
|
|
118
|
+
|
|
119
|
+
results.forEach((result) => {
|
|
120
|
+
console.log(`${result.stepId}: ${result.status}`);
|
|
121
|
+
});
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
### Wait for Elements
|
|
125
|
+
|
|
126
|
+
```javascript
|
|
127
|
+
// Wait for selector before checking
|
|
128
|
+
await session.checkAfterSelector('Product List', '.product-grid');
|
|
129
|
+
|
|
130
|
+
// Wait for navigation before checking
|
|
131
|
+
await page.click('#next-page');
|
|
132
|
+
await session.checkAfterNavigation('Page 2');
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
## Configuration
|
|
136
|
+
|
|
137
|
+
### ClawdTestConfig
|
|
138
|
+
|
|
139
|
+
```typescript
|
|
140
|
+
interface ClawdTestConfig {
|
|
141
|
+
apiKey: string; // Required: Your API key
|
|
142
|
+
apiUrl?: string; // Optional: API endpoint (default: http://localhost:3001/api/v1)
|
|
143
|
+
projectId: string; // Required: Project ID
|
|
144
|
+
browserName?: string; // Optional: Browser name (default: 'chromium')
|
|
145
|
+
viewport?: { // Optional: Default viewport
|
|
146
|
+
width: number;
|
|
147
|
+
height: number;
|
|
148
|
+
};
|
|
149
|
+
baselineBranch?: string; // Optional: Baseline branch (default: 'main')
|
|
150
|
+
}
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
### SessionOptions
|
|
154
|
+
|
|
155
|
+
```typescript
|
|
156
|
+
interface SessionOptions {
|
|
157
|
+
testName: string; // Required: Test name
|
|
158
|
+
browserName?: string; // Optional: Override browser name
|
|
159
|
+
viewport?: { // Optional: Override viewport
|
|
160
|
+
width: number;
|
|
161
|
+
height: number;
|
|
162
|
+
};
|
|
163
|
+
baselineBranch?: string; // Optional: Override baseline branch
|
|
164
|
+
}
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
### CheckOptions
|
|
168
|
+
|
|
169
|
+
```typescript
|
|
170
|
+
interface CheckOptions {
|
|
171
|
+
stepName: string; // Required: Step name
|
|
172
|
+
fullPage?: boolean; // Optional: Full page screenshot
|
|
173
|
+
selector?: string; // Optional: CSS selector for element
|
|
174
|
+
clip?: { // Optional: Screenshot region
|
|
175
|
+
x: number;
|
|
176
|
+
y: number;
|
|
177
|
+
width: number;
|
|
178
|
+
height: number;
|
|
179
|
+
};
|
|
180
|
+
metadata?: Record<string, any>; // Optional: Custom metadata
|
|
181
|
+
}
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
## API Reference
|
|
185
|
+
|
|
186
|
+
### ClawdTest Class
|
|
187
|
+
|
|
188
|
+
#### `constructor(config: ClawdTestConfig)`
|
|
189
|
+
Create a new ClawdTest client.
|
|
190
|
+
|
|
191
|
+
#### `async session(page: Page, options: SessionOptions): Promise<ClawdTestSession>`
|
|
192
|
+
Create a new test session.
|
|
193
|
+
|
|
194
|
+
#### `async check(page: Page, testName: string, stepName: string)`
|
|
195
|
+
Quick check - creates session, takes screenshot, and closes session.
|
|
196
|
+
|
|
197
|
+
#### `async getSession(sessionId: string)`
|
|
198
|
+
Get session details.
|
|
199
|
+
|
|
200
|
+
### ClawdTestSession Class
|
|
201
|
+
|
|
202
|
+
#### `async check(stepNameOrOptions: string | CheckOptions): Promise<StepResult>`
|
|
203
|
+
Take a screenshot and compare with baseline.
|
|
204
|
+
|
|
205
|
+
#### `async checkMultiple(checks: Array<string | CheckOptions>): Promise<StepResult[]>`
|
|
206
|
+
Check multiple elements in sequence.
|
|
207
|
+
|
|
208
|
+
#### `async checkAfterNavigation(stepName: string, options?: Omit<CheckOptions, 'stepName'>): Promise<StepResult>`
|
|
209
|
+
Wait for navigation and then check.
|
|
210
|
+
|
|
211
|
+
#### `async checkAfterSelector(stepName: string, selector: string, options?: Omit<CheckOptions, 'stepName' | 'selector'>): Promise<StepResult>`
|
|
212
|
+
Wait for selector and then check.
|
|
213
|
+
|
|
214
|
+
#### `async close(): Promise<SessionResult>`
|
|
215
|
+
Complete the session.
|
|
216
|
+
|
|
217
|
+
#### `getSessionId(): string`
|
|
218
|
+
Get current session ID.
|
|
219
|
+
|
|
220
|
+
#### `getStepCount(): number`
|
|
221
|
+
Get number of steps taken.
|
|
222
|
+
|
|
223
|
+
## TypeScript Support
|
|
224
|
+
|
|
225
|
+
This SDK is written in TypeScript and includes type definitions.
|
|
226
|
+
|
|
227
|
+
```typescript
|
|
228
|
+
import { ClawdTest, ClawdTestSession, StepResult } from '@clawdtesting/puppeteer';
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
## Error Handling
|
|
232
|
+
|
|
233
|
+
```javascript
|
|
234
|
+
try {
|
|
235
|
+
const result = await session.check('Homepage');
|
|
236
|
+
|
|
237
|
+
if (result.status === 'failed') {
|
|
238
|
+
console.error('Visual regression detected!');
|
|
239
|
+
console.log('Diff score:', result.diffScore);
|
|
240
|
+
console.log('Diff image:', result.diffImageUrl);
|
|
241
|
+
}
|
|
242
|
+
} catch (error) {
|
|
243
|
+
console.error('Test failed:', error.message);
|
|
244
|
+
}
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
## Examples
|
|
248
|
+
|
|
249
|
+
See the [examples](./examples) directory for more usage examples:
|
|
250
|
+
- Basic usage
|
|
251
|
+
- CI/CD integration
|
|
252
|
+
- Custom viewport testing
|
|
253
|
+
- Multi-page flows
|
|
254
|
+
|
|
255
|
+
## License
|
|
256
|
+
|
|
257
|
+
MIT
|
package/dist/client.d.ts
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { Page } from 'puppeteer';
|
|
2
|
+
import { ClawdTestConfig, SessionOptions } from './types';
|
|
3
|
+
import { ClawdTestSession } from './session';
|
|
4
|
+
export declare class ClawdTest {
|
|
5
|
+
private apiClient;
|
|
6
|
+
private config;
|
|
7
|
+
constructor(config: ClawdTestConfig);
|
|
8
|
+
/**
|
|
9
|
+
* Create a new test session
|
|
10
|
+
*/
|
|
11
|
+
session(page: Page, options: SessionOptions): Promise<ClawdTestSession>;
|
|
12
|
+
/**
|
|
13
|
+
* Quick check - creates session, takes screenshot, and closes session
|
|
14
|
+
*/
|
|
15
|
+
check(page: Page, testName: string, stepName: string): Promise<{
|
|
16
|
+
sessionResult: import("./types").SessionResult;
|
|
17
|
+
stepId: string;
|
|
18
|
+
status: "passed" | "failed" | "new";
|
|
19
|
+
diffScore: number;
|
|
20
|
+
isNew: boolean;
|
|
21
|
+
diffImageUrl: string | null;
|
|
22
|
+
}>;
|
|
23
|
+
/**
|
|
24
|
+
* Get session details
|
|
25
|
+
*/
|
|
26
|
+
getSession(sessionId: string): Promise<any>;
|
|
27
|
+
}
|
package/dist/client.js
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.ClawdTest = void 0;
|
|
7
|
+
const axios_1 = __importDefault(require("axios"));
|
|
8
|
+
const session_1 = require("./session");
|
|
9
|
+
class ClawdTest {
|
|
10
|
+
constructor(config) {
|
|
11
|
+
this.config = {
|
|
12
|
+
apiUrl: config.apiUrl || 'http://localhost:3001/api/v1',
|
|
13
|
+
apiKey: config.apiKey,
|
|
14
|
+
projectId: config.projectId,
|
|
15
|
+
browserName: config.browserName || 'chromium',
|
|
16
|
+
viewport: config.viewport || { width: 1280, height: 720 },
|
|
17
|
+
baselineBranch: config.baselineBranch || 'main',
|
|
18
|
+
};
|
|
19
|
+
this.apiClient = axios_1.default.create({
|
|
20
|
+
baseURL: this.config.apiUrl,
|
|
21
|
+
headers: {
|
|
22
|
+
'x-api-key': this.config.apiKey,
|
|
23
|
+
'Content-Type': 'application/json',
|
|
24
|
+
},
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Create a new test session
|
|
29
|
+
*/
|
|
30
|
+
async session(page, options) {
|
|
31
|
+
const sessionOptions = {
|
|
32
|
+
browserName: options.browserName || this.config.browserName,
|
|
33
|
+
viewport: options.viewport || this.config.viewport,
|
|
34
|
+
baselineBranch: options.baselineBranch || this.config.baselineBranch,
|
|
35
|
+
};
|
|
36
|
+
const response = await this.apiClient.post('/sessions', {
|
|
37
|
+
projectId: this.config.projectId,
|
|
38
|
+
testName: options.testName,
|
|
39
|
+
browserName: sessionOptions.browserName,
|
|
40
|
+
viewport: sessionOptions.viewport,
|
|
41
|
+
baselineBranch: sessionOptions.baselineBranch,
|
|
42
|
+
});
|
|
43
|
+
const { sessionId } = response.data;
|
|
44
|
+
return new session_1.ClawdTestSession(sessionId, page, this.apiClient, sessionOptions.viewport);
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Quick check - creates session, takes screenshot, and closes session
|
|
48
|
+
*/
|
|
49
|
+
async check(page, testName, stepName) {
|
|
50
|
+
const session = await this.session(page, { testName });
|
|
51
|
+
const result = await session.check(stepName);
|
|
52
|
+
const sessionResult = await session.close();
|
|
53
|
+
return {
|
|
54
|
+
...result,
|
|
55
|
+
sessionResult,
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Get session details
|
|
60
|
+
*/
|
|
61
|
+
async getSession(sessionId) {
|
|
62
|
+
const response = await this.apiClient.get(`/sessions/${sessionId}`);
|
|
63
|
+
return response.data;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
exports.ClawdTest = ClawdTest;
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
exports.ClawdTestSession = exports.ClawdTest = void 0;
|
|
18
|
+
var client_1 = require("./client");
|
|
19
|
+
Object.defineProperty(exports, "ClawdTest", { enumerable: true, get: function () { return client_1.ClawdTest; } });
|
|
20
|
+
var session_1 = require("./session");
|
|
21
|
+
Object.defineProperty(exports, "ClawdTestSession", { enumerable: true, get: function () { return session_1.ClawdTestSession; } });
|
|
22
|
+
__exportStar(require("./types"), exports);
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { Page } from 'puppeteer';
|
|
2
|
+
import { AxiosInstance } from 'axios';
|
|
3
|
+
import { CheckOptions, StepResult, SessionResult } from './types';
|
|
4
|
+
export declare class ClawdTestSession {
|
|
5
|
+
private sessionId;
|
|
6
|
+
private page;
|
|
7
|
+
private apiClient;
|
|
8
|
+
private viewport;
|
|
9
|
+
private stepCount;
|
|
10
|
+
constructor(sessionId: string, page: Page, apiClient: AxiosInstance, viewport: {
|
|
11
|
+
width: number;
|
|
12
|
+
height: number;
|
|
13
|
+
});
|
|
14
|
+
/**
|
|
15
|
+
* Take a screenshot and compare with baseline
|
|
16
|
+
*/
|
|
17
|
+
check(stepNameOrOptions: string | CheckOptions): Promise<StepResult>;
|
|
18
|
+
/**
|
|
19
|
+
* Check multiple elements in sequence
|
|
20
|
+
*/
|
|
21
|
+
checkMultiple(checks: Array<string | CheckOptions>): Promise<StepResult[]>;
|
|
22
|
+
/**
|
|
23
|
+
* Wait for navigation and then check
|
|
24
|
+
*/
|
|
25
|
+
checkAfterNavigation(stepName: string, options?: Omit<CheckOptions, 'stepName'>): Promise<StepResult>;
|
|
26
|
+
/**
|
|
27
|
+
* Wait for selector and then check
|
|
28
|
+
*/
|
|
29
|
+
checkAfterSelector(stepName: string, selector: string, options?: Omit<CheckOptions, 'stepName' | 'selector'>): Promise<StepResult>;
|
|
30
|
+
/**
|
|
31
|
+
* Complete the session
|
|
32
|
+
*/
|
|
33
|
+
close(): Promise<SessionResult>;
|
|
34
|
+
/**
|
|
35
|
+
* Get current session ID
|
|
36
|
+
*/
|
|
37
|
+
getSessionId(): string;
|
|
38
|
+
/**
|
|
39
|
+
* Get number of steps taken
|
|
40
|
+
*/
|
|
41
|
+
getStepCount(): number;
|
|
42
|
+
}
|
package/dist/session.js
ADDED
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.ClawdTestSession = void 0;
|
|
7
|
+
const form_data_1 = __importDefault(require("form-data"));
|
|
8
|
+
class ClawdTestSession {
|
|
9
|
+
constructor(sessionId, page, apiClient, viewport) {
|
|
10
|
+
this.stepCount = 0;
|
|
11
|
+
this.sessionId = sessionId;
|
|
12
|
+
this.page = page;
|
|
13
|
+
this.apiClient = apiClient;
|
|
14
|
+
this.viewport = viewport;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Take a screenshot and compare with baseline
|
|
18
|
+
*/
|
|
19
|
+
async check(stepNameOrOptions) {
|
|
20
|
+
const options = typeof stepNameOrOptions === 'string'
|
|
21
|
+
? { stepName: stepNameOrOptions }
|
|
22
|
+
: stepNameOrOptions;
|
|
23
|
+
// Set viewport
|
|
24
|
+
await this.page.setViewport(this.viewport);
|
|
25
|
+
// Take screenshot
|
|
26
|
+
let screenshot;
|
|
27
|
+
if (options.selector) {
|
|
28
|
+
const element = await this.page.$(options.selector);
|
|
29
|
+
if (!element) {
|
|
30
|
+
throw new Error(`Element not found: ${options.selector}`);
|
|
31
|
+
}
|
|
32
|
+
screenshot = await element.screenshot({ encoding: 'binary' });
|
|
33
|
+
}
|
|
34
|
+
else if (options.clip) {
|
|
35
|
+
screenshot = await this.page.screenshot({
|
|
36
|
+
clip: options.clip,
|
|
37
|
+
encoding: 'binary',
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
else {
|
|
41
|
+
screenshot = await this.page.screenshot({
|
|
42
|
+
fullPage: options.fullPage || false,
|
|
43
|
+
encoding: 'binary',
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
// Upload screenshot to API
|
|
47
|
+
const formData = new form_data_1.default();
|
|
48
|
+
formData.append('stepName', options.stepName);
|
|
49
|
+
formData.append('screenshot', screenshot, {
|
|
50
|
+
filename: `${options.stepName}.png`,
|
|
51
|
+
contentType: 'image/png',
|
|
52
|
+
});
|
|
53
|
+
if (options.metadata) {
|
|
54
|
+
formData.append('metadata', JSON.stringify(options.metadata));
|
|
55
|
+
}
|
|
56
|
+
const response = await this.apiClient.post(`/sessions/${this.sessionId}/steps`, formData, {
|
|
57
|
+
headers: formData.getHeaders(),
|
|
58
|
+
});
|
|
59
|
+
this.stepCount++;
|
|
60
|
+
return response.data;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Check multiple elements in sequence
|
|
64
|
+
*/
|
|
65
|
+
async checkMultiple(checks) {
|
|
66
|
+
const results = [];
|
|
67
|
+
for (const check of checks) {
|
|
68
|
+
const result = await this.check(check);
|
|
69
|
+
results.push(result);
|
|
70
|
+
}
|
|
71
|
+
return results;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Wait for navigation and then check
|
|
75
|
+
*/
|
|
76
|
+
async checkAfterNavigation(stepName, options) {
|
|
77
|
+
await this.page.waitForNavigation({ waitUntil: 'networkidle0' });
|
|
78
|
+
return this.check({ stepName, ...options });
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Wait for selector and then check
|
|
82
|
+
*/
|
|
83
|
+
async checkAfterSelector(stepName, selector, options) {
|
|
84
|
+
await this.page.waitForSelector(selector);
|
|
85
|
+
return this.check({ stepName, selector, ...options });
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Complete the session
|
|
89
|
+
*/
|
|
90
|
+
async close() {
|
|
91
|
+
const response = await this.apiClient.post(`/sessions/${this.sessionId}/complete`);
|
|
92
|
+
return response.data;
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Get current session ID
|
|
96
|
+
*/
|
|
97
|
+
getSessionId() {
|
|
98
|
+
return this.sessionId;
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Get number of steps taken
|
|
102
|
+
*/
|
|
103
|
+
getStepCount() {
|
|
104
|
+
return this.stepCount;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
exports.ClawdTestSession = ClawdTestSession;
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
export interface ClawdTestConfig {
|
|
2
|
+
apiKey: string;
|
|
3
|
+
apiUrl?: string;
|
|
4
|
+
projectId: string;
|
|
5
|
+
browserName?: string;
|
|
6
|
+
viewport?: {
|
|
7
|
+
width: number;
|
|
8
|
+
height: number;
|
|
9
|
+
};
|
|
10
|
+
baselineBranch?: string;
|
|
11
|
+
}
|
|
12
|
+
export interface SessionOptions {
|
|
13
|
+
testName: string;
|
|
14
|
+
browserName?: string;
|
|
15
|
+
viewport?: {
|
|
16
|
+
width: number;
|
|
17
|
+
height: number;
|
|
18
|
+
};
|
|
19
|
+
baselineBranch?: string;
|
|
20
|
+
}
|
|
21
|
+
export interface StepResult {
|
|
22
|
+
stepId: string;
|
|
23
|
+
status: 'passed' | 'failed' | 'new';
|
|
24
|
+
diffScore: number;
|
|
25
|
+
isNew: boolean;
|
|
26
|
+
diffImageUrl: string | null;
|
|
27
|
+
}
|
|
28
|
+
export interface SessionResult {
|
|
29
|
+
sessionId: string;
|
|
30
|
+
status: 'passed' | 'failed' | 'unresolved';
|
|
31
|
+
totalSteps: number;
|
|
32
|
+
passedSteps: number;
|
|
33
|
+
failedSteps: number;
|
|
34
|
+
newSteps: number;
|
|
35
|
+
}
|
|
36
|
+
export interface CheckOptions {
|
|
37
|
+
stepName: string;
|
|
38
|
+
fullPage?: boolean;
|
|
39
|
+
selector?: string;
|
|
40
|
+
clip?: {
|
|
41
|
+
x: number;
|
|
42
|
+
y: number;
|
|
43
|
+
width: number;
|
|
44
|
+
height: number;
|
|
45
|
+
};
|
|
46
|
+
metadata?: Record<string, any>;
|
|
47
|
+
}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
const puppeteer = require('puppeteer');
|
|
2
|
+
const { ClawdTest } = require('../dist');
|
|
3
|
+
|
|
4
|
+
async function basicExample() {
|
|
5
|
+
// Initialize ClawdTest client
|
|
6
|
+
const clawdTest = new ClawdTest({
|
|
7
|
+
apiKey: process.env.CLAWDTEST_API_KEY || 'your-api-key',
|
|
8
|
+
apiUrl: process.env.CLAWDTEST_API_URL || 'http://localhost:3001/api/v1',
|
|
9
|
+
projectId: process.env.CLAWDTEST_PROJECT_ID || 'your-project-id',
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
// Launch browser
|
|
13
|
+
const browser = await puppeteer.launch({
|
|
14
|
+
headless: true,
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
const page = await browser.newPage();
|
|
18
|
+
|
|
19
|
+
try {
|
|
20
|
+
// Navigate to page
|
|
21
|
+
await page.goto('https://example.com');
|
|
22
|
+
|
|
23
|
+
// Quick check
|
|
24
|
+
const result = await clawdTest.check(
|
|
25
|
+
page,
|
|
26
|
+
'Example.com Test',
|
|
27
|
+
'Homepage',
|
|
28
|
+
);
|
|
29
|
+
|
|
30
|
+
console.log('✓ Test completed');
|
|
31
|
+
console.log(' Status:', result.status);
|
|
32
|
+
console.log(' Diff Score:', result.diffScore);
|
|
33
|
+
|
|
34
|
+
if (result.status === 'failed') {
|
|
35
|
+
console.log(' Diff Image:', result.diffImageUrl);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Exit with error code if test failed
|
|
39
|
+
if (result.sessionResult.status === 'failed') {
|
|
40
|
+
process.exit(1);
|
|
41
|
+
}
|
|
42
|
+
} catch (error) {
|
|
43
|
+
console.error('✗ Test failed:', error.message);
|
|
44
|
+
process.exit(1);
|
|
45
|
+
} finally {
|
|
46
|
+
await browser.close();
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
basicExample();
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
const puppeteer = require('puppeteer');
|
|
2
|
+
const { ClawdTest } = require('../dist');
|
|
3
|
+
|
|
4
|
+
async function sessionExample() {
|
|
5
|
+
const clawdTest = new ClawdTest({
|
|
6
|
+
apiKey: process.env.CLAWDTEST_API_KEY || 'your-api-key',
|
|
7
|
+
apiUrl: process.env.CLAWDTEST_API_URL || 'http://localhost:3001/api/v1',
|
|
8
|
+
projectId: process.env.CLAWDTEST_PROJECT_ID || 'your-project-id',
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
const browser = await puppeteer.launch({ headless: true });
|
|
12
|
+
const page = await browser.newPage();
|
|
13
|
+
|
|
14
|
+
try {
|
|
15
|
+
// Create session
|
|
16
|
+
const session = await clawdTest.session(page, {
|
|
17
|
+
testName: 'Multi-Step User Flow',
|
|
18
|
+
viewport: { width: 1920, height: 1080 },
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
console.log('Session created:', session.getSessionId());
|
|
22
|
+
|
|
23
|
+
// Step 1: Homepage
|
|
24
|
+
await page.goto('https://example.com');
|
|
25
|
+
let result = await session.check('Homepage');
|
|
26
|
+
console.log('Step 1 - Homepage:', result.status);
|
|
27
|
+
|
|
28
|
+
// Step 2: Scroll to section
|
|
29
|
+
await page.evaluate(() => window.scrollTo(0, 500));
|
|
30
|
+
result = await session.check({
|
|
31
|
+
stepName: 'Middle Section',
|
|
32
|
+
metadata: { scrollY: 500 },
|
|
33
|
+
});
|
|
34
|
+
console.log('Step 2 - Middle Section:', result.status);
|
|
35
|
+
|
|
36
|
+
// Step 3: Check specific element
|
|
37
|
+
result = await session.check({
|
|
38
|
+
stepName: 'Main Content',
|
|
39
|
+
selector: 'main',
|
|
40
|
+
});
|
|
41
|
+
console.log('Step 3 - Main Content:', result.status);
|
|
42
|
+
|
|
43
|
+
// Step 4: Full page
|
|
44
|
+
result = await session.check({
|
|
45
|
+
stepName: 'Full Page',
|
|
46
|
+
fullPage: true,
|
|
47
|
+
});
|
|
48
|
+
console.log('Step 4 - Full Page:', result.status);
|
|
49
|
+
|
|
50
|
+
// Complete session
|
|
51
|
+
const sessionResult = await session.close();
|
|
52
|
+
|
|
53
|
+
console.log('\n✓ Session completed');
|
|
54
|
+
console.log(' Total Steps:', sessionResult.totalSteps);
|
|
55
|
+
console.log(' Passed:', sessionResult.passedSteps);
|
|
56
|
+
console.log(' Failed:', sessionResult.failedSteps);
|
|
57
|
+
console.log(' New:', sessionResult.newSteps);
|
|
58
|
+
console.log(' Status:', sessionResult.status);
|
|
59
|
+
|
|
60
|
+
// Exit with error code if any failures
|
|
61
|
+
if (sessionResult.status === 'failed') {
|
|
62
|
+
process.exit(1);
|
|
63
|
+
}
|
|
64
|
+
} catch (error) {
|
|
65
|
+
console.error('✗ Test failed:', error.message);
|
|
66
|
+
process.exit(1);
|
|
67
|
+
} finally {
|
|
68
|
+
await browser.close();
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
sessionExample();
|
package/package.json
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@clawdtesting/puppeteer",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Visual Testing SDK for Puppeteer",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"scripts": {
|
|
8
|
+
"build": "tsc",
|
|
9
|
+
"dev": "tsc --watch",
|
|
10
|
+
"test": "jest",
|
|
11
|
+
"prepublish": "npm run build"
|
|
12
|
+
},
|
|
13
|
+
"keywords": [
|
|
14
|
+
"visual-testing",
|
|
15
|
+
"puppeteer",
|
|
16
|
+
"screenshot",
|
|
17
|
+
"regression-testing"
|
|
18
|
+
],
|
|
19
|
+
"author": "",
|
|
20
|
+
"license": "MIT",
|
|
21
|
+
"dependencies": {
|
|
22
|
+
"puppeteer": "^21.9.0",
|
|
23
|
+
"axios": "^1.6.5",
|
|
24
|
+
"form-data": "^4.0.0"
|
|
25
|
+
},
|
|
26
|
+
"devDependencies": {
|
|
27
|
+
"@types/node": "^20.11.0",
|
|
28
|
+
"@types/puppeteer": "^7.0.4",
|
|
29
|
+
"typescript": "^5.3.3",
|
|
30
|
+
"jest": "^29.7.0",
|
|
31
|
+
"ts-jest": "^29.1.1"
|
|
32
|
+
},
|
|
33
|
+
"peerDependencies": {
|
|
34
|
+
"puppeteer": "^21.0.0"
|
|
35
|
+
}
|
|
36
|
+
}
|
package/src/client.ts
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import axios, { AxiosInstance } from 'axios';
|
|
2
|
+
import { Page } from 'puppeteer';
|
|
3
|
+
import { ClawdTestConfig, SessionOptions } from './types';
|
|
4
|
+
import { ClawdTestSession } from './session';
|
|
5
|
+
|
|
6
|
+
export class ClawdTest {
|
|
7
|
+
private apiClient: AxiosInstance;
|
|
8
|
+
private config: Required<ClawdTestConfig>;
|
|
9
|
+
|
|
10
|
+
constructor(config: ClawdTestConfig) {
|
|
11
|
+
this.config = {
|
|
12
|
+
apiUrl: config.apiUrl || 'http://localhost:3001/api/v1',
|
|
13
|
+
apiKey: config.apiKey,
|
|
14
|
+
projectId: config.projectId,
|
|
15
|
+
browserName: config.browserName || 'chromium',
|
|
16
|
+
viewport: config.viewport || { width: 1280, height: 720 },
|
|
17
|
+
baselineBranch: config.baselineBranch || 'main',
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
this.apiClient = axios.create({
|
|
21
|
+
baseURL: this.config.apiUrl,
|
|
22
|
+
headers: {
|
|
23
|
+
'x-api-key': this.config.apiKey,
|
|
24
|
+
'Content-Type': 'application/json',
|
|
25
|
+
},
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Create a new test session
|
|
31
|
+
*/
|
|
32
|
+
async session(page: Page, options: SessionOptions): Promise<ClawdTestSession> {
|
|
33
|
+
const sessionOptions = {
|
|
34
|
+
browserName: options.browserName || this.config.browserName,
|
|
35
|
+
viewport: options.viewport || this.config.viewport,
|
|
36
|
+
baselineBranch: options.baselineBranch || this.config.baselineBranch,
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
const response = await this.apiClient.post('/sessions', {
|
|
40
|
+
projectId: this.config.projectId,
|
|
41
|
+
testName: options.testName,
|
|
42
|
+
browserName: sessionOptions.browserName,
|
|
43
|
+
viewport: sessionOptions.viewport,
|
|
44
|
+
baselineBranch: sessionOptions.baselineBranch,
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
const { sessionId } = response.data;
|
|
48
|
+
|
|
49
|
+
return new ClawdTestSession(
|
|
50
|
+
sessionId,
|
|
51
|
+
page,
|
|
52
|
+
this.apiClient,
|
|
53
|
+
sessionOptions.viewport,
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Quick check - creates session, takes screenshot, and closes session
|
|
59
|
+
*/
|
|
60
|
+
async check(page: Page, testName: string, stepName: string) {
|
|
61
|
+
const session = await this.session(page, { testName });
|
|
62
|
+
const result = await session.check(stepName);
|
|
63
|
+
const sessionResult = await session.close();
|
|
64
|
+
|
|
65
|
+
return {
|
|
66
|
+
...result,
|
|
67
|
+
sessionResult,
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Get session details
|
|
73
|
+
*/
|
|
74
|
+
async getSession(sessionId: string) {
|
|
75
|
+
const response = await this.apiClient.get(`/sessions/${sessionId}`);
|
|
76
|
+
return response.data;
|
|
77
|
+
}
|
|
78
|
+
}
|
package/src/index.ts
ADDED
package/src/session.ts
ADDED
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import { Page } from 'puppeteer';
|
|
2
|
+
import { AxiosInstance } from 'axios';
|
|
3
|
+
import FormData from 'form-data';
|
|
4
|
+
import { CheckOptions, StepResult, SessionResult } from './types';
|
|
5
|
+
|
|
6
|
+
export class ClawdTestSession {
|
|
7
|
+
private sessionId: string;
|
|
8
|
+
private page: Page;
|
|
9
|
+
private apiClient: AxiosInstance;
|
|
10
|
+
private viewport: { width: number; height: number };
|
|
11
|
+
private stepCount: number = 0;
|
|
12
|
+
|
|
13
|
+
constructor(
|
|
14
|
+
sessionId: string,
|
|
15
|
+
page: Page,
|
|
16
|
+
apiClient: AxiosInstance,
|
|
17
|
+
viewport: { width: number; height: number },
|
|
18
|
+
) {
|
|
19
|
+
this.sessionId = sessionId;
|
|
20
|
+
this.page = page;
|
|
21
|
+
this.apiClient = apiClient;
|
|
22
|
+
this.viewport = viewport;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Take a screenshot and compare with baseline
|
|
27
|
+
*/
|
|
28
|
+
async check(
|
|
29
|
+
stepNameOrOptions: string | CheckOptions,
|
|
30
|
+
): Promise<StepResult> {
|
|
31
|
+
const options: CheckOptions =
|
|
32
|
+
typeof stepNameOrOptions === 'string'
|
|
33
|
+
? { stepName: stepNameOrOptions }
|
|
34
|
+
: stepNameOrOptions;
|
|
35
|
+
|
|
36
|
+
// Set viewport
|
|
37
|
+
await this.page.setViewport(this.viewport);
|
|
38
|
+
|
|
39
|
+
// Take screenshot
|
|
40
|
+
let screenshot: Buffer;
|
|
41
|
+
|
|
42
|
+
if (options.selector) {
|
|
43
|
+
const element = await this.page.$(options.selector);
|
|
44
|
+
if (!element) {
|
|
45
|
+
throw new Error(`Element not found: ${options.selector}`);
|
|
46
|
+
}
|
|
47
|
+
screenshot = await element.screenshot({ encoding: 'binary' }) as Buffer;
|
|
48
|
+
} else if (options.clip) {
|
|
49
|
+
screenshot = await this.page.screenshot({
|
|
50
|
+
clip: options.clip,
|
|
51
|
+
encoding: 'binary',
|
|
52
|
+
}) as Buffer;
|
|
53
|
+
} else {
|
|
54
|
+
screenshot = await this.page.screenshot({
|
|
55
|
+
fullPage: options.fullPage || false,
|
|
56
|
+
encoding: 'binary',
|
|
57
|
+
}) as Buffer;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Upload screenshot to API
|
|
61
|
+
const formData = new FormData();
|
|
62
|
+
formData.append('stepName', options.stepName);
|
|
63
|
+
formData.append('screenshot', screenshot, {
|
|
64
|
+
filename: `${options.stepName}.png`,
|
|
65
|
+
contentType: 'image/png',
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
if (options.metadata) {
|
|
69
|
+
formData.append('metadata', JSON.stringify(options.metadata));
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const response = await this.apiClient.post(
|
|
73
|
+
`/sessions/${this.sessionId}/steps`,
|
|
74
|
+
formData,
|
|
75
|
+
{
|
|
76
|
+
headers: formData.getHeaders(),
|
|
77
|
+
},
|
|
78
|
+
);
|
|
79
|
+
|
|
80
|
+
this.stepCount++;
|
|
81
|
+
|
|
82
|
+
return response.data as StepResult;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Check multiple elements in sequence
|
|
87
|
+
*/
|
|
88
|
+
async checkMultiple(
|
|
89
|
+
checks: Array<string | CheckOptions>,
|
|
90
|
+
): Promise<StepResult[]> {
|
|
91
|
+
const results: StepResult[] = [];
|
|
92
|
+
|
|
93
|
+
for (const check of checks) {
|
|
94
|
+
const result = await this.check(check);
|
|
95
|
+
results.push(result);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
return results;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Wait for navigation and then check
|
|
103
|
+
*/
|
|
104
|
+
async checkAfterNavigation(
|
|
105
|
+
stepName: string,
|
|
106
|
+
options?: Omit<CheckOptions, 'stepName'>,
|
|
107
|
+
): Promise<StepResult> {
|
|
108
|
+
await this.page.waitForNavigation({ waitUntil: 'networkidle0' });
|
|
109
|
+
return this.check({ stepName, ...options });
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Wait for selector and then check
|
|
114
|
+
*/
|
|
115
|
+
async checkAfterSelector(
|
|
116
|
+
stepName: string,
|
|
117
|
+
selector: string,
|
|
118
|
+
options?: Omit<CheckOptions, 'stepName' | 'selector'>,
|
|
119
|
+
): Promise<StepResult> {
|
|
120
|
+
await this.page.waitForSelector(selector);
|
|
121
|
+
return this.check({ stepName, selector, ...options });
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Complete the session
|
|
126
|
+
*/
|
|
127
|
+
async close(): Promise<SessionResult> {
|
|
128
|
+
const response = await this.apiClient.post(
|
|
129
|
+
`/sessions/${this.sessionId}/complete`,
|
|
130
|
+
);
|
|
131
|
+
return response.data as SessionResult;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Get current session ID
|
|
136
|
+
*/
|
|
137
|
+
getSessionId(): string {
|
|
138
|
+
return this.sessionId;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Get number of steps taken
|
|
143
|
+
*/
|
|
144
|
+
getStepCount(): number {
|
|
145
|
+
return this.stepCount;
|
|
146
|
+
}
|
|
147
|
+
}
|
package/src/types.ts
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
export interface ClawdTestConfig {
|
|
2
|
+
apiKey: string;
|
|
3
|
+
apiUrl?: string;
|
|
4
|
+
projectId: string;
|
|
5
|
+
browserName?: string;
|
|
6
|
+
viewport?: {
|
|
7
|
+
width: number;
|
|
8
|
+
height: number;
|
|
9
|
+
};
|
|
10
|
+
baselineBranch?: string;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export interface SessionOptions {
|
|
14
|
+
testName: string;
|
|
15
|
+
browserName?: string;
|
|
16
|
+
viewport?: {
|
|
17
|
+
width: number;
|
|
18
|
+
height: number;
|
|
19
|
+
};
|
|
20
|
+
baselineBranch?: string;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface StepResult {
|
|
24
|
+
stepId: string;
|
|
25
|
+
status: 'passed' | 'failed' | 'new';
|
|
26
|
+
diffScore: number;
|
|
27
|
+
isNew: boolean;
|
|
28
|
+
diffImageUrl: string | null;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export interface SessionResult {
|
|
32
|
+
sessionId: string;
|
|
33
|
+
status: 'passed' | 'failed' | 'unresolved';
|
|
34
|
+
totalSteps: number;
|
|
35
|
+
passedSteps: number;
|
|
36
|
+
failedSteps: number;
|
|
37
|
+
newSteps: number;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export interface CheckOptions {
|
|
41
|
+
stepName: string;
|
|
42
|
+
fullPage?: boolean;
|
|
43
|
+
selector?: string;
|
|
44
|
+
clip?: {
|
|
45
|
+
x: number;
|
|
46
|
+
y: number;
|
|
47
|
+
width: number;
|
|
48
|
+
height: number;
|
|
49
|
+
};
|
|
50
|
+
metadata?: Record<string, any>;
|
|
51
|
+
}
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2020",
|
|
4
|
+
"module": "commonjs",
|
|
5
|
+
"lib": ["ES2020"],
|
|
6
|
+
"declaration": true,
|
|
7
|
+
"outDir": "./dist",
|
|
8
|
+
"rootDir": "./src",
|
|
9
|
+
"strict": true,
|
|
10
|
+
"esModuleInterop": true,
|
|
11
|
+
"skipLibCheck": true,
|
|
12
|
+
"forceConsistentCasingInFileNames": true,
|
|
13
|
+
"resolveJsonModule": true,
|
|
14
|
+
"moduleResolution": "node"
|
|
15
|
+
},
|
|
16
|
+
"include": ["src/**/*"],
|
|
17
|
+
"exclude": ["node_modules", "dist", "**/*.spec.ts"]
|
|
18
|
+
}
|