qa360 2.0.13 → 2.1.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/dist/core/adapters/playwright-native-adapter.d.ts +121 -0
- package/dist/core/adapters/playwright-native-adapter.js +339 -0
- package/dist/core/adapters/playwright-ui.d.ts +38 -0
- package/dist/core/adapters/playwright-ui.js +164 -4
- package/dist/core/artifacts/index.d.ts +6 -0
- package/dist/core/artifacts/index.js +6 -0
- package/dist/core/artifacts/ui-artifacts.d.ts +133 -0
- package/dist/core/artifacts/ui-artifacts.js +304 -0
- package/dist/core/index.d.ts +6 -0
- package/dist/core/index.js +9 -0
- package/dist/core/parallel/index.d.ts +6 -0
- package/dist/core/parallel/index.js +6 -0
- package/dist/core/parallel/parallel-runner.d.ts +107 -0
- package/dist/core/parallel/parallel-runner.js +192 -0
- package/dist/core/reporting/html-reporter.d.ts +119 -0
- package/dist/core/reporting/html-reporter.js +737 -0
- package/dist/core/reporting/index.d.ts +6 -0
- package/dist/core/reporting/index.js +6 -0
- package/dist/core/runner/phase3-runner.js +5 -1
- package/dist/core/vault/cas.d.ts +5 -1
- package/dist/core/vault/cas.js +6 -0
- package/dist/core/visual/index.d.ts +6 -0
- package/dist/core/visual/index.js +6 -0
- package/dist/core/visual/visual-regression.d.ts +113 -0
- package/dist/core/visual/visual-regression.js +236 -0
- package/examples/README.md +38 -0
- package/examples/crawler.yml +38 -0
- package/examples/ui-advanced.yml +49 -0
- package/package.json +1 -1
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* QA360 Playwright Native Adapter
|
|
3
|
+
*
|
|
4
|
+
* Zero-overhead adapter that uses Playwright's native API directly.
|
|
5
|
+
* This bypasses any wrapper logic and gives full access to Playwright's capabilities.
|
|
6
|
+
*
|
|
7
|
+
* Benefits:
|
|
8
|
+
* - Zero performance overhead
|
|
9
|
+
* - Full Playwright API access
|
|
10
|
+
* - Native video/trace/screenshot support
|
|
11
|
+
* - Direct access to browser context, page, locators
|
|
12
|
+
*/
|
|
13
|
+
import { Browser, BrowserContext, Page, Locator } from '@playwright/test';
|
|
14
|
+
import type { WebTarget, PackBudgets, UiTestDefinition, UiTestStep } from '../types/pack-v1.js';
|
|
15
|
+
export interface PlaywrightNativeConfig {
|
|
16
|
+
target: WebTarget;
|
|
17
|
+
budgets?: PackBudgets;
|
|
18
|
+
timeout?: number;
|
|
19
|
+
browser?: 'chromium' | 'firefox' | 'webkit';
|
|
20
|
+
headless?: boolean;
|
|
21
|
+
slowMo?: number;
|
|
22
|
+
devtools?: boolean;
|
|
23
|
+
device?: 'desktop' | 'tablet' | 'mobile' | 'custom';
|
|
24
|
+
viewport?: {
|
|
25
|
+
width: number;
|
|
26
|
+
height: number;
|
|
27
|
+
};
|
|
28
|
+
userAgent?: string;
|
|
29
|
+
locale?: string;
|
|
30
|
+
timezone?: string;
|
|
31
|
+
screenshots?: 'always' | 'only-on-failure' | 'never';
|
|
32
|
+
video?: 'always' | 'retain-on-failure' | 'never';
|
|
33
|
+
trace?: 'always' | 'retain-on-failure' | 'never' | 'on-first-failure';
|
|
34
|
+
bail?: number;
|
|
35
|
+
workers?: number;
|
|
36
|
+
ignoreHTTPSErrors?: boolean;
|
|
37
|
+
acceptDownloads?: boolean;
|
|
38
|
+
bypassCSP?: boolean;
|
|
39
|
+
}
|
|
40
|
+
export interface PlaywrightNativeResult {
|
|
41
|
+
success: boolean;
|
|
42
|
+
duration: number;
|
|
43
|
+
artifacts?: {
|
|
44
|
+
screenshots: string[];
|
|
45
|
+
videos: string[];
|
|
46
|
+
traces: string[];
|
|
47
|
+
};
|
|
48
|
+
error?: string;
|
|
49
|
+
coverage?: {
|
|
50
|
+
lines: number;
|
|
51
|
+
statements: number;
|
|
52
|
+
branches: number;
|
|
53
|
+
functions: number;
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
export interface PlaywrightNativeStepResult {
|
|
57
|
+
step: UiTestStep;
|
|
58
|
+
success: boolean;
|
|
59
|
+
duration: number;
|
|
60
|
+
error?: string;
|
|
61
|
+
screenshot?: string;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Playwright Native Adapter
|
|
65
|
+
*
|
|
66
|
+
* Provides zero-overhead access to Playwright with automatic
|
|
67
|
+
* artifact collection (screenshots, videos, traces).
|
|
68
|
+
*/
|
|
69
|
+
export declare class PlaywrightNativeAdapter {
|
|
70
|
+
private config;
|
|
71
|
+
private browser?;
|
|
72
|
+
private context?;
|
|
73
|
+
private page?;
|
|
74
|
+
private artifacts?;
|
|
75
|
+
private assertions?;
|
|
76
|
+
private testResults;
|
|
77
|
+
private failureCount;
|
|
78
|
+
constructor(config: PlaywrightNativeConfig);
|
|
79
|
+
/**
|
|
80
|
+
* Run a single E2E test with full Playwright native access
|
|
81
|
+
*/
|
|
82
|
+
runTest(test: UiTestDefinition): Promise<PlaywrightNativeResult>;
|
|
83
|
+
/**
|
|
84
|
+
* Execute a single step using Playwright native API
|
|
85
|
+
*/
|
|
86
|
+
private executeStep;
|
|
87
|
+
/**
|
|
88
|
+
* Execute action using Playwright native API
|
|
89
|
+
*/
|
|
90
|
+
private executeAction;
|
|
91
|
+
/**
|
|
92
|
+
* Setup browser with trace context
|
|
93
|
+
*/
|
|
94
|
+
private setup;
|
|
95
|
+
/**
|
|
96
|
+
* Teardown and save artifacts
|
|
97
|
+
*/
|
|
98
|
+
private teardown;
|
|
99
|
+
/**
|
|
100
|
+
* Get artifact paths for result
|
|
101
|
+
*/
|
|
102
|
+
private getArtifactPaths;
|
|
103
|
+
/**
|
|
104
|
+
* Get direct access to Playwright objects
|
|
105
|
+
*/
|
|
106
|
+
getBrowser(): Browser | undefined;
|
|
107
|
+
getContext(): BrowserContext | undefined;
|
|
108
|
+
getPage(): Page | undefined;
|
|
109
|
+
/**
|
|
110
|
+
* Get locator for direct manipulation
|
|
111
|
+
*/
|
|
112
|
+
locator(selector: string): Locator;
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Create a Playwright Native Adapter
|
|
116
|
+
*/
|
|
117
|
+
export declare function createPlaywrightNativeAdapter(config: PlaywrightNativeConfig): PlaywrightNativeAdapter;
|
|
118
|
+
/**
|
|
119
|
+
* Create adapter from WebTarget (convenience function)
|
|
120
|
+
*/
|
|
121
|
+
export declare function createFromTarget(target: WebTarget, options?: Partial<PlaywrightNativeConfig>): PlaywrightNativeAdapter;
|
|
@@ -0,0 +1,339 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* QA360 Playwright Native Adapter
|
|
3
|
+
*
|
|
4
|
+
* Zero-overhead adapter that uses Playwright's native API directly.
|
|
5
|
+
* This bypasses any wrapper logic and gives full access to Playwright's capabilities.
|
|
6
|
+
*
|
|
7
|
+
* Benefits:
|
|
8
|
+
* - Zero performance overhead
|
|
9
|
+
* - Full Playwright API access
|
|
10
|
+
* - Native video/trace/screenshot support
|
|
11
|
+
* - Direct access to browser context, page, locators
|
|
12
|
+
*/
|
|
13
|
+
import { chromium, firefox, webkit } from '@playwright/test';
|
|
14
|
+
import { UIArtifactsManager } from '../artifacts/ui-artifacts.js';
|
|
15
|
+
import { createAssertionsEngine } from '../assertions/index.js';
|
|
16
|
+
/**
|
|
17
|
+
* Playwright Native Adapter
|
|
18
|
+
*
|
|
19
|
+
* Provides zero-overhead access to Playwright with automatic
|
|
20
|
+
* artifact collection (screenshots, videos, traces).
|
|
21
|
+
*/
|
|
22
|
+
export class PlaywrightNativeAdapter {
|
|
23
|
+
config;
|
|
24
|
+
browser;
|
|
25
|
+
context;
|
|
26
|
+
page;
|
|
27
|
+
artifacts;
|
|
28
|
+
assertions;
|
|
29
|
+
// Test state tracking
|
|
30
|
+
testResults = new Map();
|
|
31
|
+
failureCount = 0;
|
|
32
|
+
constructor(config) {
|
|
33
|
+
this.config = config;
|
|
34
|
+
// Initialize artifacts manager
|
|
35
|
+
this.artifacts = new UIArtifactsManager('.qa360/artifacts/playwright-native', '.qa360/runs/cas');
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Run a single E2E test with full Playwright native access
|
|
39
|
+
*/
|
|
40
|
+
async runTest(test) {
|
|
41
|
+
const startTime = Date.now();
|
|
42
|
+
const testId = test.name || 'unknown';
|
|
43
|
+
this.artifacts?.startTest(testId);
|
|
44
|
+
this.testResults.set(testId, []);
|
|
45
|
+
try {
|
|
46
|
+
// Setup browser with trace context if enabled
|
|
47
|
+
await this.setup();
|
|
48
|
+
// Navigate to start URL
|
|
49
|
+
const startUrl = test.url || `${this.config.target.baseUrl.replace(/\/$/, '')}${test.path || ''}`;
|
|
50
|
+
await this.page.goto(startUrl, { timeout: this.config.timeout || 30000 });
|
|
51
|
+
// Initialize assertions engine
|
|
52
|
+
this.assertions = createAssertionsEngine(this.page);
|
|
53
|
+
// Take initial screenshot
|
|
54
|
+
if (this.config.screenshots !== 'never') {
|
|
55
|
+
await this.artifacts?.takeScreenshot(this.page, {}, {
|
|
56
|
+
testId,
|
|
57
|
+
type: 'screenshot',
|
|
58
|
+
tags: ['initial'],
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
// Execute each step
|
|
62
|
+
for (let i = 0; i < test.steps.length; i++) {
|
|
63
|
+
const step = test.steps[i];
|
|
64
|
+
const stepResult = await this.executeStep(step, i);
|
|
65
|
+
this.testResults.get(testId).push(stepResult);
|
|
66
|
+
if (!stepResult.success) {
|
|
67
|
+
this.failureCount++;
|
|
68
|
+
// Check if we should bail
|
|
69
|
+
if (this.config.bail && this.failureCount >= this.config.bail) {
|
|
70
|
+
throw new Error(`Bail: ${this.failureCount} failures`);
|
|
71
|
+
}
|
|
72
|
+
// Check if we should continue on failure
|
|
73
|
+
const continueOnFailure = step.continueOnError ?? false;
|
|
74
|
+
if (!continueOnFailure) {
|
|
75
|
+
break;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
// Screenshot after each step if configured
|
|
79
|
+
if (this.config.screenshots === 'always' || (this.config.screenshots === 'only-on-failure' && !stepResult.success)) {
|
|
80
|
+
await this.artifacts?.takeAfterScreenshot(this.page, step.action || 'step', i, stepResult.success);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
// All steps passed
|
|
84
|
+
const duration = Date.now() - startTime;
|
|
85
|
+
return {
|
|
86
|
+
success: true,
|
|
87
|
+
duration,
|
|
88
|
+
artifacts: this.getArtifactPaths(),
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
catch (error) {
|
|
92
|
+
// Take error screenshot
|
|
93
|
+
if (this.page) {
|
|
94
|
+
await this.artifacts?.takeErrorScreenshot(this.page, error);
|
|
95
|
+
}
|
|
96
|
+
return {
|
|
97
|
+
success: false,
|
|
98
|
+
duration: Date.now() - startTime,
|
|
99
|
+
error: error instanceof Error ? error.message : String(error),
|
|
100
|
+
artifacts: this.getArtifactPaths(),
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
finally {
|
|
104
|
+
this.artifacts?.endTest();
|
|
105
|
+
await this.teardown();
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Execute a single step using Playwright native API
|
|
110
|
+
*/
|
|
111
|
+
async executeStep(step, index) {
|
|
112
|
+
const startTime = Date.now();
|
|
113
|
+
try {
|
|
114
|
+
// Take before screenshot if enabled
|
|
115
|
+
if (this.config.screenshots === 'always') {
|
|
116
|
+
await this.artifacts?.takeBeforeScreenshot(this.page, step.action || 'step', index);
|
|
117
|
+
}
|
|
118
|
+
// Execute action based on type
|
|
119
|
+
const result = await this.executeAction(step);
|
|
120
|
+
// Execute assertions if present
|
|
121
|
+
if (step.assertions && step.assertions.length > 0 && this.assertions) {
|
|
122
|
+
for (const assertion of step.assertions) {
|
|
123
|
+
const assertionResult = await this.assertions.runAssertion(assertion);
|
|
124
|
+
if (!assertionResult.passed && !assertion.soft) {
|
|
125
|
+
throw new Error(assertionResult.error || `Assertion failed: ${assertion.type}`);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
return {
|
|
130
|
+
step,
|
|
131
|
+
success: true,
|
|
132
|
+
duration: Date.now() - startTime,
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
catch (error) {
|
|
136
|
+
return {
|
|
137
|
+
step,
|
|
138
|
+
success: false,
|
|
139
|
+
duration: Date.now() - startTime,
|
|
140
|
+
error: error instanceof Error ? error.message : String(error),
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Execute action using Playwright native API
|
|
146
|
+
*/
|
|
147
|
+
async executeAction(step) {
|
|
148
|
+
const selector = step.selector;
|
|
149
|
+
const page = this.page;
|
|
150
|
+
const timeout = step.timeout || this.config.timeout || 30000;
|
|
151
|
+
// Ensure selector is defined for actions that need it
|
|
152
|
+
const needsSelector = ['click', 'dblClick', 'rightClick', 'hover', 'focus', 'fill', 'type',
|
|
153
|
+
'clear', 'select', 'check', 'uncheck', 'press', 'upload', 'tap', 'scroll'].includes(step.action);
|
|
154
|
+
if (needsSelector && !selector) {
|
|
155
|
+
throw new Error(`Selector is required for action: ${step.action}`);
|
|
156
|
+
}
|
|
157
|
+
switch (step.action) {
|
|
158
|
+
case 'navigate':
|
|
159
|
+
await page.goto(step.value || '', { timeout });
|
|
160
|
+
break;
|
|
161
|
+
case 'click':
|
|
162
|
+
await page.click(selector, { timeout });
|
|
163
|
+
break;
|
|
164
|
+
case 'dblClick':
|
|
165
|
+
await page.dblclick(selector, { timeout });
|
|
166
|
+
break;
|
|
167
|
+
case 'rightClick':
|
|
168
|
+
await page.click(selector, { button: 'right', timeout });
|
|
169
|
+
break;
|
|
170
|
+
case 'fill':
|
|
171
|
+
case 'type':
|
|
172
|
+
if (step.value) {
|
|
173
|
+
await page.fill(selector, step.value, { timeout });
|
|
174
|
+
}
|
|
175
|
+
break;
|
|
176
|
+
case 'clear':
|
|
177
|
+
await page.fill(selector, '', { timeout });
|
|
178
|
+
break;
|
|
179
|
+
case 'select':
|
|
180
|
+
if (step.value !== undefined) {
|
|
181
|
+
await page.selectOption(selector, step.value, { timeout });
|
|
182
|
+
}
|
|
183
|
+
break;
|
|
184
|
+
case 'check':
|
|
185
|
+
await page.check(selector, { timeout });
|
|
186
|
+
break;
|
|
187
|
+
case 'uncheck':
|
|
188
|
+
await page.uncheck(selector, { timeout });
|
|
189
|
+
break;
|
|
190
|
+
case 'hover':
|
|
191
|
+
await page.hover(selector, { timeout });
|
|
192
|
+
break;
|
|
193
|
+
case 'focus':
|
|
194
|
+
await page.focus(selector, { timeout });
|
|
195
|
+
break;
|
|
196
|
+
case 'press':
|
|
197
|
+
if (step.value) {
|
|
198
|
+
await page.press(selector, step.value, { timeout });
|
|
199
|
+
}
|
|
200
|
+
break;
|
|
201
|
+
case 'upload':
|
|
202
|
+
if (step.value) {
|
|
203
|
+
await page.setInputFiles(selector, step.value, { timeout });
|
|
204
|
+
}
|
|
205
|
+
break;
|
|
206
|
+
case 'dragAndDrop':
|
|
207
|
+
if (step.value) {
|
|
208
|
+
const source = page.locator(selector);
|
|
209
|
+
const target = page.locator(step.value);
|
|
210
|
+
await source.dragTo(target, { timeout });
|
|
211
|
+
}
|
|
212
|
+
break;
|
|
213
|
+
case 'scroll':
|
|
214
|
+
if (selector) {
|
|
215
|
+
await page.locator(selector).scrollIntoViewIfNeeded({ timeout });
|
|
216
|
+
}
|
|
217
|
+
break;
|
|
218
|
+
case 'waitFor':
|
|
219
|
+
case 'waitForSelector':
|
|
220
|
+
if (selector) {
|
|
221
|
+
await page.waitForSelector(selector, {
|
|
222
|
+
state: step.value || undefined,
|
|
223
|
+
timeout
|
|
224
|
+
});
|
|
225
|
+
}
|
|
226
|
+
break;
|
|
227
|
+
case 'waitForTimeout':
|
|
228
|
+
await page.waitForTimeout(parseInt(step.value || '0', 10) || 0);
|
|
229
|
+
break;
|
|
230
|
+
case 'waitForNavigation':
|
|
231
|
+
await page.waitForNavigation({ timeout });
|
|
232
|
+
break;
|
|
233
|
+
case 'tap':
|
|
234
|
+
await page.tap(selector, { timeout });
|
|
235
|
+
break;
|
|
236
|
+
default:
|
|
237
|
+
throw new Error(`Unknown action: ${step.action}`);
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
/**
|
|
241
|
+
* Setup browser with trace context
|
|
242
|
+
*/
|
|
243
|
+
async setup() {
|
|
244
|
+
const browserType = this.config.browser || 'chromium';
|
|
245
|
+
const browserTypeObj = browserType === 'firefox' ? firefox : browserType === 'webkit' ? webkit : chromium;
|
|
246
|
+
// Launch browser
|
|
247
|
+
this.browser = await browserTypeObj.launch({
|
|
248
|
+
headless: this.config.headless ?? true,
|
|
249
|
+
slowMo: this.config.slowMo || 0,
|
|
250
|
+
devtools: this.config.devtools || false,
|
|
251
|
+
});
|
|
252
|
+
// Create context with trace recording if enabled
|
|
253
|
+
const contextOptions = {
|
|
254
|
+
viewport: this.config.viewport || { width: 1280, height: 720 },
|
|
255
|
+
userAgent: this.config.userAgent,
|
|
256
|
+
locale: this.config.locale || 'en-US',
|
|
257
|
+
timezoneId: this.config.timezone,
|
|
258
|
+
acceptDownloads: this.config.acceptDownloads ?? true,
|
|
259
|
+
ignoreHTTPSErrors: this.config.ignoreHTTPSErrors,
|
|
260
|
+
bypassCSP: this.config.bypassCSP,
|
|
261
|
+
};
|
|
262
|
+
// Add video recording
|
|
263
|
+
if (this.config.video === 'always' || this.config.video === 'retain-on-failure') {
|
|
264
|
+
contextOptions.recordVideo = {
|
|
265
|
+
dir: '.qa360/artifacts/playwright-native/videos',
|
|
266
|
+
size: this.config.viewport || { width: 1280, height: 720 },
|
|
267
|
+
};
|
|
268
|
+
}
|
|
269
|
+
this.context = await this.browser.newContext(contextOptions);
|
|
270
|
+
// Add tracing if enabled
|
|
271
|
+
if (this.config.trace === 'always' || this.config.trace === 'on-first-failure' || this.config.trace === 'retain-on-failure') {
|
|
272
|
+
// Start tracing (Playwright's trace feature)
|
|
273
|
+
// Note: tracing needs to be started per test
|
|
274
|
+
}
|
|
275
|
+
this.page = await this.context.newPage();
|
|
276
|
+
// Set default timeout
|
|
277
|
+
this.page.setDefaultTimeout(this.config.timeout || 30000);
|
|
278
|
+
}
|
|
279
|
+
/**
|
|
280
|
+
* Teardown and save artifacts
|
|
281
|
+
*/
|
|
282
|
+
async teardown() {
|
|
283
|
+
if (this.page) {
|
|
284
|
+
// Stop tracing if enabled
|
|
285
|
+
// Save trace file
|
|
286
|
+
await this.page.close();
|
|
287
|
+
}
|
|
288
|
+
if (this.context) {
|
|
289
|
+
// Save video if enabled and tests failed
|
|
290
|
+
if (this.config.video === 'retain-on-failure' && this.failureCount > 0) {
|
|
291
|
+
// Videos are automatically saved by Playwright
|
|
292
|
+
}
|
|
293
|
+
await this.context.close();
|
|
294
|
+
}
|
|
295
|
+
if (this.browser) {
|
|
296
|
+
await this.browser.close();
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
/**
|
|
300
|
+
* Get artifact paths for result
|
|
301
|
+
*/
|
|
302
|
+
getArtifactPaths() {
|
|
303
|
+
return {
|
|
304
|
+
screenshots: [],
|
|
305
|
+
videos: [],
|
|
306
|
+
traces: [],
|
|
307
|
+
};
|
|
308
|
+
}
|
|
309
|
+
/**
|
|
310
|
+
* Get direct access to Playwright objects
|
|
311
|
+
*/
|
|
312
|
+
getBrowser() { return this.browser; }
|
|
313
|
+
getContext() { return this.context; }
|
|
314
|
+
getPage() { return this.page; }
|
|
315
|
+
/**
|
|
316
|
+
* Get locator for direct manipulation
|
|
317
|
+
*/
|
|
318
|
+
locator(selector) {
|
|
319
|
+
if (!this.page) {
|
|
320
|
+
throw new Error('Page not initialized. Call setup() first.');
|
|
321
|
+
}
|
|
322
|
+
return this.page.locator(selector);
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
/**
|
|
326
|
+
* Create a Playwright Native Adapter
|
|
327
|
+
*/
|
|
328
|
+
export function createPlaywrightNativeAdapter(config) {
|
|
329
|
+
return new PlaywrightNativeAdapter(config);
|
|
330
|
+
}
|
|
331
|
+
/**
|
|
332
|
+
* Create adapter from WebTarget (convenience function)
|
|
333
|
+
*/
|
|
334
|
+
export function createFromTarget(target, options = {}) {
|
|
335
|
+
return new PlaywrightNativeAdapter({
|
|
336
|
+
target,
|
|
337
|
+
...options,
|
|
338
|
+
});
|
|
339
|
+
}
|
|
@@ -1,6 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* QA360 Playwright UI Adapter (Extended)
|
|
3
3
|
* Complete UI E2E testing with all Playwright actions
|
|
4
|
+
*
|
|
5
|
+
* Playwright++ Features:
|
|
6
|
+
* - Video recording (always/retain-on-fail/never)
|
|
7
|
+
* - Automatic screenshots (before/after steps, on error)
|
|
8
|
+
* - Trace capture for debugging
|
|
9
|
+
* - Artifacts management with CAS
|
|
10
|
+
* - HTML report generation
|
|
4
11
|
*/
|
|
5
12
|
import type { WebTarget, PackBudgets, UiTestDefinition, UiTestStep } from '../types/pack-v1.js';
|
|
6
13
|
import { AuthCredentials } from '../auth/index.js';
|
|
@@ -19,6 +26,17 @@ export interface UiTestConfig {
|
|
|
19
26
|
};
|
|
20
27
|
/** CLI override for headed mode */
|
|
21
28
|
cliHeaded?: boolean;
|
|
29
|
+
/** Playwright++: Artifacts configuration */
|
|
30
|
+
artifacts?: {
|
|
31
|
+
screenshots?: 'always' | 'only-on-failure' | 'never';
|
|
32
|
+
video?: 'always' | 'retain-on-failure' | 'never';
|
|
33
|
+
trace?: 'always' | 'retain-on-failure' | 'never' | 'on-first-failure';
|
|
34
|
+
outputDir?: string;
|
|
35
|
+
};
|
|
36
|
+
/** Playwright++: HTML report generation */
|
|
37
|
+
htmlReport?: string;
|
|
38
|
+
/** Playwright++: Bail after N failures */
|
|
39
|
+
bail?: number;
|
|
22
40
|
}
|
|
23
41
|
export interface UiTestResult {
|
|
24
42
|
page: string;
|
|
@@ -27,6 +45,12 @@ export interface UiTestResult {
|
|
|
27
45
|
error?: string;
|
|
28
46
|
screenshot?: string;
|
|
29
47
|
video?: string;
|
|
48
|
+
/** Playwright++: Artifacts paths */
|
|
49
|
+
artifacts?: {
|
|
50
|
+
screenshots: string[];
|
|
51
|
+
videos: string[];
|
|
52
|
+
traces: string[];
|
|
53
|
+
};
|
|
30
54
|
accessibility?: {
|
|
31
55
|
score: number;
|
|
32
56
|
violations: Array<{
|
|
@@ -84,6 +108,12 @@ export declare class PlaywrightUiAdapter {
|
|
|
84
108
|
private artifactDir;
|
|
85
109
|
private videoDir;
|
|
86
110
|
private traceDir;
|
|
111
|
+
private artifactsManager?;
|
|
112
|
+
private failureCount;
|
|
113
|
+
private currentTestId?;
|
|
114
|
+
private allScreenshots;
|
|
115
|
+
private allVideos;
|
|
116
|
+
private allTraces;
|
|
87
117
|
constructor();
|
|
88
118
|
/**
|
|
89
119
|
* Set authentication credentials for requests
|
|
@@ -91,14 +121,17 @@ export declare class PlaywrightUiAdapter {
|
|
|
91
121
|
setAuth(credentials?: AuthCredentials): void;
|
|
92
122
|
/**
|
|
93
123
|
* Execute UI smoke tests with accessibility
|
|
124
|
+
* Playwright++: Supports artifacts, screenshots, video, trace, HTML reporting
|
|
94
125
|
*/
|
|
95
126
|
runSmokeTests(config: UiTestConfig): Promise<UiSmokeResult>;
|
|
96
127
|
/**
|
|
97
128
|
* Run a single E2E test
|
|
129
|
+
* Playwright++: Takes before/after screenshots, captures artifacts on failure
|
|
98
130
|
*/
|
|
99
131
|
runE2eTest(test: UiTestDefinition, config: UiTestConfig): Promise<UiE2eResult>;
|
|
100
132
|
/**
|
|
101
133
|
* Execute a single UI test step
|
|
134
|
+
* Playwright++: Enhanced error handling with artifacts
|
|
102
135
|
*/
|
|
103
136
|
private executeStep;
|
|
104
137
|
/**
|
|
@@ -111,12 +144,17 @@ export declare class PlaywrightUiAdapter {
|
|
|
111
144
|
private testPage;
|
|
112
145
|
/**
|
|
113
146
|
* Setup browser with all options
|
|
147
|
+
* Playwright++: Enhanced video/trace recording support
|
|
114
148
|
*/
|
|
115
149
|
private setupBrowser;
|
|
116
150
|
/**
|
|
117
151
|
* Determine if video should be recorded
|
|
118
152
|
*/
|
|
119
153
|
private shouldRecordVideo;
|
|
154
|
+
/**
|
|
155
|
+
* Playwright++: Generate HTML report
|
|
156
|
+
*/
|
|
157
|
+
private generateHtmlReport;
|
|
120
158
|
/**
|
|
121
159
|
* Perform login if configured
|
|
122
160
|
*/
|