donobu 2.18.4 → 2.19.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/apis/FlowsApi.d.ts +4 -0
- package/dist/apis/FlowsApi.d.ts.map +1 -1
- package/dist/apis/FlowsApi.js +15 -1
- package/dist/apis/FlowsApi.js.map +1 -1
- package/dist/apis/SpecialFlowsApi.d.ts.map +1 -1
- package/dist/apis/SpecialFlowsApi.js +11 -9
- package/dist/apis/SpecialFlowsApi.js.map +1 -1
- package/dist/assets/control-panel.js +7 -1
- package/dist/assets/generated/parameter-schemas.json +44 -0
- package/dist/assets/generated/version +1 -1
- package/dist/assets/page-interactions-tracker.js +27 -30
- package/dist/bindings/PageInteractionTracker.d.ts.map +1 -1
- package/dist/bindings/PageInteractionTracker.js +1 -1
- package/dist/bindings/PageInteractionTracker.js.map +1 -1
- package/dist/esm/apis/FlowsApi.d.ts +4 -0
- package/dist/esm/apis/FlowsApi.d.ts.map +1 -1
- package/dist/esm/apis/FlowsApi.js +15 -1
- package/dist/esm/apis/FlowsApi.js.map +1 -1
- package/dist/esm/apis/SpecialFlowsApi.d.ts.map +1 -1
- package/dist/esm/apis/SpecialFlowsApi.js +11 -9
- package/dist/esm/apis/SpecialFlowsApi.js.map +1 -1
- package/dist/esm/assets/control-panel.js +7 -1
- package/dist/esm/assets/generated/parameter-schemas.json +44 -0
- package/dist/esm/assets/generated/version +1 -1
- package/dist/esm/assets/page-interactions-tracker.js +27 -30
- package/dist/esm/bindings/PageInteractionTracker.d.ts.map +1 -1
- package/dist/esm/bindings/PageInteractionTracker.js +1 -1
- package/dist/esm/bindings/PageInteractionTracker.js.map +1 -1
- package/dist/esm/lib/testExtension.d.ts.map +1 -1
- package/dist/esm/lib/testExtension.js +20 -19
- package/dist/esm/lib/testExtension.js.map +1 -1
- package/dist/esm/managers/AdminApiController.d.ts.map +1 -1
- package/dist/esm/managers/AdminApiController.js +1 -0
- package/dist/esm/managers/AdminApiController.js.map +1 -1
- package/dist/esm/managers/CodeGenerator.d.ts +47 -1
- package/dist/esm/managers/CodeGenerator.d.ts.map +1 -1
- package/dist/esm/managers/CodeGenerator.js +211 -19
- package/dist/esm/managers/CodeGenerator.js.map +1 -1
- package/dist/esm/managers/ControlPanel.d.ts.map +1 -1
- package/dist/esm/managers/ControlPanel.js +7 -5
- package/dist/esm/managers/ControlPanel.js.map +1 -1
- package/dist/esm/managers/DonobuFlow.d.ts +3 -4
- package/dist/esm/managers/DonobuFlow.d.ts.map +1 -1
- package/dist/esm/managers/DonobuFlow.js +57 -44
- package/dist/esm/managers/DonobuFlow.js.map +1 -1
- package/dist/esm/managers/DonobuFlowsManager.d.ts +58 -34
- package/dist/esm/managers/DonobuFlowsManager.d.ts.map +1 -1
- package/dist/esm/managers/DonobuFlowsManager.js +217 -64
- package/dist/esm/managers/DonobuFlowsManager.js.map +1 -1
- package/dist/esm/managers/FlowDependencyAnalyzer.d.ts +40 -0
- package/dist/esm/managers/FlowDependencyAnalyzer.d.ts.map +1 -0
- package/dist/esm/managers/FlowDependencyAnalyzer.js +236 -0
- package/dist/esm/managers/FlowDependencyAnalyzer.js.map +1 -0
- package/dist/esm/managers/ProjectStructureGenerator.d.ts +62 -0
- package/dist/esm/managers/ProjectStructureGenerator.d.ts.map +1 -0
- package/dist/esm/managers/ProjectStructureGenerator.js +234 -0
- package/dist/esm/managers/ProjectStructureGenerator.js.map +1 -0
- package/dist/esm/managers/ToolManager.d.ts.map +1 -1
- package/dist/esm/managers/ToolManager.js +4 -2
- package/dist/esm/managers/ToolManager.js.map +1 -1
- package/dist/esm/models/FlowMetadata.d.ts +52 -0
- package/dist/esm/models/FlowMetadata.d.ts.map +1 -1
- package/dist/esm/persistence/flows/FlowsPersistence.d.ts +11 -9
- package/dist/esm/persistence/flows/FlowsPersistence.d.ts.map +1 -1
- package/dist/esm/persistence/flows/FlowsPersistenceAwsS3.d.ts +4 -5
- package/dist/esm/persistence/flows/FlowsPersistenceAwsS3.d.ts.map +1 -1
- package/dist/esm/persistence/flows/FlowsPersistenceAwsS3.js +76 -77
- package/dist/esm/persistence/flows/FlowsPersistenceAwsS3.js.map +1 -1
- package/dist/esm/persistence/flows/FlowsPersistenceFactoryImpl.js +2 -2
- package/dist/esm/persistence/flows/FlowsPersistenceFactoryImpl.js.map +1 -1
- package/dist/esm/persistence/flows/FlowsPersistenceFilesystem.d.ts +7 -7
- package/dist/esm/persistence/flows/FlowsPersistenceFilesystem.d.ts.map +1 -1
- package/dist/esm/persistence/flows/FlowsPersistenceFilesystem.js +51 -60
- package/dist/esm/persistence/flows/FlowsPersistenceFilesystem.js.map +1 -1
- package/dist/esm/persistence/flows/FlowsPersistenceGoogleCloudStorage.d.ts +4 -5
- package/dist/esm/persistence/flows/FlowsPersistenceGoogleCloudStorage.d.ts.map +1 -1
- package/dist/esm/persistence/flows/FlowsPersistenceGoogleCloudStorage.js +40 -41
- package/dist/esm/persistence/flows/FlowsPersistenceGoogleCloudStorage.js.map +1 -1
- package/dist/esm/persistence/flows/FlowsPersistenceSqlite.d.ts +4 -4
- package/dist/esm/persistence/flows/FlowsPersistenceSqlite.d.ts.map +1 -1
- package/dist/esm/persistence/flows/FlowsPersistenceSqlite.js +40 -40
- package/dist/esm/persistence/flows/FlowsPersistenceSqlite.js.map +1 -1
- package/dist/esm/persistence/flows/FlowsPersistenceSupabase.d.ts +4 -5
- package/dist/esm/persistence/flows/FlowsPersistenceSupabase.d.ts.map +1 -1
- package/dist/esm/persistence/flows/FlowsPersistenceSupabase.js +37 -37
- package/dist/esm/persistence/flows/FlowsPersistenceSupabase.js.map +1 -1
- package/dist/esm/persistence/flows/FlowsPersistenceVolatile.d.ts +7 -7
- package/dist/esm/persistence/flows/FlowsPersistenceVolatile.d.ts.map +1 -1
- package/dist/esm/persistence/flows/FlowsPersistenceVolatile.js +35 -33
- package/dist/esm/persistence/flows/FlowsPersistenceVolatile.js.map +1 -1
- package/dist/esm/tools/AssertPageTool.d.ts +34 -0
- package/dist/esm/tools/AssertPageTool.d.ts.map +1 -0
- package/dist/esm/tools/AssertPageTool.js +156 -0
- package/dist/esm/tools/AssertPageTool.js.map +1 -0
- package/dist/esm/tools/AssertTool.js +1 -1
- package/dist/esm/tools/AssertTool.js.map +1 -1
- package/dist/esm/tools/ClickTool.d.ts.map +1 -1
- package/dist/esm/tools/ClickTool.js +3 -0
- package/dist/esm/tools/ClickTool.js.map +1 -1
- package/dist/esm/tools/DetectBrokenLinksTool.js +1 -1
- package/dist/esm/tools/DetectBrokenLinksTool.js.map +1 -1
- package/dist/esm/tools/ExtractGoogleStreetviewEntityDataTool.js +1 -1
- package/dist/esm/tools/ExtractGoogleStreetviewEntityDataTool.js.map +1 -1
- package/dist/esm/tools/ExtractPaymentProviderKeyTool.d.ts +47 -1
- package/dist/esm/tools/ExtractPaymentProviderKeyTool.d.ts.map +1 -1
- package/dist/esm/tools/ExtractPaymentProviderKeyTool.js +160 -25
- package/dist/esm/tools/ExtractPaymentProviderKeyTool.js.map +1 -1
- package/dist/esm/tools/SolveMfaChallenge.d.ts.map +1 -1
- package/dist/esm/tools/SolveMfaChallenge.js.map +1 -1
- package/dist/esm/utils/BrowserUtils.d.ts +52 -0
- package/dist/esm/utils/BrowserUtils.d.ts.map +1 -0
- package/dist/esm/utils/BrowserUtils.js +340 -0
- package/dist/esm/utils/BrowserUtils.js.map +1 -0
- package/dist/esm/utils/PlaywrightUtils.d.ts +15 -11
- package/dist/esm/utils/PlaywrightUtils.d.ts.map +1 -1
- package/dist/esm/utils/PlaywrightUtils.js +110 -122
- package/dist/esm/utils/PlaywrightUtils.js.map +1 -1
- package/dist/lib/autoTest.d.ts +2 -0
- package/dist/lib/autoTest.d.ts.map +1 -0
- package/dist/lib/autoTest.js +17 -0
- package/dist/lib/autoTest.js.map +1 -0
- package/dist/lib/testExtension.d.ts.map +1 -1
- package/dist/lib/testExtension.js +20 -19
- package/dist/lib/testExtension.js.map +1 -1
- package/dist/managers/AdminApiController.d.ts.map +1 -1
- package/dist/managers/AdminApiController.js +1 -0
- package/dist/managers/AdminApiController.js.map +1 -1
- package/dist/managers/CodeGenerator.d.ts +47 -1
- package/dist/managers/CodeGenerator.d.ts.map +1 -1
- package/dist/managers/CodeGenerator.js +211 -19
- package/dist/managers/CodeGenerator.js.map +1 -1
- package/dist/managers/ControlPanel.d.ts.map +1 -1
- package/dist/managers/ControlPanel.js +7 -5
- package/dist/managers/ControlPanel.js.map +1 -1
- package/dist/managers/DonobuFlow.d.ts +3 -4
- package/dist/managers/DonobuFlow.d.ts.map +1 -1
- package/dist/managers/DonobuFlow.js +57 -44
- package/dist/managers/DonobuFlow.js.map +1 -1
- package/dist/managers/DonobuFlowsManager.d.ts +58 -34
- package/dist/managers/DonobuFlowsManager.d.ts.map +1 -1
- package/dist/managers/DonobuFlowsManager.js +217 -64
- package/dist/managers/DonobuFlowsManager.js.map +1 -1
- package/dist/managers/FlowDependencyAnalyzer.d.ts +40 -0
- package/dist/managers/FlowDependencyAnalyzer.d.ts.map +1 -0
- package/dist/managers/FlowDependencyAnalyzer.js +236 -0
- package/dist/managers/FlowDependencyAnalyzer.js.map +1 -0
- package/dist/managers/ProjectStructureGenerator.d.ts +62 -0
- package/dist/managers/ProjectStructureGenerator.d.ts.map +1 -0
- package/dist/managers/ProjectStructureGenerator.js +234 -0
- package/dist/managers/ProjectStructureGenerator.js.map +1 -0
- package/dist/managers/ToolManager.d.ts.map +1 -1
- package/dist/managers/ToolManager.js +4 -2
- package/dist/managers/ToolManager.js.map +1 -1
- package/dist/models/BrowserFramework.d.ts +2 -12
- package/dist/models/BrowserFramework.d.ts.map +1 -1
- package/dist/models/BrowserFramework.js +91 -66
- package/dist/models/BrowserFramework.js.map +1 -1
- package/dist/models/FlowMetadata.d.ts +52 -0
- package/dist/models/FlowMetadata.d.ts.map +1 -1
- package/dist/persistence/flows/FlowsPersistence.d.ts +11 -9
- package/dist/persistence/flows/FlowsPersistence.d.ts.map +1 -1
- package/dist/persistence/flows/FlowsPersistenceAwsS3.d.ts +4 -5
- package/dist/persistence/flows/FlowsPersistenceAwsS3.d.ts.map +1 -1
- package/dist/persistence/flows/FlowsPersistenceAwsS3.js +76 -77
- package/dist/persistence/flows/FlowsPersistenceAwsS3.js.map +1 -1
- package/dist/persistence/flows/FlowsPersistenceFactoryImpl.js +2 -2
- package/dist/persistence/flows/FlowsPersistenceFactoryImpl.js.map +1 -1
- package/dist/persistence/flows/FlowsPersistenceFilesystem.d.ts +7 -7
- package/dist/persistence/flows/FlowsPersistenceFilesystem.d.ts.map +1 -1
- package/dist/persistence/flows/FlowsPersistenceFilesystem.js +51 -60
- package/dist/persistence/flows/FlowsPersistenceFilesystem.js.map +1 -1
- package/dist/persistence/flows/FlowsPersistenceGoogleCloudStorage.d.ts +4 -5
- package/dist/persistence/flows/FlowsPersistenceGoogleCloudStorage.d.ts.map +1 -1
- package/dist/persistence/flows/FlowsPersistenceGoogleCloudStorage.js +40 -41
- package/dist/persistence/flows/FlowsPersistenceGoogleCloudStorage.js.map +1 -1
- package/dist/persistence/flows/FlowsPersistenceSqlite.d.ts +4 -4
- package/dist/persistence/flows/FlowsPersistenceSqlite.d.ts.map +1 -1
- package/dist/persistence/flows/FlowsPersistenceSqlite.js +40 -40
- package/dist/persistence/flows/FlowsPersistenceSqlite.js.map +1 -1
- package/dist/persistence/flows/FlowsPersistenceSupabase.d.ts +4 -5
- package/dist/persistence/flows/FlowsPersistenceSupabase.d.ts.map +1 -1
- package/dist/persistence/flows/FlowsPersistenceSupabase.js +37 -37
- package/dist/persistence/flows/FlowsPersistenceSupabase.js.map +1 -1
- package/dist/persistence/flows/FlowsPersistenceVolatile.d.ts +7 -7
- package/dist/persistence/flows/FlowsPersistenceVolatile.d.ts.map +1 -1
- package/dist/persistence/flows/FlowsPersistenceVolatile.js +35 -33
- package/dist/persistence/flows/FlowsPersistenceVolatile.js.map +1 -1
- package/dist/tools/AssertPageTool.d.ts +34 -0
- package/dist/tools/AssertPageTool.d.ts.map +1 -0
- package/dist/tools/AssertPageTool.js +156 -0
- package/dist/tools/AssertPageTool.js.map +1 -0
- package/dist/tools/AssertTool.js +1 -1
- package/dist/tools/AssertTool.js.map +1 -1
- package/dist/tools/ClickTool.d.ts.map +1 -1
- package/dist/tools/ClickTool.js +3 -0
- package/dist/tools/ClickTool.js.map +1 -1
- package/dist/tools/DetectBrokenLinksTool.js +1 -1
- package/dist/tools/DetectBrokenLinksTool.js.map +1 -1
- package/dist/tools/ExtractGoogleStreetviewEntityDataTool.js +1 -1
- package/dist/tools/ExtractGoogleStreetviewEntityDataTool.js.map +1 -1
- package/dist/tools/ExtractPaymentProviderKeyTool.d.ts +47 -1
- package/dist/tools/ExtractPaymentProviderKeyTool.d.ts.map +1 -1
- package/dist/tools/ExtractPaymentProviderKeyTool.js +160 -25
- package/dist/tools/ExtractPaymentProviderKeyTool.js.map +1 -1
- package/dist/tools/SolveMfaChallenge.d.ts.map +1 -1
- package/dist/tools/SolveMfaChallenge.js.map +1 -1
- package/dist/utils/BrowserUtils.d.ts +52 -0
- package/dist/utils/BrowserUtils.d.ts.map +1 -0
- package/dist/utils/BrowserUtils.js +340 -0
- package/dist/utils/BrowserUtils.js.map +1 -0
- package/dist/utils/PlaywrightUtils.d.ts +15 -11
- package/dist/utils/PlaywrightUtils.d.ts.map +1 -1
- package/dist/utils/PlaywrightUtils.js +110 -122
- package/dist/utils/PlaywrightUtils.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,340 @@
|
|
|
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.BrowserUtils = void 0;
|
|
7
|
+
const playwright_1 = require("playwright");
|
|
8
|
+
const JsonUtils_1 = require("./JsonUtils");
|
|
9
|
+
const InvalidParamValueException_1 = require("../exceptions/InvalidParamValueException");
|
|
10
|
+
const path_1 = __importDefault(require("path"));
|
|
11
|
+
const envVars_1 = require("../envVars");
|
|
12
|
+
const Logger_1 = require("./Logger");
|
|
13
|
+
class BrowserUtils {
|
|
14
|
+
/**
|
|
15
|
+
* Loads all the pre-canned browser device configurations from the local
|
|
16
|
+
* filesystem.
|
|
17
|
+
*
|
|
18
|
+
* The returned map keys the devices by their name (ex: 'Desktop Firefox').
|
|
19
|
+
*
|
|
20
|
+
* See `assets/devices.json` for details.
|
|
21
|
+
*/
|
|
22
|
+
static getSupportedDevices() {
|
|
23
|
+
const rawDevices = JsonUtils_1.JsonUtils.readResourceAsJson(path_1.default.join('devices.json'));
|
|
24
|
+
if (!rawDevices) {
|
|
25
|
+
throw new Error('Failed to load devices configuration');
|
|
26
|
+
}
|
|
27
|
+
return new Map(Object.entries(rawDevices));
|
|
28
|
+
}
|
|
29
|
+
static async create(browserConfig, outputDir, storageState) {
|
|
30
|
+
const type = browserConfig.using.type;
|
|
31
|
+
let browser;
|
|
32
|
+
let browserContext;
|
|
33
|
+
switch (type) {
|
|
34
|
+
case 'device': {
|
|
35
|
+
({ browser, browserContext } = await BrowserUtils.forDevice(browserConfig.using.deviceName ?? BrowserUtils.DEFAULT_DEVICE_NAME, browserConfig.using.headless ?? false, outputDir, storageState));
|
|
36
|
+
BrowserUtils.patchClose(browserContext, () => browser.close());
|
|
37
|
+
break;
|
|
38
|
+
}
|
|
39
|
+
case 'remoteInstance': {
|
|
40
|
+
({ browser, browserContext } = await BrowserUtils.forRemoteBrowser(browserConfig.using.url, outputDir, storageState));
|
|
41
|
+
BrowserUtils.patchClose(browserContext, () => browser.close());
|
|
42
|
+
break;
|
|
43
|
+
}
|
|
44
|
+
case 'browserBase': {
|
|
45
|
+
let browserBaseData;
|
|
46
|
+
({ browser, browserContext, browserBaseData } =
|
|
47
|
+
await BrowserUtils.forBrowserBase(browserConfig.using.sessionArgs, outputDir, storageState));
|
|
48
|
+
BrowserUtils.patchClose(browserContext, async () => {
|
|
49
|
+
try {
|
|
50
|
+
await browser.close();
|
|
51
|
+
}
|
|
52
|
+
catch (_error) {
|
|
53
|
+
// Ignore, the browser may have already been closed and that is fine.
|
|
54
|
+
}
|
|
55
|
+
const body = {
|
|
56
|
+
projectId: browserBaseData.projectId,
|
|
57
|
+
status: 'REQUEST_RELEASE',
|
|
58
|
+
};
|
|
59
|
+
const password = process.env[envVars_1.ENV_VAR_NAMES.BROWSERBASE_API_KEY] ?? '';
|
|
60
|
+
const options = {
|
|
61
|
+
method: 'POST',
|
|
62
|
+
headers: {
|
|
63
|
+
'X-BB-API-Key': password,
|
|
64
|
+
'Content-Type': 'application/json',
|
|
65
|
+
},
|
|
66
|
+
body: JSON.stringify(body),
|
|
67
|
+
};
|
|
68
|
+
try {
|
|
69
|
+
await fetch(`https://api.browserbase.com/v1/sessions/${browserBaseData.id}`, options);
|
|
70
|
+
}
|
|
71
|
+
catch (error) {
|
|
72
|
+
// Ignore, BrowserBase sessions expire automatically anyway.
|
|
73
|
+
Logger_1.appLogger.warn('Failed to release BrowserBase session', error);
|
|
74
|
+
}
|
|
75
|
+
});
|
|
76
|
+
break;
|
|
77
|
+
}
|
|
78
|
+
default: {
|
|
79
|
+
throw new InvalidParamValueException_1.InvalidParamValueException('type', type);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
await BrowserUtils.attachSessionStorageToBrowserContext(browserContext, storageState);
|
|
83
|
+
return browserContext;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Gets the browser storage state including cookies, localStorage, and sessionStorage.
|
|
87
|
+
*
|
|
88
|
+
* @param browserContext The browser context to extract storage state from.
|
|
89
|
+
* @returns A promise that resolves to the complete browser storage state.
|
|
90
|
+
*/
|
|
91
|
+
static async getBrowserStorageState(browserContext) {
|
|
92
|
+
let result;
|
|
93
|
+
try {
|
|
94
|
+
// First get the standard storage state (cookies and localStorage)
|
|
95
|
+
result = await browserContext.storageState({
|
|
96
|
+
indexedDB: true,
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
catch (error) {
|
|
100
|
+
Logger_1.appLogger.warn('Failed to get storage state with indexedDB, falling back to cookies only', error);
|
|
101
|
+
result = await browserContext.storageState({
|
|
102
|
+
indexedDB: false,
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
// Get all pages in the context
|
|
106
|
+
const pages = browserContext.pages();
|
|
107
|
+
// Process each page to collect sessionStorage data
|
|
108
|
+
for (const page of pages) {
|
|
109
|
+
try {
|
|
110
|
+
// Get the origin for the current page
|
|
111
|
+
const pageOrigin = new URL(page.url()).origin;
|
|
112
|
+
// Find if we already have an entry for this origin
|
|
113
|
+
let originEntry = result.origins.find((entry) => entry.origin === pageOrigin);
|
|
114
|
+
// If not, create a new entry
|
|
115
|
+
if (!originEntry) {
|
|
116
|
+
originEntry = {
|
|
117
|
+
origin: pageOrigin,
|
|
118
|
+
localStorage: [],
|
|
119
|
+
sessionStorage: [],
|
|
120
|
+
};
|
|
121
|
+
result.origins.push(originEntry);
|
|
122
|
+
}
|
|
123
|
+
else if (!('sessionStorage' in originEntry)) {
|
|
124
|
+
// If the entry exists but doesn't have sessionStorage yet, add the property
|
|
125
|
+
originEntry.sessionStorage = [];
|
|
126
|
+
}
|
|
127
|
+
// Extract sessionStorage from the page
|
|
128
|
+
const sessionStorageItems = await page.evaluate(() => {
|
|
129
|
+
const items = [];
|
|
130
|
+
for (let i = 0; i < sessionStorage.length; i++) {
|
|
131
|
+
const name = sessionStorage.key(i);
|
|
132
|
+
if (name) {
|
|
133
|
+
items.push({
|
|
134
|
+
name,
|
|
135
|
+
value: sessionStorage.getItem(name) || '',
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
return items;
|
|
140
|
+
});
|
|
141
|
+
// Add sessionStorage items to the origin entry
|
|
142
|
+
originEntry.sessionStorage = sessionStorageItems;
|
|
143
|
+
}
|
|
144
|
+
catch (error) {
|
|
145
|
+
Logger_1.appLogger.warn(`Failed to extract sessionStorage for page: ${page.url()}`, error);
|
|
146
|
+
// Skip pages that might have navigation errors or are about:blank.
|
|
147
|
+
continue;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
return result;
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Connects to an existing Chromium browser using the Chrome DevTools Protocol (CDP) at the given
|
|
154
|
+
* URL.
|
|
155
|
+
*/
|
|
156
|
+
static async forRemoteBrowser(remoteBrowserInstanceUrl, outputDir, storageState) {
|
|
157
|
+
try {
|
|
158
|
+
const browser = await playwright_1.chromium.connectOverCDP(remoteBrowserInstanceUrl);
|
|
159
|
+
try {
|
|
160
|
+
const contextOptions = {
|
|
161
|
+
recordVideo: { dir: outputDir },
|
|
162
|
+
};
|
|
163
|
+
if (storageState) {
|
|
164
|
+
contextOptions.storageState = storageState;
|
|
165
|
+
}
|
|
166
|
+
return {
|
|
167
|
+
browser: browser,
|
|
168
|
+
browserContext: await browser.newContext(contextOptions),
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
catch (error) {
|
|
172
|
+
await browser.close();
|
|
173
|
+
throw error;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
catch (_) {
|
|
177
|
+
throw new InvalidParamValueException_1.InvalidParamValueException('remoteBrowserInstanceUrl', remoteBrowserInstanceUrl);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* If {@link storageState} is present, must be an object conforming to what is returned by
|
|
182
|
+
* {@link BrowserContext.storageState()}.
|
|
183
|
+
*/
|
|
184
|
+
static async forDevice(deviceName, headless, outputDir, storageState) {
|
|
185
|
+
const device = BrowserUtils.getSupportedDevices().get(deviceName);
|
|
186
|
+
if (!device) {
|
|
187
|
+
throw new InvalidParamValueException_1.InvalidParamValueException('deviceName', deviceName);
|
|
188
|
+
}
|
|
189
|
+
const contextOptions = {
|
|
190
|
+
userAgent: device.userAgent,
|
|
191
|
+
recordVideo: {
|
|
192
|
+
dir: outputDir,
|
|
193
|
+
size: {
|
|
194
|
+
width: device.viewport?.width ?? 1280,
|
|
195
|
+
height: device.viewport?.height ?? 720,
|
|
196
|
+
},
|
|
197
|
+
},
|
|
198
|
+
viewport: {
|
|
199
|
+
width: device.viewport?.width ?? 1280,
|
|
200
|
+
height: device.viewport?.height ?? 720,
|
|
201
|
+
},
|
|
202
|
+
screen: {
|
|
203
|
+
width: device.screen?.width ?? device.viewport?.width ?? 1280,
|
|
204
|
+
height: device.screen?.height ?? device.viewport?.height ?? 720,
|
|
205
|
+
},
|
|
206
|
+
deviceScaleFactor: device.deviceScaleFactor ?? 1.0,
|
|
207
|
+
isMobile: device.isMobile ?? false,
|
|
208
|
+
hasTouch: device.hasTouch ?? false,
|
|
209
|
+
};
|
|
210
|
+
if (storageState) {
|
|
211
|
+
contextOptions.storageState = storageState;
|
|
212
|
+
}
|
|
213
|
+
const launchOptions = {
|
|
214
|
+
headless,
|
|
215
|
+
args: [
|
|
216
|
+
'--ignore-certificate-errors',
|
|
217
|
+
'--disable-blink-features=AutomationControlled',
|
|
218
|
+
],
|
|
219
|
+
};
|
|
220
|
+
let browser;
|
|
221
|
+
switch (device.defaultBrowserType.toLowerCase()) {
|
|
222
|
+
case 'firefox':
|
|
223
|
+
browser = await playwright_1.firefox.launch(launchOptions);
|
|
224
|
+
break;
|
|
225
|
+
case 'chromium':
|
|
226
|
+
browser = await playwright_1.chromium.launch(launchOptions);
|
|
227
|
+
break;
|
|
228
|
+
case 'chrome':
|
|
229
|
+
browser = await playwright_1.chromium.launch({
|
|
230
|
+
...launchOptions,
|
|
231
|
+
channel: 'chrome',
|
|
232
|
+
});
|
|
233
|
+
break;
|
|
234
|
+
case 'webkit':
|
|
235
|
+
case 'ios':
|
|
236
|
+
browser = await playwright_1.webkit.launch(launchOptions);
|
|
237
|
+
break;
|
|
238
|
+
default:
|
|
239
|
+
throw new InvalidParamValueException_1.InvalidParamValueException('browserType', device.defaultBrowserType);
|
|
240
|
+
}
|
|
241
|
+
try {
|
|
242
|
+
const browserContext = await browser.newContext(contextOptions);
|
|
243
|
+
return { browser, browserContext };
|
|
244
|
+
}
|
|
245
|
+
catch (error) {
|
|
246
|
+
await browser.close();
|
|
247
|
+
throw error;
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
/**
|
|
251
|
+
* Creates a BrowserBase session. Using this method requires the
|
|
252
|
+
* BROWSERBASE_API_KEY environment variable to be set.
|
|
253
|
+
*
|
|
254
|
+
* The returned browserBaseData object conforms to the response of the session
|
|
255
|
+
* creation API endpoint. See...
|
|
256
|
+
* https://docs.browserbase.com/reference/api/create-a-session#response-id
|
|
257
|
+
*/
|
|
258
|
+
static async forBrowserBase(sessionArgs, outputDir, storageState) {
|
|
259
|
+
const browserBaseData = await BrowserUtils.establishBrowserBaseSession(sessionArgs);
|
|
260
|
+
const browser = await playwright_1.chromium.connectOverCDP(browserBaseData.connectUrl);
|
|
261
|
+
const contextOptions = {
|
|
262
|
+
recordVideo: { dir: outputDir },
|
|
263
|
+
};
|
|
264
|
+
if (storageState) {
|
|
265
|
+
contextOptions.storageState = storageState;
|
|
266
|
+
}
|
|
267
|
+
return {
|
|
268
|
+
browser: browser,
|
|
269
|
+
browserContext: await browser.newContext(contextOptions),
|
|
270
|
+
browserBaseData: browserBaseData,
|
|
271
|
+
};
|
|
272
|
+
}
|
|
273
|
+
/**
|
|
274
|
+
* Establishes a BrowserBase session. The returned structure matches the
|
|
275
|
+
* response structore from the BrowserBase session API. See...
|
|
276
|
+
* https://docs.browserbase.com/reference/api/create-a-session#response-id
|
|
277
|
+
*/
|
|
278
|
+
static async establishBrowserBaseSession(sessionArgs) {
|
|
279
|
+
const password = process.env[envVars_1.ENV_VAR_NAMES.BROWSERBASE_API_KEY];
|
|
280
|
+
if (!password) {
|
|
281
|
+
throw new InvalidParamValueException_1.InvalidParamValueException(envVars_1.ENV_VAR_NAMES.BROWSERBASE_API_KEY, null);
|
|
282
|
+
}
|
|
283
|
+
const options = {
|
|
284
|
+
method: 'POST',
|
|
285
|
+
headers: { 'X-BB-API-Key': password, 'Content-Type': 'application/json' },
|
|
286
|
+
body: JSON.stringify(sessionArgs),
|
|
287
|
+
};
|
|
288
|
+
const browserBaseData = await fetch('https://api.browserbase.com/v1/sessions', options).then((response) => response.json());
|
|
289
|
+
if (browserBaseData.error) {
|
|
290
|
+
throw new InvalidParamValueException_1.InvalidParamValueException(envVars_1.ENV_VAR_NAMES.BROWSERBASE_API_KEY, '(redacted)', `${browserBaseData.error}: ${browserBaseData.message}`);
|
|
291
|
+
}
|
|
292
|
+
return browserBaseData;
|
|
293
|
+
}
|
|
294
|
+
static async attachSessionStorageToBrowserContext(browserContext, storageState) {
|
|
295
|
+
// Add init script to restore sessionStorage if storage state is provided
|
|
296
|
+
if (storageState && storageState.origins) {
|
|
297
|
+
// Transform the storage state to map origins to their sessionStorage
|
|
298
|
+
const sessionStorageByOrigin = {};
|
|
299
|
+
for (const origin of storageState.origins) {
|
|
300
|
+
if (origin.sessionStorage && origin.sessionStorage.length > 0) {
|
|
301
|
+
// Create a key-value map for this origin's sessionStorage
|
|
302
|
+
const sessionStorageMap = {};
|
|
303
|
+
for (const item of origin.sessionStorage) {
|
|
304
|
+
sessionStorageMap[item.name] = item.value;
|
|
305
|
+
}
|
|
306
|
+
sessionStorageByOrigin[origin.origin] = sessionStorageMap;
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
// Add the init script to restore sessionStorage based on the page's origin
|
|
310
|
+
await browserContext.addInitScript((storageData) => {
|
|
311
|
+
// Get current origin
|
|
312
|
+
const currentOrigin = window.location.origin;
|
|
313
|
+
// Check if we have sessionStorage data for this origin
|
|
314
|
+
if (storageData[currentOrigin]) {
|
|
315
|
+
// Restore the sessionStorage items
|
|
316
|
+
for (const [key, value] of Object.entries(storageData[currentOrigin])) {
|
|
317
|
+
window.sessionStorage.setItem(key, value);
|
|
318
|
+
}
|
|
319
|
+
console.log(`Restored ${Object.keys(storageData[currentOrigin]).length} sessionStorage items for ${currentOrigin}`);
|
|
320
|
+
}
|
|
321
|
+
}, sessionStorageByOrigin);
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
static patchClose(ctx, extra) {
|
|
325
|
+
const original = ctx.close.bind(ctx);
|
|
326
|
+
ctx.close = async (...args) => {
|
|
327
|
+
try {
|
|
328
|
+
await original(...args);
|
|
329
|
+
}
|
|
330
|
+
catch { }
|
|
331
|
+
try {
|
|
332
|
+
await extra();
|
|
333
|
+
}
|
|
334
|
+
catch { }
|
|
335
|
+
};
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
exports.BrowserUtils = BrowserUtils;
|
|
339
|
+
BrowserUtils.DEFAULT_DEVICE_NAME = 'Desktop Chromium';
|
|
340
|
+
//# sourceMappingURL=BrowserUtils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"BrowserUtils.js","sourceRoot":"","sources":["../../../src/utils/BrowserUtils.ts"],"names":[],"mappings":"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"}
|
|
@@ -28,7 +28,21 @@ export declare class PlaywrightUtils {
|
|
|
28
28
|
* Note that if the Donobu control panel is present in the page, it will be
|
|
29
29
|
* excluded from the screenshot.
|
|
30
30
|
*/
|
|
31
|
-
static
|
|
31
|
+
static takeViewportScreenshot(page: Page, type?: 'png' | 'jpeg'): Promise<Buffer>;
|
|
32
|
+
/**
|
|
33
|
+
* Captures up to three screenshots of the given page.
|
|
34
|
+
*
|
|
35
|
+
* - `viewport` : exact current viewport
|
|
36
|
+
* - `aboveViewport` : up to half-viewport preview just above the fold (cropped to exclude main viewport)
|
|
37
|
+
* - `belowViewport` : up to half-viewport preview just below the fold (cropped to exclude main viewport)
|
|
38
|
+
*
|
|
39
|
+
* All images are returned as JPEG buffers.
|
|
40
|
+
*/
|
|
41
|
+
static takePageScreenshots(page: Page): Promise<{
|
|
42
|
+
aboveViewport: Buffer | null;
|
|
43
|
+
viewport: Buffer;
|
|
44
|
+
belowViewport: Buffer | null;
|
|
45
|
+
}>;
|
|
32
46
|
/**
|
|
33
47
|
* Generate valid selectors for the given element. The generated selectors are
|
|
34
48
|
* in a priority order based on their ability to identify the given element.
|
|
@@ -59,15 +73,5 @@ export declare class PlaywrightUtils {
|
|
|
59
73
|
* it is logged and ignored. If page is null, this function has no effect.
|
|
60
74
|
*/
|
|
61
75
|
static waitForPageStability(page: Page | null): Promise<void>;
|
|
62
|
-
/**
|
|
63
|
-
* Takes a screenshot of the webpage as if it were scrolled down by one viewport.
|
|
64
|
-
* If the page cannot be scrolled as a whole, tries to find and scroll individual
|
|
65
|
-
* scrollable elements on the page. After taking the screenshot, it reverts to
|
|
66
|
-
* the original scroll position.
|
|
67
|
-
*
|
|
68
|
-
* @param page The Playwright Page to take a screenshot of
|
|
69
|
-
* @returns An image buffer of the scrolled down view, or undefined if scrolling isn't possible
|
|
70
|
-
*/
|
|
71
|
-
static getScrolledDownScreenShot(page: Page): Promise<Buffer | undefined>;
|
|
72
76
|
}
|
|
73
77
|
//# sourceMappingURL=PlaywrightUtils.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PlaywrightUtils.d.ts","sourceRoot":"","sources":["../../../src/utils/PlaywrightUtils.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAUlD;;;GAGG;AACH,qBAAa,eAAe;IAC1B;;;;;;OAMG;IACH,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,iCAAiC,CACkB;IAE3E;;;;;OAKG;IACH,gBAAuB,oCAAoC,SACmB;IAE9E;;;;;;;;OAQG;WACiB,
|
|
1
|
+
{"version":3,"file":"PlaywrightUtils.d.ts","sourceRoot":"","sources":["../../../src/utils/PlaywrightUtils.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAUlD;;;GAGG;AACH,qBAAa,eAAe;IAC1B;;;;;;OAMG;IACH,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,iCAAiC,CACkB;IAE3E;;;;;OAKG;IACH,gBAAuB,oCAAoC,SACmB;IAE9E;;;;;;;;OAQG;WACiB,sBAAsB,CACxC,IAAI,EAAE,IAAI,EACV,IAAI,GAAE,KAAK,GAAG,MAAe,GAC5B,OAAO,CAAC,MAAM,CAAC;IAgBlB;;;;;;;;OAQG;WACiB,mBAAmB,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC;QAC3D,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;QAC7B,QAAQ,EAAE,MAAM,CAAC;QACjB,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;KAC9B,CAAC;IA8HF;;;;;;OAMG;WACiB,iBAAiB,CAAC,OAAO,EAAE;QAC7C,QAAQ,EAAE,CAAC,YAAY,EAAE,GAAG,KAAK,OAAO,CAAC,GAAG,CAAC,CAAC;KAC/C,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;IAMrB;;;;;OAKG;WACiB,wBAAwB,CAC1C,cAAc,EAAE,cAAc,GAC7B,OAAO,CAAC,IAAI,CAAC;IAWhB;;;OAGG;WACW,iBAAiB,CAAC,KAAK,EAAE,GAAG,GAAG,OAAO;WAkBhC,4BAA4B,IAAI,OAAO,CAAC,IAAI,CAAC;WAI7C,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAgDnE;;;;OAIG;WACiB,oBAAoB,CAAC,IAAI,EAAE,IAAI,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;CAuB3E"}
|
|
@@ -26,7 +26,7 @@ class PlaywrightUtils {
|
|
|
26
26
|
* Note that if the Donobu control panel is present in the page, it will be
|
|
27
27
|
* excluded from the screenshot.
|
|
28
28
|
*/
|
|
29
|
-
static async
|
|
29
|
+
static async takeViewportScreenshot(page, type = 'jpeg') {
|
|
30
30
|
try {
|
|
31
31
|
const style = `#${ControlPanel_1.ControlPanel.DONOBU_CONTROL_PANEL_ELEMENT_ID} { display: none !important; }`;
|
|
32
32
|
return await page.screenshot({
|
|
@@ -43,6 +43,115 @@ class PlaywrightUtils {
|
|
|
43
43
|
}
|
|
44
44
|
}
|
|
45
45
|
}
|
|
46
|
+
/**
|
|
47
|
+
* Captures up to three screenshots of the given page.
|
|
48
|
+
*
|
|
49
|
+
* - `viewport` : exact current viewport
|
|
50
|
+
* - `aboveViewport` : up to half-viewport preview just above the fold (cropped to exclude main viewport)
|
|
51
|
+
* - `belowViewport` : up to half-viewport preview just below the fold (cropped to exclude main viewport)
|
|
52
|
+
*
|
|
53
|
+
* All images are returned as JPEG buffers.
|
|
54
|
+
*/
|
|
55
|
+
static async takePageScreenshots(page) {
|
|
56
|
+
const screenshotStyleOverride = `#${ControlPanel_1.ControlPanel.DONOBU_CONTROL_PANEL_ELEMENT_ID} { display: none !important; }`;
|
|
57
|
+
// Take the screenshot of the current viewport immediately – this is always available.
|
|
58
|
+
const viewportBuffer = await PlaywrightUtils.takeViewportScreenshot(page, 'jpeg');
|
|
59
|
+
// If the viewport size is not available we cannot reliably scroll.
|
|
60
|
+
const viewportSize = page.viewportSize();
|
|
61
|
+
if (!viewportSize) {
|
|
62
|
+
return {
|
|
63
|
+
aboveViewport: null,
|
|
64
|
+
viewport: viewportBuffer,
|
|
65
|
+
belowViewport: null,
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
const viewportHeight = viewportSize.height;
|
|
69
|
+
const halfViewport = Math.floor(viewportHeight / 2);
|
|
70
|
+
// Capture original scroll position & page dimensions so we can both
|
|
71
|
+
// determine if we *can* scroll and later restore state.
|
|
72
|
+
const { originalScrollY, scrollHeight } = await page.evaluate(() => {
|
|
73
|
+
return {
|
|
74
|
+
originalScrollY: window.scrollY || window.pageYOffset || 0,
|
|
75
|
+
scrollHeight: document.documentElement.scrollHeight ||
|
|
76
|
+
document.body.scrollHeight ||
|
|
77
|
+
0,
|
|
78
|
+
};
|
|
79
|
+
});
|
|
80
|
+
let aboveBuffer = null;
|
|
81
|
+
let belowBuffer = null;
|
|
82
|
+
// Always wrap scrolling logic with try/finally so that we restore the
|
|
83
|
+
// initial scroll position even when an unexpected error occurs.
|
|
84
|
+
try {
|
|
85
|
+
/************************************************************
|
|
86
|
+
* Capture the area *above* the current viewport ("just above the fold")
|
|
87
|
+
************************************************************/
|
|
88
|
+
if (originalScrollY > 0) {
|
|
89
|
+
const targetY = Math.max(0, originalScrollY - halfViewport);
|
|
90
|
+
await page.evaluate((y) => window.scrollTo(0, y), targetY);
|
|
91
|
+
// Give the browser a tiny bit of time – many sites throttle paint.
|
|
92
|
+
await page.waitForTimeout(100);
|
|
93
|
+
// Calculate the non-overlapping portion to capture
|
|
94
|
+
const overlapPixels = Math.max(0, targetY + viewportHeight - originalScrollY);
|
|
95
|
+
const cropHeight = viewportHeight - overlapPixels;
|
|
96
|
+
if (cropHeight > 0) {
|
|
97
|
+
// Use Playwright's clip parameter to capture only the non-overlapping portion
|
|
98
|
+
aboveBuffer = await page.screenshot({
|
|
99
|
+
style: screenshotStyleOverride,
|
|
100
|
+
type: 'jpeg',
|
|
101
|
+
clip: {
|
|
102
|
+
x: 0,
|
|
103
|
+
y: 0,
|
|
104
|
+
width: viewportSize.width,
|
|
105
|
+
height: cropHeight,
|
|
106
|
+
},
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
/************************************************************
|
|
111
|
+
* Capture the area *below* the current viewport ("just below the fold")
|
|
112
|
+
************************************************************/
|
|
113
|
+
if (originalScrollY + viewportHeight < scrollHeight) {
|
|
114
|
+
const targetY = Math.min(scrollHeight - viewportHeight, originalScrollY + halfViewport);
|
|
115
|
+
await page.evaluate((y) => window.scrollTo(0, y), targetY);
|
|
116
|
+
await page.waitForTimeout(100);
|
|
117
|
+
// Calculate the non-overlapping portion to capture
|
|
118
|
+
const overlapPixels = Math.max(0, originalScrollY + viewportHeight - targetY);
|
|
119
|
+
const cropY = overlapPixels;
|
|
120
|
+
const cropHeight = viewportHeight - overlapPixels;
|
|
121
|
+
if (cropHeight > 0) {
|
|
122
|
+
// Use Playwright's clip parameter to capture only the non-overlapping portion
|
|
123
|
+
belowBuffer = await page.screenshot({
|
|
124
|
+
style: screenshotStyleOverride,
|
|
125
|
+
type: 'jpeg',
|
|
126
|
+
clip: {
|
|
127
|
+
x: 0,
|
|
128
|
+
y: cropY,
|
|
129
|
+
width: viewportSize.width,
|
|
130
|
+
height: cropHeight,
|
|
131
|
+
},
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
finally {
|
|
137
|
+
// Always attempt to restore the page to its initial scroll position so
|
|
138
|
+
// that callers see *no* side‑effects.
|
|
139
|
+
try {
|
|
140
|
+
if ((await page.evaluate(() => window.scrollY)) !== originalScrollY) {
|
|
141
|
+
await page.evaluate((y) => window.scrollTo(0, y), originalScrollY);
|
|
142
|
+
await page.waitForTimeout(50);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
catch (restoreError) {
|
|
146
|
+
Logger_1.appLogger.error(`Failed to restore scroll position for page ${page.url()}`, restoreError);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
return {
|
|
150
|
+
aboveViewport: aboveBuffer,
|
|
151
|
+
viewport: viewportBuffer,
|
|
152
|
+
belowViewport: belowBuffer,
|
|
153
|
+
};
|
|
154
|
+
}
|
|
46
155
|
/**
|
|
47
156
|
* Generate valid selectors for the given element. The generated selectors are
|
|
48
157
|
* in a priority order based on their ability to identify the given element.
|
|
@@ -156,127 +265,6 @@ class PlaywrightUtils {
|
|
|
156
265
|
}
|
|
157
266
|
}
|
|
158
267
|
}
|
|
159
|
-
/**
|
|
160
|
-
* Takes a screenshot of the webpage as if it were scrolled down by one viewport.
|
|
161
|
-
* If the page cannot be scrolled as a whole, tries to find and scroll individual
|
|
162
|
-
* scrollable elements on the page. After taking the screenshot, it reverts to
|
|
163
|
-
* the original scroll position.
|
|
164
|
-
*
|
|
165
|
-
* @param page The Playwright Page to take a screenshot of
|
|
166
|
-
* @returns An image buffer of the scrolled down view, or undefined if scrolling isn't possible
|
|
167
|
-
*/
|
|
168
|
-
static async getScrolledDownScreenShot(page) {
|
|
169
|
-
try {
|
|
170
|
-
// Get the current scroll position and viewport height
|
|
171
|
-
const { scrollX, scrollY, viewportHeight } = await page.evaluate(() => {
|
|
172
|
-
return {
|
|
173
|
-
scrollX: window.scrollX,
|
|
174
|
-
scrollY: window.scrollY,
|
|
175
|
-
viewportHeight: window.innerHeight,
|
|
176
|
-
};
|
|
177
|
-
});
|
|
178
|
-
// Step 1: Try to scroll the window down by one viewport
|
|
179
|
-
await page.evaluate((viewportHeight) => {
|
|
180
|
-
window.scrollBy(0, viewportHeight);
|
|
181
|
-
}, viewportHeight);
|
|
182
|
-
// Check if the page actually scrolled
|
|
183
|
-
const newScrollY = await page.evaluate(() => window.scrollY);
|
|
184
|
-
// If the window scrolled successfully, take the screenshot and return
|
|
185
|
-
if (newScrollY > scrollY) {
|
|
186
|
-
// Take the screenshot
|
|
187
|
-
const screenshot = await PlaywrightUtils.takeScreenshot(page);
|
|
188
|
-
// Scroll back to the original position
|
|
189
|
-
await page.evaluate(([x, y]) => {
|
|
190
|
-
window.scrollTo(x, y);
|
|
191
|
-
}, [scrollX, scrollY]);
|
|
192
|
-
return screenshot;
|
|
193
|
-
}
|
|
194
|
-
// Step 2: If window didn't scroll, try to find scrollable elements
|
|
195
|
-
const didScrollElement = await page.evaluate(() => {
|
|
196
|
-
// Find all potentially scrollable elements
|
|
197
|
-
function findScrollableElements() {
|
|
198
|
-
const allElements = document.querySelectorAll('*');
|
|
199
|
-
const scrollableElements = [];
|
|
200
|
-
allElements.forEach((el) => {
|
|
201
|
-
const style = window.getComputedStyle(el);
|
|
202
|
-
const overflowY = style.getPropertyValue('overflow-y');
|
|
203
|
-
const overflowX = style.getPropertyValue('overflow-x');
|
|
204
|
-
const overflow = style.getPropertyValue('overflow');
|
|
205
|
-
// Check if the element has scrollable properties
|
|
206
|
-
if (overflowY === 'auto' ||
|
|
207
|
-
overflowY === 'scroll' ||
|
|
208
|
-
overflowX === 'auto' ||
|
|
209
|
-
overflowX === 'scroll' ||
|
|
210
|
-
overflow === 'auto' ||
|
|
211
|
-
overflow === 'scroll') {
|
|
212
|
-
// Additional check to see if the element actually has content to scroll
|
|
213
|
-
const scrollHeight = el.scrollHeight;
|
|
214
|
-
const clientHeight = el.clientHeight;
|
|
215
|
-
if (scrollHeight > clientHeight) {
|
|
216
|
-
scrollableElements.push({
|
|
217
|
-
element: el,
|
|
218
|
-
area: el.clientWidth * el.clientHeight, // Calculate area for sorting
|
|
219
|
-
scrollTop: el.scrollTop, // Save original scroll position
|
|
220
|
-
});
|
|
221
|
-
}
|
|
222
|
-
}
|
|
223
|
-
});
|
|
224
|
-
return scrollableElements;
|
|
225
|
-
}
|
|
226
|
-
// Find and sort scrollable elements by size (largest first)
|
|
227
|
-
const scrollableElements = findScrollableElements().sort((a, b) => b.area - a.area);
|
|
228
|
-
let didScroll = false;
|
|
229
|
-
// Try to scroll each element until one successfully scrolls
|
|
230
|
-
for (const { element } of scrollableElements) {
|
|
231
|
-
const originalScrollTop = element.scrollTop;
|
|
232
|
-
// Try to scroll down by the element's client height
|
|
233
|
-
element.scrollTop += element.clientHeight;
|
|
234
|
-
// Check if scrolling was successful
|
|
235
|
-
if (element.scrollTop > originalScrollTop) {
|
|
236
|
-
didScroll = true;
|
|
237
|
-
// Save the element in the window object so we can access it later
|
|
238
|
-
// @ts-ignore
|
|
239
|
-
window.__lastScrolledElement = element;
|
|
240
|
-
// @ts-ignore
|
|
241
|
-
window.__lastScrolledElementOriginalPosition = originalScrollTop;
|
|
242
|
-
break;
|
|
243
|
-
}
|
|
244
|
-
}
|
|
245
|
-
return didScroll;
|
|
246
|
-
});
|
|
247
|
-
// If no element could be scrolled, return undefined
|
|
248
|
-
if (!didScrollElement) {
|
|
249
|
-
return undefined;
|
|
250
|
-
}
|
|
251
|
-
// Take a screenshot after scrolling the element
|
|
252
|
-
const screenshot = await PlaywrightUtils.takeScreenshot(page);
|
|
253
|
-
// Restore the original scroll position of the element
|
|
254
|
-
await page.evaluate(() => {
|
|
255
|
-
if (
|
|
256
|
-
// @ts-ignore
|
|
257
|
-
window.__lastScrolledElement &&
|
|
258
|
-
// @ts-ignore
|
|
259
|
-
window.__lastScrolledElementOriginalPosition !== undefined) {
|
|
260
|
-
// @ts-ignore
|
|
261
|
-
window.__lastScrolledElement.scrollTop = window.__lastScrolledElementOriginalPosition;
|
|
262
|
-
// Clean up
|
|
263
|
-
// @ts-ignore
|
|
264
|
-
delete window.__lastScrolledElement;
|
|
265
|
-
// @ts-ignore
|
|
266
|
-
delete window.__lastScrolledElementOriginalPosition;
|
|
267
|
-
}
|
|
268
|
-
});
|
|
269
|
-
return screenshot;
|
|
270
|
-
}
|
|
271
|
-
catch (error) {
|
|
272
|
-
if (PlaywrightUtils.isPageClosedError(error)) {
|
|
273
|
-
throw new PageClosedException_1.PageClosedException();
|
|
274
|
-
}
|
|
275
|
-
else {
|
|
276
|
-
throw error;
|
|
277
|
-
}
|
|
278
|
-
}
|
|
279
|
-
}
|
|
280
268
|
}
|
|
281
269
|
exports.PlaywrightUtils = PlaywrightUtils;
|
|
282
270
|
/**
|