n8n-nodes-nvk-browser 1.0.5 → 1.0.6
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 paste the full Puppeteer code from Chrome Recorder (export as "Puppeteer"). The node will automatically
|
|
25
|
+
description: 'For Puppeteer: CSS selector, XPath (with >XPATH> prefix), or paste the full Puppeteer code from Chrome Recorder (export as "Puppeteer"). The node will automatically detect and execute .click() or .fill() actions from Locator.race() blocks. Examples: "textarea", ">XPATH>/html/body/div", or full code with puppeteer.Locator.race([...]).click()/.fill(). For Javascript: CSS selector only. Note: The node only executes click/fill actions, not goto() or other navigation actions.',
|
|
26
26
|
typeOptions: {
|
|
27
27
|
rows: 4,
|
|
28
28
|
},
|
|
@@ -99,6 +99,8 @@ class MoveAndClick {
|
|
|
99
99
|
const timeout = this.getNodeParameter('timeout', i) || 30000;
|
|
100
100
|
const tabIndex = this.getNodeParameter('tabIndex', i) || 0;
|
|
101
101
|
const autoStart = this.getNodeParameter('autoStart', i) || false;
|
|
102
|
+
// Track action type for return value (default to click)
|
|
103
|
+
let actionType = 'click';
|
|
102
104
|
let instance = browserManager.getInstance(profileId);
|
|
103
105
|
// Auto start profile nếu chưa chạy và option được bật
|
|
104
106
|
if (!instance && autoStart) {
|
|
@@ -146,20 +148,35 @@ class MoveAndClick {
|
|
|
146
148
|
try {
|
|
147
149
|
// Parse selectors from the code pattern
|
|
148
150
|
const locators = [];
|
|
149
|
-
// Find Locator.race blocks -
|
|
151
|
+
// Find Locator.race blocks - check for both .click() and .fill() actions
|
|
150
152
|
// Improved regex to handle multiline code from Chrome Recorder
|
|
151
|
-
// Match pattern: puppeteer.Locator.race([...]).setTimeout(...).click(...)
|
|
153
|
+
// Match pattern: puppeteer.Locator.race([...]).setTimeout(...).click(...) or .fill(...)
|
|
152
154
|
const clickBlockPattern = /puppeteer\.Locator\.race\s*\(\s*\[([\s\S]*?)\]\s*\)[\s\S]*?\.click\s*\(/;
|
|
155
|
+
const fillBlockPattern = /puppeteer\.Locator\.race\s*\(\s*\[([\s\S]*?)\]\s*\)[\s\S]*?\.fill\s*\(/;
|
|
153
156
|
const clickBlockMatch = selector.match(clickBlockPattern);
|
|
157
|
+
const fillBlockMatch = selector.match(fillBlockPattern);
|
|
154
158
|
let raceBlockContent = null;
|
|
155
|
-
|
|
159
|
+
let fillText = null;
|
|
160
|
+
// Prioritize fill if both exist (fill usually comes after click)
|
|
161
|
+
if (fillBlockMatch && fillBlockMatch[1]) {
|
|
162
|
+
raceBlockContent = fillBlockMatch[1];
|
|
163
|
+
actionType = 'fill';
|
|
164
|
+
// Extract fill text value
|
|
165
|
+
const fillTextMatch = selector.match(/\.fill\s*\(\s*['"]([^'"]*(?:\\.[^'"]*)*)['"]\s*\)/);
|
|
166
|
+
if (fillTextMatch && fillTextMatch[1]) {
|
|
167
|
+
fillText = fillTextMatch[1].replace(/\\(.)/g, '$1'); // Unescape
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
else if (clickBlockMatch && clickBlockMatch[1]) {
|
|
156
171
|
raceBlockContent = clickBlockMatch[1];
|
|
172
|
+
actionType = 'click';
|
|
157
173
|
}
|
|
158
174
|
else {
|
|
159
|
-
// Fallback: find any Locator.race block (even without .click())
|
|
175
|
+
// Fallback: find any Locator.race block (even without .click() or .fill())
|
|
160
176
|
const anyRaceBlockMatch = selector.match(/puppeteer\.Locator\.race\s*\(\s*\[([\s\S]*?)\]\s*\)/);
|
|
161
177
|
if (anyRaceBlockMatch && anyRaceBlockMatch[1]) {
|
|
162
178
|
raceBlockContent = anyRaceBlockMatch[1];
|
|
179
|
+
actionType = 'click'; // Default to click
|
|
163
180
|
}
|
|
164
181
|
}
|
|
165
182
|
if (raceBlockContent) {
|
|
@@ -244,30 +261,43 @@ class MoveAndClick {
|
|
|
244
261
|
if (waitForClick > 0) {
|
|
245
262
|
await page.waitForTimeout(waitForClick);
|
|
246
263
|
}
|
|
247
|
-
//
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
}
|
|
256
|
-
}
|
|
257
|
-
// Click with options
|
|
258
|
-
const clickOptions = {
|
|
259
|
-
button: button,
|
|
260
|
-
clickCount: clickCount,
|
|
261
|
-
};
|
|
262
|
-
// Add offset if found in code
|
|
263
|
-
if (offset) {
|
|
264
|
-
clickOptions.offset = offset;
|
|
265
|
-
}
|
|
266
|
-
if (typeof locator.click === 'function') {
|
|
267
|
-
await locator.click(clickOptions);
|
|
264
|
+
// Execute action based on detected type
|
|
265
|
+
if (actionType === 'fill' && fillText) {
|
|
266
|
+
// Fill action
|
|
267
|
+
if (typeof locator.fill === 'function') {
|
|
268
|
+
await locator.fill(fillText);
|
|
269
|
+
}
|
|
270
|
+
else {
|
|
271
|
+
throw new Error('Locator.fill is not available');
|
|
272
|
+
}
|
|
268
273
|
}
|
|
269
274
|
else {
|
|
270
|
-
|
|
275
|
+
// Click action (default)
|
|
276
|
+
// Parse offset from code if present (handle multiline)
|
|
277
|
+
let offset;
|
|
278
|
+
// Match offset in click options, handling multiline and whitespace
|
|
279
|
+
const offsetMatch = selector.match(/\.click\s*\(\s*\{[\s\S]*?offset:\s*\{[\s\S]*?x:\s*(\d+)[\s\S]*?y:\s*(\d+)[\s\S]*?\}[\s\S]*?\}/);
|
|
280
|
+
if (offsetMatch && offsetMatch[1] && offsetMatch[2]) {
|
|
281
|
+
offset = {
|
|
282
|
+
x: parseInt(offsetMatch[1], 10),
|
|
283
|
+
y: parseInt(offsetMatch[2], 10),
|
|
284
|
+
};
|
|
285
|
+
}
|
|
286
|
+
// Click with options
|
|
287
|
+
const clickOptions = {
|
|
288
|
+
button: button,
|
|
289
|
+
clickCount: clickCount,
|
|
290
|
+
};
|
|
291
|
+
// Add offset if found in code
|
|
292
|
+
if (offset) {
|
|
293
|
+
clickOptions.offset = offset;
|
|
294
|
+
}
|
|
295
|
+
if (typeof locator.click === 'function') {
|
|
296
|
+
await locator.click(clickOptions);
|
|
297
|
+
}
|
|
298
|
+
else {
|
|
299
|
+
throw new Error('Locator.click is not available');
|
|
300
|
+
}
|
|
271
301
|
}
|
|
272
302
|
}
|
|
273
303
|
else {
|
|
@@ -345,7 +375,8 @@ class MoveAndClick {
|
|
|
345
375
|
success: true,
|
|
346
376
|
selector,
|
|
347
377
|
method: clickMethod,
|
|
348
|
-
|
|
378
|
+
action: actionType || 'click',
|
|
379
|
+
message: actionType === 'fill' ? 'Fill performed successfully' : 'Click performed successfully',
|
|
349
380
|
},
|
|
350
381
|
});
|
|
351
382
|
}
|