n8n-nodes-nvk-browser 1.0.3 → 1.0.4
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.
|
@@ -22,7 +22,7 @@ exports.moveAndClickFields = [
|
|
|
22
22
|
type: 'string',
|
|
23
23
|
required: true,
|
|
24
24
|
default: '',
|
|
25
|
-
description: 'For Puppeteer: CSS selector, XPath (with >XPATH> prefix), or
|
|
25
|
+
description: 'For Puppeteer: CSS selector, XPath (with >XPATH> prefix), or Locator.race() code block. Examples: "textarea", ">XPATH>/html/body/div", or paste the Locator.race() block (e.g., "puppeteer.Locator.race([targetPage.locator(\'selector\')]).click()"). The node will automatically extract locators from the first .click() block. For Javascript: CSS selector only.',
|
|
26
26
|
typeOptions: {
|
|
27
27
|
rows: 4,
|
|
28
28
|
},
|
|
@@ -146,28 +146,66 @@ class MoveAndClick {
|
|
|
146
146
|
try {
|
|
147
147
|
// Parse selectors from the code pattern
|
|
148
148
|
const locators = [];
|
|
149
|
-
//
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
149
|
+
// Find Locator.race blocks - prioritize blocks that end with .click()
|
|
150
|
+
// First, try to find blocks with .click()
|
|
151
|
+
const clickBlocks = selector.match(/puppeteer\.Locator\.race\s*\(\s*\[([\s\S]*?)\]\s*\)[\s\S]*?\.click\s*\(/g);
|
|
152
|
+
let raceBlockContent = null;
|
|
153
|
+
if (clickBlocks && clickBlocks.length > 0) {
|
|
154
|
+
// Use the first click block
|
|
155
|
+
const firstClickBlock = clickBlocks[0];
|
|
156
|
+
const match = firstClickBlock.match(/puppeteer\.Locator\.race\s*\(\s*\[([\s\S]*?)\]\s*\)/);
|
|
157
|
+
if (match && match[1]) {
|
|
158
|
+
raceBlockContent = match[1];
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
else {
|
|
162
|
+
// Fallback: find any Locator.race block
|
|
163
|
+
const anyRaceBlockMatch = selector.match(/puppeteer\.Locator\.race\s*\(\s*\[([\s\S]*?)\]\s*\)/);
|
|
164
|
+
if (anyRaceBlockMatch && anyRaceBlockMatch[1]) {
|
|
165
|
+
raceBlockContent = anyRaceBlockMatch[1];
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
if (raceBlockContent) {
|
|
169
|
+
// Extract all locator calls from the race block
|
|
170
|
+
// Handle both single and double quotes, and handle escaped quotes
|
|
171
|
+
const locatorMatches = raceBlockContent.match(/(?:targetPage|page)\.locator\(['"]([^'"]*(?:\\.[^'"]*)*)['"]\)/g);
|
|
172
|
+
if (locatorMatches) {
|
|
173
|
+
locatorMatches.forEach(match => {
|
|
174
|
+
// Extract selector value, handling escaped quotes
|
|
175
|
+
const selectorMatch = match.match(/['"]([^'"]*(?:\\.[^'"]*)*)['"]/);
|
|
176
|
+
if (selectorMatch && selectorMatch[1]) {
|
|
177
|
+
const selectorValue = selectorMatch[1].replace(/\\(.)/g, '$1'); // Unescape
|
|
178
|
+
if (selectorValue && typeof page.locator === 'function') {
|
|
179
|
+
locators.push(page.locator(selectorValue));
|
|
180
|
+
}
|
|
158
181
|
}
|
|
159
|
-
}
|
|
160
|
-
}
|
|
182
|
+
});
|
|
183
|
+
}
|
|
161
184
|
}
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
185
|
+
if (locators.length === 0) {
|
|
186
|
+
// Fallback: Parse CSS selectors (lines with targetPage.locator('...') or page.locator('...'))
|
|
187
|
+
const cssMatches = selector.match(/(?:targetPage|page)\.locator\(['"]([^'"]+)['"]\)/g);
|
|
188
|
+
if (cssMatches) {
|
|
189
|
+
cssMatches.forEach(match => {
|
|
190
|
+
const cssSel = match.match(/['"]([^'"]+)['"]/)?.[1];
|
|
191
|
+
if (cssSel && !cssSel.startsWith('::-p-xpath')) {
|
|
192
|
+
// Use page.locator if available, otherwise fallback
|
|
193
|
+
if (typeof page.locator === 'function') {
|
|
194
|
+
locators.push(page.locator(cssSel));
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
// Parse XPath selectors (::-p-xpath(...))
|
|
200
|
+
const xpathMatches = selector.match(/::-p-xpath\(([^)]+)\)/g);
|
|
201
|
+
if (xpathMatches) {
|
|
202
|
+
xpathMatches.forEach(match => {
|
|
203
|
+
const xpath = match.match(/::-p-xpath\(([^)]+)\)/)?.[1];
|
|
204
|
+
if (xpath && typeof page.locator === 'function') {
|
|
205
|
+
locators.push(page.locator(`::-p-xpath(${xpath})`));
|
|
206
|
+
}
|
|
207
|
+
});
|
|
208
|
+
}
|
|
171
209
|
}
|
|
172
210
|
// Also check for >CSS> and >XPATH> prefixes
|
|
173
211
|
if (selector.includes('>CSS>') || selector.includes('>XPATH>')) {
|
|
@@ -213,11 +251,24 @@ class MoveAndClick {
|
|
|
213
251
|
if (waitForClick > 0) {
|
|
214
252
|
await page.waitForTimeout(waitForClick);
|
|
215
253
|
}
|
|
254
|
+
// Parse offset from code if present
|
|
255
|
+
let offset;
|
|
256
|
+
const offsetMatch = selector.match(/offset:\s*\{\s*x:\s*(\d+),\s*y:\s*(\d+)\s*\}/);
|
|
257
|
+
if (offsetMatch) {
|
|
258
|
+
offset = {
|
|
259
|
+
x: parseInt(offsetMatch[1], 10),
|
|
260
|
+
y: parseInt(offsetMatch[2], 10),
|
|
261
|
+
};
|
|
262
|
+
}
|
|
216
263
|
// Click with options
|
|
217
264
|
const clickOptions = {
|
|
218
265
|
button: button,
|
|
219
266
|
clickCount: clickCount,
|
|
220
267
|
};
|
|
268
|
+
// Add offset if found in code
|
|
269
|
+
if (offset) {
|
|
270
|
+
clickOptions.offset = offset;
|
|
271
|
+
}
|
|
221
272
|
if (typeof locator.click === 'function') {
|
|
222
273
|
await locator.click(clickOptions);
|
|
223
274
|
}
|
|
@@ -227,19 +278,13 @@ class MoveAndClick {
|
|
|
227
278
|
}
|
|
228
279
|
else {
|
|
229
280
|
// Fallback to simple selector parsing
|
|
230
|
-
throw new Error('Could not parse
|
|
281
|
+
throw new Error('Could not parse Locator.race() from code. Please ensure your code contains a valid puppeteer.Locator.race([...]) pattern with targetPage.locator() or page.locator() calls.');
|
|
231
282
|
}
|
|
232
283
|
}
|
|
233
284
|
catch (error) {
|
|
234
|
-
// If code parsing fails,
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
await page.waitForTimeout(waitForClick);
|
|
238
|
-
}
|
|
239
|
-
await page.click(selector, {
|
|
240
|
-
button: button,
|
|
241
|
-
clickCount: clickCount,
|
|
242
|
-
});
|
|
285
|
+
// If code parsing fails, provide helpful error message
|
|
286
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
287
|
+
throw new Error(`Failed to parse Puppeteer Locator code: ${errorMessage}. Please check that your selector field contains a valid Locator.race() pattern or a simple CSS/XPath selector.`);
|
|
243
288
|
}
|
|
244
289
|
}
|
|
245
290
|
else {
|