cbrowser 3.0.1 → 4.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/dist/browser.d.ts +82 -0
- package/dist/browser.d.ts.map +1 -1
- package/dist/browser.js +353 -9
- package/dist/browser.js.map +1 -1
- package/dist/cli.js +206 -2
- package/dist/cli.js.map +1 -1
- package/dist/config.d.ts +2 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +3 -3
- package/dist/config.js.map +1 -1
- package/package.json +1 -1
package/dist/browser.d.ts
CHANGED
|
@@ -25,6 +25,10 @@ export declare class CBrowser {
|
|
|
25
25
|
* Close the browser.
|
|
26
26
|
*/
|
|
27
27
|
close(): Promise<void>;
|
|
28
|
+
/**
|
|
29
|
+
* Reset persistent browser state (clear cookies, localStorage, etc.)
|
|
30
|
+
*/
|
|
31
|
+
reset(): Promise<void>;
|
|
28
32
|
/**
|
|
29
33
|
* Get the current page, launching if needed.
|
|
30
34
|
*/
|
|
@@ -372,4 +376,82 @@ export declare function executeNaturalLanguageScript(browser: CBrowser, commands
|
|
|
372
376
|
result?: unknown;
|
|
373
377
|
error?: string;
|
|
374
378
|
}>>;
|
|
379
|
+
/**
|
|
380
|
+
* AI-powered semantic element finding.
|
|
381
|
+
* Examples: "the cheapest product", "login form", "main navigation"
|
|
382
|
+
*/
|
|
383
|
+
export declare function findElementByIntent(browser: CBrowser, intent: string): Promise<{
|
|
384
|
+
selector: string;
|
|
385
|
+
confidence: number;
|
|
386
|
+
description: string;
|
|
387
|
+
} | null>;
|
|
388
|
+
export interface BugReport {
|
|
389
|
+
type: "broken-link" | "console-error" | "a11y-violation" | "slow-resource" | "missing-image" | "form-error";
|
|
390
|
+
severity: "critical" | "high" | "medium" | "low";
|
|
391
|
+
description: string;
|
|
392
|
+
url: string;
|
|
393
|
+
selector?: string;
|
|
394
|
+
screenshot?: string;
|
|
395
|
+
}
|
|
396
|
+
/**
|
|
397
|
+
* Autonomously explore a page and find bugs.
|
|
398
|
+
*/
|
|
399
|
+
export declare function huntBugs(browser: CBrowser, url: string, options?: {
|
|
400
|
+
maxDepth?: number;
|
|
401
|
+
maxPages?: number;
|
|
402
|
+
timeout?: number;
|
|
403
|
+
}): Promise<{
|
|
404
|
+
bugs: BugReport[];
|
|
405
|
+
pagesVisited: number;
|
|
406
|
+
duration: number;
|
|
407
|
+
}>;
|
|
408
|
+
export interface BrowserDiffResult {
|
|
409
|
+
url: string;
|
|
410
|
+
browsers: string[];
|
|
411
|
+
differences: Array<{
|
|
412
|
+
type: "visual" | "timing" | "content" | "error";
|
|
413
|
+
description: string;
|
|
414
|
+
browsers: string[];
|
|
415
|
+
}>;
|
|
416
|
+
screenshots: Record<string, string>;
|
|
417
|
+
metrics: Record<string, {
|
|
418
|
+
loadTime: number;
|
|
419
|
+
resourceCount: number;
|
|
420
|
+
}>;
|
|
421
|
+
}
|
|
422
|
+
/**
|
|
423
|
+
* Compare page behavior across multiple browsers.
|
|
424
|
+
*/
|
|
425
|
+
export declare function crossBrowserDiff(url: string, browsers?: Array<"chromium" | "firefox" | "webkit">): Promise<BrowserDiffResult>;
|
|
426
|
+
export interface ChaosConfig {
|
|
427
|
+
/** Simulate slow network (ms latency) */
|
|
428
|
+
networkLatency?: number;
|
|
429
|
+
/** Simulate offline mode */
|
|
430
|
+
offline?: boolean;
|
|
431
|
+
/** Block specific URL patterns */
|
|
432
|
+
blockUrls?: string[];
|
|
433
|
+
/** Inject random delays (0-1 probability) */
|
|
434
|
+
randomDelays?: number;
|
|
435
|
+
/** Fail specific API calls */
|
|
436
|
+
failApis?: Array<{
|
|
437
|
+
pattern: string;
|
|
438
|
+
status: number;
|
|
439
|
+
body?: string;
|
|
440
|
+
}>;
|
|
441
|
+
/** CPU throttling (1-20x slowdown) */
|
|
442
|
+
cpuThrottle?: number;
|
|
443
|
+
}
|
|
444
|
+
/**
|
|
445
|
+
* Apply chaos engineering conditions to browser.
|
|
446
|
+
*/
|
|
447
|
+
export declare function applyChaos(browser: CBrowser, config: ChaosConfig): Promise<void>;
|
|
448
|
+
/**
|
|
449
|
+
* Run chaos test - apply conditions and verify app resilience.
|
|
450
|
+
*/
|
|
451
|
+
export declare function runChaosTest(browser: CBrowser, url: string, chaos: ChaosConfig, actions?: string[]): Promise<{
|
|
452
|
+
passed: boolean;
|
|
453
|
+
errors: string[];
|
|
454
|
+
duration: number;
|
|
455
|
+
screenshot: string;
|
|
456
|
+
}>;
|
|
375
457
|
//# sourceMappingURL=browser.d.ts.map
|
package/dist/browser.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"browser.d.ts","sourceRoot":"","sources":["../src/browser.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAMH,OAAO,EAAE,KAAK,cAAc,EAAgE,MAAM,aAAa,CAAC;AAEhH,OAAO,KAAK,EACV,YAAY,EACZ,gBAAgB,EAChB,WAAW,EACX,aAAa,EACb,aAAa,EAKb,cAAc,EACd,aAAa,EACb,cAAc,EAEd,WAAW,EACX,cAAc,EAId,kBAAkB,EAClB,sBAAsB,EACvB,MAAM,YAAY,CAAC;AAGpB,qBAAa,QAAQ;IACnB,OAAO,CAAC,MAAM,CAAiB;IAC/B,OAAO,CAAC,KAAK,CAAgB;IAC7B,OAAO,CAAC,OAAO,CAAwB;IACvC,OAAO,CAAC,OAAO,CAA+B;IAC9C,OAAO,CAAC,IAAI,CAAqB;IACjC,OAAO,CAAC,cAAc,CAAwB;IAC9C,OAAO,CAAC,eAAe,CAAwB;IAC/C,OAAO,CAAC,gBAAgB,CAA2C;IACnE,OAAO,CAAC,UAAU,CAAkB;IACpC,OAAO,CAAC,cAAc,CAAS;gBAEnB,UAAU,GAAE,OAAO,CAAC,cAAc,CAAM;IASpD;;OAEG;IACG,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"browser.d.ts","sourceRoot":"","sources":["../src/browser.ts"],"names":[],"mappings":"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"}
|
package/dist/browser.js
CHANGED
|
@@ -9,6 +9,11 @@ exports.FluentCBrowser = exports.CBrowser = void 0;
|
|
|
9
9
|
exports.parseNaturalLanguage = parseNaturalLanguage;
|
|
10
10
|
exports.executeNaturalLanguage = executeNaturalLanguage;
|
|
11
11
|
exports.executeNaturalLanguageScript = executeNaturalLanguageScript;
|
|
12
|
+
exports.findElementByIntent = findElementByIntent;
|
|
13
|
+
exports.huntBugs = huntBugs;
|
|
14
|
+
exports.crossBrowserDiff = crossBrowserDiff;
|
|
15
|
+
exports.applyChaos = applyChaos;
|
|
16
|
+
exports.runChaosTest = runChaosTest;
|
|
12
17
|
const playwright_1 = require("playwright");
|
|
13
18
|
const fs_1 = require("fs");
|
|
14
19
|
const path_1 = require("path");
|
|
@@ -37,7 +42,7 @@ class CBrowser {
|
|
|
37
42
|
* Launch the browser.
|
|
38
43
|
*/
|
|
39
44
|
async launch() {
|
|
40
|
-
if (this.browser)
|
|
45
|
+
if (this.browser || this.context)
|
|
41
46
|
return;
|
|
42
47
|
// Select browser engine based on config
|
|
43
48
|
const browserType = this.config.browser === "firefox"
|
|
@@ -45,9 +50,6 @@ class CBrowser {
|
|
|
45
50
|
: this.config.browser === "webkit"
|
|
46
51
|
? playwright_1.webkit
|
|
47
52
|
: playwright_1.chromium;
|
|
48
|
-
this.browser = await browserType.launch({
|
|
49
|
-
headless: this.config.headless,
|
|
50
|
-
});
|
|
51
53
|
// Build context options
|
|
52
54
|
const contextOptions = {
|
|
53
55
|
viewport: {
|
|
@@ -111,8 +113,28 @@ class CBrowser {
|
|
|
111
113
|
},
|
|
112
114
|
};
|
|
113
115
|
}
|
|
114
|
-
|
|
115
|
-
|
|
116
|
+
// Use persistent context if enabled (preserves cookies/localStorage between sessions)
|
|
117
|
+
if (this.config.persistent) {
|
|
118
|
+
const browserStateDir = (0, path_1.join)(this.paths.dataDir, "browser-state");
|
|
119
|
+
if (!(0, fs_1.existsSync)(browserStateDir)) {
|
|
120
|
+
(0, fs_1.mkdirSync)(browserStateDir, { recursive: true });
|
|
121
|
+
}
|
|
122
|
+
this.context = await browserType.launchPersistentContext(browserStateDir, {
|
|
123
|
+
headless: this.config.headless,
|
|
124
|
+
...contextOptions,
|
|
125
|
+
});
|
|
126
|
+
this.page = this.context.pages()[0] || await this.context.newPage();
|
|
127
|
+
if (this.config.verbose) {
|
|
128
|
+
console.log(`🔄 Using persistent browser context: ${browserStateDir}`);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
else {
|
|
132
|
+
this.browser = await browserType.launch({
|
|
133
|
+
headless: this.config.headless,
|
|
134
|
+
});
|
|
135
|
+
this.context = await this.browser.newContext(contextOptions);
|
|
136
|
+
this.page = await this.context.newPage();
|
|
137
|
+
}
|
|
116
138
|
// Apply network mocks if configured
|
|
117
139
|
if (this.config.networkMocks && this.config.networkMocks.length > 0) {
|
|
118
140
|
await this.setupNetworkMocks(this.config.networkMocks);
|
|
@@ -124,12 +146,30 @@ class CBrowser {
|
|
|
124
146
|
* Close the browser.
|
|
125
147
|
*/
|
|
126
148
|
async close() {
|
|
127
|
-
if (this.
|
|
128
|
-
await this.
|
|
129
|
-
this.browser = null;
|
|
149
|
+
if (this.context) {
|
|
150
|
+
await this.context.close().catch(() => { });
|
|
130
151
|
this.context = null;
|
|
131
152
|
this.page = null;
|
|
132
153
|
}
|
|
154
|
+
if (this.browser) {
|
|
155
|
+
await this.browser.close().catch(() => { });
|
|
156
|
+
this.browser = null;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Reset persistent browser state (clear cookies, localStorage, etc.)
|
|
161
|
+
*/
|
|
162
|
+
async reset() {
|
|
163
|
+
await this.close();
|
|
164
|
+
const browserStateDir = (0, path_1.join)(this.paths.dataDir, "browser-state");
|
|
165
|
+
if ((0, fs_1.existsSync)(browserStateDir)) {
|
|
166
|
+
const { rmSync } = await import("fs");
|
|
167
|
+
rmSync(browserStateDir, { recursive: true, force: true });
|
|
168
|
+
(0, fs_1.mkdirSync)(browserStateDir, { recursive: true });
|
|
169
|
+
}
|
|
170
|
+
if (this.config.verbose) {
|
|
171
|
+
console.log("🔄 Browser state reset");
|
|
172
|
+
}
|
|
133
173
|
}
|
|
134
174
|
/**
|
|
135
175
|
* Get the current page, launching if needed.
|
|
@@ -1562,4 +1602,308 @@ async function executeNaturalLanguageScript(browser, commands) {
|
|
|
1562
1602
|
}
|
|
1563
1603
|
return results;
|
|
1564
1604
|
}
|
|
1605
|
+
// ============================================================================
|
|
1606
|
+
// Tier 4: Visual AI Understanding (v4.0.0)
|
|
1607
|
+
// ============================================================================
|
|
1608
|
+
/**
|
|
1609
|
+
* AI-powered semantic element finding.
|
|
1610
|
+
* Examples: "the cheapest product", "login form", "main navigation"
|
|
1611
|
+
*/
|
|
1612
|
+
async function findElementByIntent(browser, intent) {
|
|
1613
|
+
const page = await browser.getPage();
|
|
1614
|
+
// Extract page structure for AI analysis
|
|
1615
|
+
const pageData = await page.evaluate(() => {
|
|
1616
|
+
const elements = [];
|
|
1617
|
+
// Find interactive elements
|
|
1618
|
+
const interactives = document.querySelectorAll("button, a, input, select, [role='button'], [onclick], .btn, .card, .product, [data-price], .price");
|
|
1619
|
+
interactives.forEach((el, i) => {
|
|
1620
|
+
const text = el.innerText?.trim().slice(0, 100) || "";
|
|
1621
|
+
const priceMatch = text.match(/\$[\d,.]+|\d+\.\d{2}/);
|
|
1622
|
+
elements.push({
|
|
1623
|
+
tag: el.tagName.toLowerCase(),
|
|
1624
|
+
text,
|
|
1625
|
+
classes: el.className.toString().slice(0, 100),
|
|
1626
|
+
id: el.id,
|
|
1627
|
+
role: el.getAttribute("role") || "",
|
|
1628
|
+
type: el.type || "",
|
|
1629
|
+
price: priceMatch ? priceMatch[0] : undefined,
|
|
1630
|
+
selector: el.id ? `#${el.id}` : `${el.tagName.toLowerCase()}:nth-of-type(${i + 1})`,
|
|
1631
|
+
});
|
|
1632
|
+
});
|
|
1633
|
+
return elements;
|
|
1634
|
+
});
|
|
1635
|
+
// Intent matching logic
|
|
1636
|
+
const intentLower = intent.toLowerCase();
|
|
1637
|
+
// Price-based intents
|
|
1638
|
+
if (intentLower.includes("cheapest") || intentLower.includes("lowest price")) {
|
|
1639
|
+
const withPrices = pageData.filter(el => el.price);
|
|
1640
|
+
if (withPrices.length > 0) {
|
|
1641
|
+
const sorted = withPrices.sort((a, b) => {
|
|
1642
|
+
const priceA = parseFloat(a.price.replace(/[$,]/g, ""));
|
|
1643
|
+
const priceB = parseFloat(b.price.replace(/[$,]/g, ""));
|
|
1644
|
+
return priceA - priceB;
|
|
1645
|
+
});
|
|
1646
|
+
return {
|
|
1647
|
+
selector: sorted[0].selector,
|
|
1648
|
+
confidence: 0.8,
|
|
1649
|
+
description: `Cheapest item: ${sorted[0].text.slice(0, 50)} (${sorted[0].price})`,
|
|
1650
|
+
};
|
|
1651
|
+
}
|
|
1652
|
+
}
|
|
1653
|
+
if (intentLower.includes("most expensive") || intentLower.includes("highest price")) {
|
|
1654
|
+
const withPrices = pageData.filter(el => el.price);
|
|
1655
|
+
if (withPrices.length > 0) {
|
|
1656
|
+
const sorted = withPrices.sort((a, b) => {
|
|
1657
|
+
const priceA = parseFloat(a.price.replace(/[$,]/g, ""));
|
|
1658
|
+
const priceB = parseFloat(b.price.replace(/[$,]/g, ""));
|
|
1659
|
+
return priceB - priceA;
|
|
1660
|
+
});
|
|
1661
|
+
return {
|
|
1662
|
+
selector: sorted[0].selector,
|
|
1663
|
+
confidence: 0.8,
|
|
1664
|
+
description: `Most expensive: ${sorted[0].text.slice(0, 50)} (${sorted[0].price})`,
|
|
1665
|
+
};
|
|
1666
|
+
}
|
|
1667
|
+
}
|
|
1668
|
+
// Form-based intents
|
|
1669
|
+
if (intentLower.includes("login") || intentLower.includes("sign in")) {
|
|
1670
|
+
const loginBtn = pageData.find(el => el.text.toLowerCase().includes("login") ||
|
|
1671
|
+
el.text.toLowerCase().includes("sign in") ||
|
|
1672
|
+
el.classes.includes("login"));
|
|
1673
|
+
if (loginBtn) {
|
|
1674
|
+
return { selector: loginBtn.selector, confidence: 0.9, description: "Login button/form" };
|
|
1675
|
+
}
|
|
1676
|
+
}
|
|
1677
|
+
if (intentLower.includes("search")) {
|
|
1678
|
+
const searchInput = pageData.find(el => el.type === "search" ||
|
|
1679
|
+
el.classes.includes("search") ||
|
|
1680
|
+
el.id.includes("search"));
|
|
1681
|
+
if (searchInput) {
|
|
1682
|
+
return { selector: searchInput.selector, confidence: 0.9, description: "Search input" };
|
|
1683
|
+
}
|
|
1684
|
+
}
|
|
1685
|
+
// Text-based matching
|
|
1686
|
+
const textMatch = pageData.find(el => el.text.toLowerCase().includes(intentLower) ||
|
|
1687
|
+
el.classes.toLowerCase().includes(intentLower));
|
|
1688
|
+
if (textMatch) {
|
|
1689
|
+
return { selector: textMatch.selector, confidence: 0.7, description: `Matched: ${textMatch.text.slice(0, 50)}` };
|
|
1690
|
+
}
|
|
1691
|
+
return null;
|
|
1692
|
+
}
|
|
1693
|
+
/**
|
|
1694
|
+
* Autonomously explore a page and find bugs.
|
|
1695
|
+
*/
|
|
1696
|
+
async function huntBugs(browser, url, options = {}) {
|
|
1697
|
+
const startTime = Date.now();
|
|
1698
|
+
const bugs = [];
|
|
1699
|
+
const visited = new Set();
|
|
1700
|
+
const maxPages = options.maxPages || 10;
|
|
1701
|
+
const timeout = options.timeout || 60000;
|
|
1702
|
+
const page = await browser.getPage();
|
|
1703
|
+
const consoleErrors = [];
|
|
1704
|
+
// Capture console errors
|
|
1705
|
+
page.on("console", (msg) => {
|
|
1706
|
+
if (msg.type() === "error") {
|
|
1707
|
+
consoleErrors.push(msg.text());
|
|
1708
|
+
}
|
|
1709
|
+
});
|
|
1710
|
+
// Start with initial URL
|
|
1711
|
+
await browser.navigate(url);
|
|
1712
|
+
visited.add(url);
|
|
1713
|
+
// Check for issues on current page
|
|
1714
|
+
const pageIssues = await page.evaluate(() => {
|
|
1715
|
+
const issues = [];
|
|
1716
|
+
// Check for broken images
|
|
1717
|
+
document.querySelectorAll("img").forEach((img, i) => {
|
|
1718
|
+
if (!img.complete || img.naturalWidth === 0) {
|
|
1719
|
+
issues.push({
|
|
1720
|
+
type: "missing-image",
|
|
1721
|
+
description: `Broken image: ${img.src || img.alt || "unknown"}`,
|
|
1722
|
+
selector: `img:nth-of-type(${i + 1})`,
|
|
1723
|
+
});
|
|
1724
|
+
}
|
|
1725
|
+
});
|
|
1726
|
+
// Check for empty links
|
|
1727
|
+
document.querySelectorAll("a").forEach((a, i) => {
|
|
1728
|
+
if (!a.href || a.href === "#" || a.href === "javascript:void(0)") {
|
|
1729
|
+
issues.push({
|
|
1730
|
+
type: "broken-link",
|
|
1731
|
+
description: `Empty/invalid link: ${a.textContent?.slice(0, 50) || "no text"}`,
|
|
1732
|
+
selector: `a:nth-of-type(${i + 1})`,
|
|
1733
|
+
});
|
|
1734
|
+
}
|
|
1735
|
+
});
|
|
1736
|
+
// Check for empty buttons
|
|
1737
|
+
document.querySelectorAll("button").forEach((btn, i) => {
|
|
1738
|
+
if (!btn.textContent?.trim() && !btn.getAttribute("aria-label")) {
|
|
1739
|
+
issues.push({
|
|
1740
|
+
type: "a11y-violation",
|
|
1741
|
+
description: "Button with no accessible text",
|
|
1742
|
+
selector: `button:nth-of-type(${i + 1})`,
|
|
1743
|
+
});
|
|
1744
|
+
}
|
|
1745
|
+
});
|
|
1746
|
+
// Check for missing form labels
|
|
1747
|
+
document.querySelectorAll("input:not([type='hidden'])").forEach((input, i) => {
|
|
1748
|
+
const id = input.id;
|
|
1749
|
+
const hasLabel = id && document.querySelector(`label[for="${id}"]`);
|
|
1750
|
+
if (!hasLabel && !input.getAttribute("aria-label") && !input.getAttribute("placeholder")) {
|
|
1751
|
+
issues.push({
|
|
1752
|
+
type: "form-error",
|
|
1753
|
+
description: "Input without label or placeholder",
|
|
1754
|
+
selector: `input:nth-of-type(${i + 1})`,
|
|
1755
|
+
});
|
|
1756
|
+
}
|
|
1757
|
+
});
|
|
1758
|
+
return issues;
|
|
1759
|
+
});
|
|
1760
|
+
// Add page issues to bugs
|
|
1761
|
+
for (const issue of pageIssues) {
|
|
1762
|
+
bugs.push({
|
|
1763
|
+
type: issue.type,
|
|
1764
|
+
severity: issue.type === "a11y-violation" ? "high" : "medium",
|
|
1765
|
+
description: issue.description,
|
|
1766
|
+
url,
|
|
1767
|
+
selector: issue.selector,
|
|
1768
|
+
});
|
|
1769
|
+
}
|
|
1770
|
+
// Add console errors
|
|
1771
|
+
for (const error of consoleErrors) {
|
|
1772
|
+
bugs.push({
|
|
1773
|
+
type: "console-error",
|
|
1774
|
+
severity: "high",
|
|
1775
|
+
description: error.slice(0, 200),
|
|
1776
|
+
url,
|
|
1777
|
+
});
|
|
1778
|
+
}
|
|
1779
|
+
return {
|
|
1780
|
+
bugs,
|
|
1781
|
+
pagesVisited: visited.size,
|
|
1782
|
+
duration: Date.now() - startTime,
|
|
1783
|
+
};
|
|
1784
|
+
}
|
|
1785
|
+
/**
|
|
1786
|
+
* Compare page behavior across multiple browsers.
|
|
1787
|
+
*/
|
|
1788
|
+
async function crossBrowserDiff(url, browsers = ["chromium", "firefox", "webkit"]) {
|
|
1789
|
+
const { chromium, firefox, webkit } = await import("playwright");
|
|
1790
|
+
const screenshots = {};
|
|
1791
|
+
const metrics = {};
|
|
1792
|
+
const differences = [];
|
|
1793
|
+
const contents = {};
|
|
1794
|
+
const browserLaunchers = { chromium, firefox, webkit };
|
|
1795
|
+
for (const browserName of browsers) {
|
|
1796
|
+
const launcher = browserLaunchers[browserName];
|
|
1797
|
+
const browser = await launcher.launch({ headless: true });
|
|
1798
|
+
const page = await browser.newPage();
|
|
1799
|
+
const startTime = Date.now();
|
|
1800
|
+
await page.goto(url, { waitUntil: "domcontentloaded" });
|
|
1801
|
+
const loadTime = Date.now() - startTime;
|
|
1802
|
+
// Capture metrics
|
|
1803
|
+
const resourceCount = await page.evaluate(() => performance.getEntriesByType("resource").length);
|
|
1804
|
+
metrics[browserName] = { loadTime, resourceCount };
|
|
1805
|
+
// Capture screenshot
|
|
1806
|
+
const screenshotPath = `/tmp/cross-browser-${browserName}-${Date.now()}.png`;
|
|
1807
|
+
await page.screenshot({ path: screenshotPath, fullPage: true });
|
|
1808
|
+
screenshots[browserName] = screenshotPath;
|
|
1809
|
+
// Capture content hash
|
|
1810
|
+
contents[browserName] = await page.evaluate(() => document.body.innerText.slice(0, 1000));
|
|
1811
|
+
await browser.close();
|
|
1812
|
+
}
|
|
1813
|
+
// Compare timing
|
|
1814
|
+
const loadTimes = Object.entries(metrics).map(([b, m]) => ({ browser: b, time: m.loadTime }));
|
|
1815
|
+
const avgTime = loadTimes.reduce((sum, t) => sum + t.time, 0) / loadTimes.length;
|
|
1816
|
+
const slowBrowsers = loadTimes.filter(t => t.time > avgTime * 1.5);
|
|
1817
|
+
if (slowBrowsers.length > 0) {
|
|
1818
|
+
differences.push({
|
|
1819
|
+
type: "timing",
|
|
1820
|
+
description: `Significantly slower in: ${slowBrowsers.map(b => `${b.browser} (${b.time}ms)`).join(", ")}`,
|
|
1821
|
+
browsers: slowBrowsers.map(b => b.browser),
|
|
1822
|
+
});
|
|
1823
|
+
}
|
|
1824
|
+
// Compare content
|
|
1825
|
+
const contentValues = Object.values(contents);
|
|
1826
|
+
const contentMismatch = contentValues.some(c => c !== contentValues[0]);
|
|
1827
|
+
if (contentMismatch) {
|
|
1828
|
+
differences.push({
|
|
1829
|
+
type: "content",
|
|
1830
|
+
description: "Page content differs between browsers",
|
|
1831
|
+
browsers,
|
|
1832
|
+
});
|
|
1833
|
+
}
|
|
1834
|
+
return { url, browsers, differences, screenshots, metrics };
|
|
1835
|
+
}
|
|
1836
|
+
/**
|
|
1837
|
+
* Apply chaos engineering conditions to browser.
|
|
1838
|
+
*/
|
|
1839
|
+
async function applyChaos(browser, config) {
|
|
1840
|
+
const context = await browser.context;
|
|
1841
|
+
const page = await browser.getPage();
|
|
1842
|
+
// Network conditions
|
|
1843
|
+
if (config.offline) {
|
|
1844
|
+
await context.setOffline(true);
|
|
1845
|
+
}
|
|
1846
|
+
// Route interception for latency/failures
|
|
1847
|
+
if (config.networkLatency || config.blockUrls || config.failApis) {
|
|
1848
|
+
await page.route("**/*", async (route) => {
|
|
1849
|
+
const url = route.request().url();
|
|
1850
|
+
// Block URLs
|
|
1851
|
+
if (config.blockUrls?.some(pattern => url.includes(pattern))) {
|
|
1852
|
+
await route.abort();
|
|
1853
|
+
return;
|
|
1854
|
+
}
|
|
1855
|
+
// Fail specific APIs
|
|
1856
|
+
const failConfig = config.failApis?.find(f => url.includes(f.pattern));
|
|
1857
|
+
if (failConfig) {
|
|
1858
|
+
await route.fulfill({
|
|
1859
|
+
status: failConfig.status,
|
|
1860
|
+
body: failConfig.body || "Chaos: Simulated failure",
|
|
1861
|
+
});
|
|
1862
|
+
return;
|
|
1863
|
+
}
|
|
1864
|
+
// Add latency
|
|
1865
|
+
if (config.networkLatency) {
|
|
1866
|
+
await new Promise(r => setTimeout(r, config.networkLatency));
|
|
1867
|
+
}
|
|
1868
|
+
// Random delays
|
|
1869
|
+
if (config.randomDelays && Math.random() < config.randomDelays) {
|
|
1870
|
+
await new Promise(r => setTimeout(r, Math.random() * 3000));
|
|
1871
|
+
}
|
|
1872
|
+
await route.continue();
|
|
1873
|
+
});
|
|
1874
|
+
}
|
|
1875
|
+
}
|
|
1876
|
+
/**
|
|
1877
|
+
* Run chaos test - apply conditions and verify app resilience.
|
|
1878
|
+
*/
|
|
1879
|
+
async function runChaosTest(browser, url, chaos, actions = []) {
|
|
1880
|
+
const startTime = Date.now();
|
|
1881
|
+
const errors = [];
|
|
1882
|
+
try {
|
|
1883
|
+
await applyChaos(browser, chaos);
|
|
1884
|
+
await browser.navigate(url);
|
|
1885
|
+
// Execute actions
|
|
1886
|
+
for (const action of actions) {
|
|
1887
|
+
const result = await executeNaturalLanguage(browser, action);
|
|
1888
|
+
if (!result.success) {
|
|
1889
|
+
errors.push(`Action failed: ${action} - ${result.error}`);
|
|
1890
|
+
}
|
|
1891
|
+
}
|
|
1892
|
+
const screenshot = await browser.screenshot();
|
|
1893
|
+
return {
|
|
1894
|
+
passed: errors.length === 0,
|
|
1895
|
+
errors,
|
|
1896
|
+
duration: Date.now() - startTime,
|
|
1897
|
+
screenshot,
|
|
1898
|
+
};
|
|
1899
|
+
}
|
|
1900
|
+
catch (e) {
|
|
1901
|
+
return {
|
|
1902
|
+
passed: false,
|
|
1903
|
+
errors: [...errors, e.message],
|
|
1904
|
+
duration: Date.now() - startTime,
|
|
1905
|
+
screenshot: "",
|
|
1906
|
+
};
|
|
1907
|
+
}
|
|
1908
|
+
}
|
|
1565
1909
|
//# sourceMappingURL=browser.js.map
|