feat: MCP Browser v3.0 - 42 tools with stealth mode, multi-tab, PDF, cookies, storage, network logs, device emulation
This commit is contained in:
@@ -0,0 +1,8 @@
|
||||
const browser = require('../browser');
|
||||
|
||||
module.exports = async ({ types }) => {
|
||||
if (!types || !Array.isArray(types)) throw new Error('Types array é obrigatório');
|
||||
await browser.start();
|
||||
const result = browser.blockResources(types);
|
||||
return { success: true, action: 'block_resources', ...result };
|
||||
};
|
||||
@@ -0,0 +1,7 @@
|
||||
const browser = require('../browser');
|
||||
|
||||
module.exports = async () => {
|
||||
await browser.start();
|
||||
const result = await browser.clearCookies();
|
||||
return { success: true, action: 'clear_cookies', ...result };
|
||||
};
|
||||
@@ -0,0 +1,7 @@
|
||||
const browser = require('../browser');
|
||||
|
||||
module.exports = async () => {
|
||||
await browser.start();
|
||||
await browser.clearStorage();
|
||||
return { success: true, action: 'clear_storage', cleared: true };
|
||||
};
|
||||
+4
-15
@@ -1,19 +1,8 @@
|
||||
const browser = require('../browser');
|
||||
|
||||
module.exports = async ({ selector }) => {
|
||||
if (!selector) {
|
||||
throw new Error('Selector é obrigatório');
|
||||
}
|
||||
|
||||
module.exports = async ({ selector, button = 'left', double = false }) => {
|
||||
if (!selector) throw new Error('Selector é obrigatório');
|
||||
await browser.start();
|
||||
const p = await browser.ensurePage();
|
||||
|
||||
await p.waitForSelector(selector, { state: 'visible', timeout: 10000 });
|
||||
await p.click(selector);
|
||||
|
||||
return {
|
||||
success: true,
|
||||
action: "click",
|
||||
selector
|
||||
};
|
||||
await browser.click(selector, { button, clickCount: double ? 2 : 1 });
|
||||
return { success: true, action: 'click', selector, button, double };
|
||||
};
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
const browser = require('../browser');
|
||||
|
||||
module.exports = async ({ index = null }) => {
|
||||
await browser.start();
|
||||
const result = await browser.closeTab(index);
|
||||
return { success: true, action: 'close_tab', ...result };
|
||||
};
|
||||
@@ -0,0 +1,8 @@
|
||||
const browser = require('../browser');
|
||||
|
||||
module.exports = async ({ device }) => {
|
||||
if (!device) throw new Error('Device é obrigatório');
|
||||
await browser.start();
|
||||
const result = await browser.emulateDevice(device);
|
||||
return { success: true, action: 'emulate_device', ...result };
|
||||
};
|
||||
@@ -0,0 +1,12 @@
|
||||
const browser = require('../browser');
|
||||
|
||||
module.exports = async ({ expression, selector = null }) => {
|
||||
if (!expression) throw new Error('Expression é obrigatória');
|
||||
await browser.start();
|
||||
if (selector) {
|
||||
const result = await browser.evaluateOnSelector(selector, expression);
|
||||
return { success: true, action: 'evaluate_js', selector, result };
|
||||
}
|
||||
const result = await browser.evaluateJS(expression);
|
||||
return { success: true, action: 'evaluate_js', result };
|
||||
};
|
||||
@@ -1,36 +1,15 @@
|
||||
const browser = require('../browser');
|
||||
|
||||
module.exports = async ({ selector, attributes = [] }) => {
|
||||
if (!selector) {
|
||||
throw new Error('Selector é obrigatório');
|
||||
}
|
||||
|
||||
if (!selector) throw new Error('Selector é obrigatório');
|
||||
await browser.start();
|
||||
const p = await browser.ensurePage();
|
||||
|
||||
const attrsStr = JSON.stringify(Array.isArray(attributes) ? attributes : []);
|
||||
|
||||
const elements = await p.evaluate((sel) => {
|
||||
const attrs = JSON.parse(sel.__attrs);
|
||||
const els = Array.from(document.querySelectorAll(sel.__sel));
|
||||
|
||||
return els.map(el => {
|
||||
const data = {
|
||||
text: (el.innerText || el.textContent || '').trim()
|
||||
};
|
||||
|
||||
for (let i = 0; i < attrs.length; i++) {
|
||||
data[attrs[i]] = el.getAttribute(attrs[i]);
|
||||
}
|
||||
|
||||
const attrs = Array.isArray(attributes) ? attributes : [];
|
||||
const elements = await browser.evaluateJS(({ sel, attrs: a }) => {
|
||||
return Array.from(document.querySelectorAll(sel)).map(el => {
|
||||
const data = { text: (el.innerText || el.textContent || '').trim() };
|
||||
a.forEach(attr => data[attr] = el.getAttribute(attr));
|
||||
return data;
|
||||
});
|
||||
}, { __sel: selector, __attrs: attrsStr });
|
||||
|
||||
return {
|
||||
success: true,
|
||||
action: "extract_elements",
|
||||
count: elements.length,
|
||||
elements
|
||||
};
|
||||
}, { sel: selector, attrs });
|
||||
return { success: true, action: 'extract_elements', count: elements.length, elements };
|
||||
};
|
||||
|
||||
+12
-43
@@ -1,62 +1,31 @@
|
||||
const browser = require('../browser');
|
||||
|
||||
module.exports = async ({ data }) => {
|
||||
if (!data) {
|
||||
throw new Error('Data é obrigatória');
|
||||
}
|
||||
|
||||
if (!data) throw new Error('Data é obrigatória');
|
||||
await browser.start();
|
||||
const p = await browser.ensurePage();
|
||||
|
||||
const result = await p.evaluate((formData) => {
|
||||
const result = await browser.evaluateJS((formData) => {
|
||||
const inputs = Array.from(document.querySelectorAll('input, textarea, select'));
|
||||
|
||||
let filled = [];
|
||||
|
||||
const filled = [];
|
||||
inputs.forEach(input => {
|
||||
const key = Object.keys(formData).find(k =>
|
||||
(input.name && input.name.toLowerCase().includes(k.toLowerCase())) ||
|
||||
(input.placeholder && input.placeholder.toLowerCase().includes(k.toLowerCase())) ||
|
||||
(input.id && input.id.toLowerCase().includes(k.toLowerCase())) ||
|
||||
(input.placeholder || '').toLowerCase().includes(k.toLowerCase()) ||
|
||||
(input.id || '').toLowerCase().includes(k.toLowerCase()) ||
|
||||
(input.getAttribute('aria-label') || '').toLowerCase().includes(k.toLowerCase())
|
||||
);
|
||||
|
||||
if (key) {
|
||||
const value = formData[key];
|
||||
|
||||
const nativeInputValueSetter = Object.getOwnPropertyDescriptor(
|
||||
window.HTMLInputElement.prototype, 'value'
|
||||
)?.set;
|
||||
|
||||
const nativeTextAreaValueSetter = Object.getOwnPropertyDescriptor(
|
||||
window.HTMLTextAreaElement.prototype, 'value'
|
||||
)?.set;
|
||||
|
||||
if (input.tagName === 'INPUT' && nativeInputValueSetter) {
|
||||
nativeInputValueSetter.call(input, value);
|
||||
} else if (input.tagName === 'TEXTAREA' && nativeTextAreaValueSetter) {
|
||||
nativeTextAreaValueSetter.call(input, value);
|
||||
} else {
|
||||
input.value = value;
|
||||
}
|
||||
|
||||
const nativeSetter = Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype, 'value')?.set;
|
||||
const nativeTextAreaSetter = Object.getOwnPropertyDescriptor(window.HTMLTextAreaElement.prototype, 'value')?.set;
|
||||
if (input.tagName === 'INPUT' && nativeSetter) nativeSetter.call(input, value);
|
||||
else if (input.tagName === 'TEXTAREA' && nativeTextAreaSetter) nativeTextAreaSetter.call(input, value);
|
||||
else input.value = value;
|
||||
input.dispatchEvent(new Event('input', { bubbles: true }));
|
||||
input.dispatchEvent(new Event('change', { bubbles: true }));
|
||||
|
||||
filled.push({
|
||||
field: input.name || input.id || input.placeholder || 'unknown',
|
||||
value: value
|
||||
});
|
||||
filled.push({ field: input.name || input.id || input.placeholder || 'unknown', value });
|
||||
}
|
||||
});
|
||||
|
||||
return filled;
|
||||
}, data);
|
||||
|
||||
return {
|
||||
success: true,
|
||||
action: "fill_form_auto",
|
||||
filled: result,
|
||||
count: result.length
|
||||
};
|
||||
return { success: true, action: 'fill_form_auto', filled: result, count: result.length };
|
||||
};
|
||||
|
||||
+5
-17
@@ -2,22 +2,10 @@ const browser = require('../browser');
|
||||
|
||||
module.exports = async () => {
|
||||
await browser.start();
|
||||
const p = await browser.ensurePage();
|
||||
|
||||
const buttons = await p.evaluate(() => {
|
||||
const elements = Array.from(document.querySelectorAll('button, input[type="submit"], input[type="button"], [role="button"], a.btn, a.button'));
|
||||
|
||||
return elements.map(el => ({
|
||||
text: (el.innerText || el.value || el.getAttribute('aria-label') || '').trim(),
|
||||
type: el.tagName.toLowerCase(),
|
||||
id: el.id || null
|
||||
})).filter(b => b.text !== '');
|
||||
const buttons = await browser.evaluateJS(() => {
|
||||
return Array.from(document.querySelectorAll('button, input[type="submit"], input[type="button"], [role="button"], a.btn, a.button'))
|
||||
.map(el => ({ text: (el.innerText || el.value || el.getAttribute('aria-label') || '').trim(), type: el.tagName.toLowerCase(), id: el.id || null }))
|
||||
.filter(b => b.text !== '');
|
||||
});
|
||||
|
||||
return {
|
||||
success: true,
|
||||
action: "get_buttons",
|
||||
count: buttons.length,
|
||||
buttons
|
||||
};
|
||||
return { success: true, action: 'get_buttons', count: buttons.length, buttons };
|
||||
};
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
const browser = require('../browser');
|
||||
|
||||
module.exports = async ({ urls = [] }) => {
|
||||
await browser.start();
|
||||
const cookies = await browser.getCookies(urls);
|
||||
return { success: true, action: 'get_cookies', count: cookies.length, cookies };
|
||||
};
|
||||
+9
-26
@@ -2,31 +2,14 @@ const browser = require('../browser');
|
||||
|
||||
module.exports = async () => {
|
||||
await browser.start();
|
||||
const p = await browser.ensurePage();
|
||||
|
||||
const forms = await p.evaluate(() => {
|
||||
const formElements = Array.from(document.querySelectorAll('form'));
|
||||
|
||||
return formElements.map(form => {
|
||||
const inputs = Array.from(form.querySelectorAll('input, textarea, select'));
|
||||
|
||||
return {
|
||||
action: form.action || null,
|
||||
method: form.method || 'GET',
|
||||
fields: inputs.map(input => ({
|
||||
name: input.name || null,
|
||||
type: input.type || input.tagName.toLowerCase(),
|
||||
placeholder: input.placeholder || '',
|
||||
id: input.id || null
|
||||
}))
|
||||
};
|
||||
});
|
||||
const forms = await browser.evaluateJS(() => {
|
||||
return Array.from(document.querySelectorAll('form')).map(form => ({
|
||||
action: form.action || null,
|
||||
method: form.method || 'GET',
|
||||
fields: Array.from(form.querySelectorAll('input, textarea, select')).map(input => ({
|
||||
name: input.name || null, type: input.type || input.tagName.toLowerCase(), placeholder: input.placeholder || '', id: input.id || null
|
||||
}))
|
||||
}));
|
||||
});
|
||||
|
||||
return {
|
||||
success: true,
|
||||
action: "get_forms",
|
||||
count: forms.length,
|
||||
forms
|
||||
};
|
||||
return { success: true, action: 'get_forms', count: forms.length, forms };
|
||||
};
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
const browser = require('../browser');
|
||||
|
||||
module.exports = async ({ selector = null }) => {
|
||||
await browser.start();
|
||||
if (selector) {
|
||||
const html = await browser.evaluateJS((sel) => document.querySelector(sel)?.outerHTML || null, selector);
|
||||
return { success: true, action: 'get_html', selector, html };
|
||||
}
|
||||
const html = await browser.content();
|
||||
return { success: true, action: 'get_html', html: html.substring(0, 50000) };
|
||||
};
|
||||
+4
-14
@@ -2,21 +2,11 @@ const browser = require('../browser');
|
||||
|
||||
module.exports = async ({ selector = 'a' }) => {
|
||||
await browser.start();
|
||||
const p = await browser.ensurePage();
|
||||
|
||||
const links = await p.evaluate((sel) => {
|
||||
const elements = Array.from(document.querySelectorAll(sel));
|
||||
|
||||
return elements.map(el => ({
|
||||
const links = await browser.evaluateJS((sel) => {
|
||||
return Array.from(document.querySelectorAll(sel)).map(el => ({
|
||||
text: (el.innerText || el.textContent || '').trim(),
|
||||
href: el.href || el.getAttribute('href') || ''
|
||||
})).filter(link => link.href && link.href !== '');
|
||||
})).filter(link => link.href);
|
||||
}, selector);
|
||||
|
||||
return {
|
||||
success: true,
|
||||
action: "get_links",
|
||||
count: links.length,
|
||||
links
|
||||
};
|
||||
return { success: true, action: 'get_links', count: links.length, links };
|
||||
};
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
const browser = require('../browser');
|
||||
|
||||
module.exports = async ({ type = null, url = null, method = null }) => {
|
||||
await browser.start();
|
||||
const filter = {};
|
||||
if (type) filter.type = type;
|
||||
if (url) filter.url = url;
|
||||
if (method) filter.method = method;
|
||||
const logs = Object.keys(filter).length > 0 ? await browser.getNetworkLogs(filter) : await browser.getNetworkLogs();
|
||||
return { success: true, action: 'get_network_logs', count: logs.length, logs };
|
||||
};
|
||||
@@ -0,0 +1,7 @@
|
||||
const browser = require('../browser');
|
||||
|
||||
module.exports = async () => {
|
||||
await browser.start();
|
||||
const metrics = await browser.getPerformanceMetrics();
|
||||
return { success: true, action: 'get_performance_metrics', metrics };
|
||||
};
|
||||
@@ -0,0 +1,7 @@
|
||||
const browser = require('../browser');
|
||||
|
||||
module.exports = async () => {
|
||||
await browser.start();
|
||||
const storage = await browser.getStorage();
|
||||
return { success: true, action: 'get_storage', ...storage };
|
||||
};
|
||||
+3
-13
@@ -2,20 +2,10 @@ const browser = require('../browser');
|
||||
|
||||
module.exports = async ({ selector = 'body' }) => {
|
||||
await browser.start();
|
||||
const p = await browser.ensurePage();
|
||||
|
||||
const text = await p.evaluate((sel) => {
|
||||
const text = await browser.evaluateJS((sel) => {
|
||||
const el = document.querySelector(sel);
|
||||
if (!el) return null;
|
||||
|
||||
const t = el.innerText || el.textContent || '';
|
||||
return t.trim() || null;
|
||||
return (el.innerText || el.textContent || '').trim();
|
||||
}, selector);
|
||||
|
||||
return {
|
||||
success: true,
|
||||
action: "get_text",
|
||||
selector,
|
||||
text
|
||||
};
|
||||
return { success: true, action: 'get_text', selector, text };
|
||||
};
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
const browser = require('../browser');
|
||||
|
||||
module.exports = async () => {
|
||||
await browser.start();
|
||||
const title = await browser.title();
|
||||
return { success: true, action: 'get_title', title };
|
||||
};
|
||||
@@ -0,0 +1,7 @@
|
||||
const browser = require('../browser');
|
||||
|
||||
module.exports = async () => {
|
||||
await browser.start();
|
||||
const url = await browser.currentUrl();
|
||||
return { success: true, action: 'get_url', url };
|
||||
};
|
||||
@@ -0,0 +1,7 @@
|
||||
const browser = require('../browser');
|
||||
|
||||
module.exports = async () => {
|
||||
await browser.start();
|
||||
const url = await browser.goBack();
|
||||
return { success: true, action: 'go_back', url };
|
||||
};
|
||||
@@ -0,0 +1,7 @@
|
||||
const browser = require('../browser');
|
||||
|
||||
module.exports = async () => {
|
||||
await browser.start();
|
||||
const url = await browser.goForward();
|
||||
return { success: true, action: 'go_forward', url };
|
||||
};
|
||||
@@ -0,0 +1,8 @@
|
||||
const browser = require('../browser');
|
||||
|
||||
module.exports = async ({ selector }) => {
|
||||
if (!selector) throw new Error('Selector é obrigatório');
|
||||
await browser.start();
|
||||
await browser.hover(selector);
|
||||
return { success: true, action: 'hover', selector };
|
||||
};
|
||||
@@ -0,0 +1,16 @@
|
||||
const browser = require('../browser');
|
||||
|
||||
module.exports = async () => {
|
||||
await browser.start();
|
||||
const tabs = browser.listTabs();
|
||||
const tabsWithTitle = await Promise.all(tabs.map(async (t, i) => {
|
||||
if (!t.isClosed) {
|
||||
try {
|
||||
const p = browser.tabs[i];
|
||||
t.title = await p.title();
|
||||
} catch (e) { t.title = null; }
|
||||
}
|
||||
return t;
|
||||
}));
|
||||
return { success: true, action: 'list_tabs', tabs: tabsWithTitle };
|
||||
};
|
||||
@@ -0,0 +1,10 @@
|
||||
const browser = require('../browser');
|
||||
|
||||
module.exports = async ({ url = null }) => {
|
||||
await browser.start();
|
||||
await browser.newTab(url);
|
||||
const p = browser.page;
|
||||
const finalUrl = url || p.url();
|
||||
const title = await p.title();
|
||||
return { success: true, action: 'new_tab', url: finalUrl, title };
|
||||
};
|
||||
+16
-48
@@ -1,56 +1,24 @@
|
||||
const browser = require('../browser');
|
||||
|
||||
module.exports = async ({ url }) => {
|
||||
if (!url) {
|
||||
throw new Error('URL é obrigatória');
|
||||
}
|
||||
|
||||
module.exports = async ({ url, waitUntil = 'domcontentloaded' }) => {
|
||||
if (!url) throw new Error('URL é obrigatória');
|
||||
await browser.start();
|
||||
const p = await browser.ensurePage();
|
||||
|
||||
const currentUrl = p.url();
|
||||
|
||||
|
||||
if (currentUrl && currentUrl !== 'about:blank') {
|
||||
if (currentUrl.includes(new URL(url).hostname)) {
|
||||
await p.goto(url, {
|
||||
waitUntil: 'domcontentloaded',
|
||||
timeout: 30000
|
||||
});
|
||||
await p.waitForTimeout(1500);
|
||||
|
||||
return {
|
||||
success: true,
|
||||
url,
|
||||
title: await p.title(),
|
||||
tab: 'reused'
|
||||
};
|
||||
}
|
||||
|
||||
const newPage = await browser.newTab();
|
||||
await newPage.goto(url, {
|
||||
waitUntil: 'domcontentloaded',
|
||||
timeout: 30000
|
||||
});
|
||||
await newPage.waitForTimeout(1500);
|
||||
|
||||
return {
|
||||
success: true,
|
||||
url,
|
||||
title: await newPage.title(),
|
||||
tab: 'new'
|
||||
};
|
||||
try {
|
||||
const targetUrl = new URL(url);
|
||||
if (currentUrl.includes(targetUrl.hostname)) {
|
||||
const result = await browser.goto(url, { waitUntil });
|
||||
return { success: true, ...result, tab: 'reused' };
|
||||
}
|
||||
} catch (e) {}
|
||||
await browser.newTab(url);
|
||||
const newPage = browser.page;
|
||||
return { success: true, url, title: await newPage.title(), tab: 'new' };
|
||||
}
|
||||
|
||||
await p.goto(url, {
|
||||
waitUntil: 'domcontentloaded',
|
||||
timeout: 30000
|
||||
});
|
||||
await p.waitForTimeout(1500);
|
||||
|
||||
return {
|
||||
success: true,
|
||||
url,
|
||||
title: await p.title(),
|
||||
tab: 'existing'
|
||||
};
|
||||
|
||||
const result = await browser.goto(url, { waitUntil });
|
||||
return { success: true, ...result, tab: 'existing' };
|
||||
};
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
const browser = require('../browser');
|
||||
|
||||
module.exports = async ({ path = null, format = 'A4', landscape = false }) => {
|
||||
await browser.start();
|
||||
const file = await browser.pdf({ path, format, landscape });
|
||||
return { success: true, action: 'pdf', file };
|
||||
};
|
||||
@@ -0,0 +1,8 @@
|
||||
const browser = require('../browser');
|
||||
|
||||
module.exports = async ({ key, selector = null }) => {
|
||||
if (!key) throw new Error('Key é obrigatória');
|
||||
await browser.start();
|
||||
await browser.pressKey(key, { selector });
|
||||
return { success: true, action: 'press_key', key, selector };
|
||||
};
|
||||
@@ -0,0 +1,7 @@
|
||||
const browser = require('../browser');
|
||||
|
||||
module.exports = async () => {
|
||||
await browser.start();
|
||||
const url = await browser.reload();
|
||||
return { success: true, action: 'reload', url };
|
||||
};
|
||||
+3
-12
@@ -1,16 +1,7 @@
|
||||
const browser = require('../browser');
|
||||
|
||||
module.exports = async ({ fullPage = true }) => {
|
||||
module.exports = async ({ fullPage = true, element = null, path: customPath = null }) => {
|
||||
await browser.start();
|
||||
const p = await browser.ensurePage();
|
||||
|
||||
const path = `screenshot-${Date.now()}.png`;
|
||||
|
||||
await p.screenshot({ path, fullPage });
|
||||
|
||||
return {
|
||||
success: true,
|
||||
action: "screenshot",
|
||||
file: path
|
||||
};
|
||||
const file = await browser.screenshot({ fullPage, path: customPath, element });
|
||||
return { success: true, action: 'screenshot', file };
|
||||
};
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
const browser = require('../browser');
|
||||
|
||||
module.exports = async ({ position }) => {
|
||||
if (!position) throw new Error('Position é obrigatória');
|
||||
await browser.start();
|
||||
await browser.scrollTo(position);
|
||||
return { success: true, action: 'scroll_to', position };
|
||||
};
|
||||
@@ -0,0 +1,8 @@
|
||||
const browser = require('../browser');
|
||||
|
||||
module.exports = async ({ selector, values }) => {
|
||||
if (!selector || !values) throw new Error('Selector e values são obrigatórios');
|
||||
await browser.start();
|
||||
const result = await browser.select(selector, values);
|
||||
return { success: true, action: 'select', selector, selected: result };
|
||||
};
|
||||
@@ -0,0 +1,8 @@
|
||||
const browser = require('../browser');
|
||||
|
||||
module.exports = async ({ cookies }) => {
|
||||
if (!cookies || !Array.isArray(cookies)) throw new Error('Cookies array é obrigatório');
|
||||
await browser.start();
|
||||
const result = await browser.setCookies(cookies);
|
||||
return { success: true, action: 'set_cookies', ...result };
|
||||
};
|
||||
@@ -0,0 +1,10 @@
|
||||
const browser = require('../browser');
|
||||
|
||||
module.exports = async ({ localStorage = {}, sessionStorage = {} }) => {
|
||||
await browser.start();
|
||||
const data = {};
|
||||
if (Object.keys(localStorage).length > 0) data.localStorage = localStorage;
|
||||
if (Object.keys(sessionStorage).length > 0) data.sessionStorage = sessionStorage;
|
||||
await browser.setStorage(data);
|
||||
return { success: true, action: 'set_storage', set: true };
|
||||
};
|
||||
@@ -0,0 +1,8 @@
|
||||
const browser = require('../browser');
|
||||
|
||||
module.exports = async ({ width, height }) => {
|
||||
if (width === undefined || height === undefined) throw new Error('Width e height são obrigatórios');
|
||||
await browser.start();
|
||||
await browser.setViewport(width, height);
|
||||
return { success: true, action: 'set_viewport', width, height };
|
||||
};
|
||||
+9
-36
@@ -1,54 +1,27 @@
|
||||
const browser = require('../browser');
|
||||
|
||||
module.exports = async ({ text }) => {
|
||||
if (!text) {
|
||||
throw new Error('Texto é obrigatório');
|
||||
}
|
||||
|
||||
if (!text) throw new Error('Texto é obrigatório');
|
||||
await browser.start();
|
||||
const p = await browser.ensurePage();
|
||||
|
||||
|
||||
const clicked = await p.evaluate((targetText) => {
|
||||
const elements = Array.from(document.querySelectorAll(
|
||||
'a, button, input[type="submit"], input[type="button"], [role="button"], [onclick]'
|
||||
));
|
||||
|
||||
const elements = Array.from(document.querySelectorAll('a, button, input[type="submit"], input[type="button"], [role="button"], [onclick]'));
|
||||
const el = elements.find(e => {
|
||||
const txt = (e.innerText || e.textContent || e.value || e.getAttribute('aria-label') || '').trim().toLowerCase();
|
||||
return txt.includes(targetText.toLowerCase());
|
||||
});
|
||||
|
||||
if (el) {
|
||||
el.scrollIntoView({ behavior: 'smooth', block: 'center' });
|
||||
el.click();
|
||||
|
||||
if (el.tagName === 'A' && el.href) {
|
||||
return { clicked: true, href: el.href };
|
||||
}
|
||||
|
||||
return { clicked: true, href: null };
|
||||
return { clicked: true, href: el.tagName === 'A' ? el.href : null };
|
||||
}
|
||||
|
||||
return { clicked: false, href: null };
|
||||
}, text);
|
||||
|
||||
if (!clicked.clicked) {
|
||||
throw new Error(`Elemento não encontrado pelo texto: "${text}"`);
|
||||
}
|
||||
|
||||
if (clicked.href) {
|
||||
try {
|
||||
await p.waitForNavigation({ timeout: 5000, waitUntil: 'domcontentloaded' });
|
||||
} catch (e) {
|
||||
}
|
||||
} else {
|
||||
await p.waitForTimeout(1000);
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
action: "smart_click",
|
||||
text,
|
||||
navigatedTo: clicked.href || null
|
||||
};
|
||||
if (!clicked.clicked) throw new Error(`Elemento não encontrado: "${text}"`);
|
||||
if (clicked.href) { try { await p.waitForNavigation({ timeout: 5000, waitUntil: 'domcontentloaded' }); } catch (e) {} }
|
||||
else await p.waitForTimeout(1000);
|
||||
|
||||
return { success: true, action: 'smart_click', text, navigatedTo: clicked.href };
|
||||
};
|
||||
|
||||
+16
-67
@@ -1,84 +1,33 @@
|
||||
const browser = require('../browser');
|
||||
|
||||
module.exports = async ({ label, text }) => {
|
||||
if (!label || !text) {
|
||||
throw new Error('Label e text são obrigatórios');
|
||||
}
|
||||
|
||||
if (!label || !text) throw new Error('Label e text são obrigatórios');
|
||||
await browser.start();
|
||||
const p = await browser.ensurePage();
|
||||
|
||||
const success = await p.evaluate(({ labelText, inputText }) => {
|
||||
function setInputValue(input, value) {
|
||||
const nativeSetter = Object.getOwnPropertyDescriptor(
|
||||
window.HTMLInputElement.prototype, 'value'
|
||||
)?.set;
|
||||
|
||||
const nativeTextAreaSetter = Object.getOwnPropertyDescriptor(
|
||||
window.HTMLTextAreaElement.prototype, 'value'
|
||||
)?.set;
|
||||
|
||||
if (input.tagName === 'INPUT' && nativeSetter) {
|
||||
nativeSetter.call(input, value);
|
||||
} else if (input.tagName === 'TEXTAREA' && nativeTextAreaSetter) {
|
||||
nativeTextAreaSetter.call(input, value);
|
||||
} else {
|
||||
input.value = value;
|
||||
}
|
||||
|
||||
const success = await browser.evaluateJS(({ labelText, inputText }) => {
|
||||
const setInput = (input, value) => {
|
||||
const nativeSetter = Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype, 'value')?.set;
|
||||
const nativeTextAreaSetter = Object.getOwnPropertyDescriptor(window.HTMLTextAreaElement.prototype, 'value')?.set;
|
||||
if (input.tagName === 'INPUT' && nativeSetter) nativeSetter.call(input, value);
|
||||
else if (input.tagName === 'TEXTAREA' && nativeTextAreaSetter) nativeTextAreaSetter.call(input, value);
|
||||
else input.value = value;
|
||||
input.dispatchEvent(new Event('input', { bubbles: true }));
|
||||
input.dispatchEvent(new Event('change', { bubbles: true }));
|
||||
}
|
||||
|
||||
const labels = Array.from(document.querySelectorAll('label'));
|
||||
|
||||
const foundLabel = labels.find(l =>
|
||||
(l.innerText || l.textContent || '').toLowerCase().includes(labelText.toLowerCase())
|
||||
);
|
||||
|
||||
};
|
||||
const foundLabel = Array.from(document.querySelectorAll('label')).find(l => (l.innerText || l.textContent || '').toLowerCase().includes(labelText.toLowerCase()));
|
||||
if (foundLabel) {
|
||||
const forAttr = foundLabel.getAttribute('for');
|
||||
|
||||
if (forAttr) {
|
||||
const input = document.getElementById(forAttr);
|
||||
if (input) {
|
||||
setInputValue(input, inputText);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
const input = foundLabel.querySelector('input, textarea');
|
||||
if (input) {
|
||||
setInputValue(input, inputText);
|
||||
return true;
|
||||
}
|
||||
if (forAttr) { const input = document.getElementById(forAttr); if (input) { setInput(input, inputText); return true; } }
|
||||
const input = foundLabel.querySelector('input, textarea'); if (input) { setInput(input, inputText); return true; }
|
||||
}
|
||||
|
||||
const inputs = Array.from(document.querySelectorAll('input, textarea'));
|
||||
|
||||
const foundInput = inputs.find(i =>
|
||||
const foundInput = Array.from(document.querySelectorAll('input, textarea')).find(i =>
|
||||
(i.placeholder || '').toLowerCase().includes(labelText.toLowerCase()) ||
|
||||
(i.name || '').toLowerCase().includes(labelText.toLowerCase()) ||
|
||||
(i.id || '').toLowerCase().includes(labelText.toLowerCase()) ||
|
||||
(i.getAttribute('aria-label') || '').toLowerCase().includes(labelText.toLowerCase())
|
||||
);
|
||||
|
||||
if (foundInput) {
|
||||
setInputValue(foundInput, inputText);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (foundInput) { setInput(foundInput, inputText); return true; }
|
||||
return false;
|
||||
}, { labelText: label, inputText: text });
|
||||
|
||||
if (!success) {
|
||||
throw new Error('Campo não encontrado');
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
action: "smart_type",
|
||||
label,
|
||||
text
|
||||
};
|
||||
if (!success) throw new Error('Campo não encontrado');
|
||||
return { success: true, action: 'smart_type', label, text };
|
||||
};
|
||||
|
||||
@@ -2,25 +2,9 @@ const browser = require('../browser');
|
||||
|
||||
module.exports = async ({ timeout = 15000 }) => {
|
||||
await browser.start();
|
||||
const oldUrl = await browser.currentUrl();
|
||||
const p = await browser.ensurePage();
|
||||
|
||||
const oldUrl = p.url();
|
||||
|
||||
try {
|
||||
await Promise.race([
|
||||
p.waitForNavigation({ timeout, waitUntil: 'domcontentloaded' }),
|
||||
p.waitForLoadState('domcontentloaded', { timeout })
|
||||
]);
|
||||
} catch (e) {
|
||||
}
|
||||
|
||||
const newUrl = p.url();
|
||||
|
||||
return {
|
||||
success: true,
|
||||
action: "smart_wait_navigation",
|
||||
oldUrl,
|
||||
newUrl,
|
||||
changed: oldUrl !== newUrl
|
||||
};
|
||||
try { await Promise.race([p.waitForNavigation({ timeout, waitUntil: 'domcontentloaded' }), p.waitForLoadState('domcontentloaded', { timeout })]); } catch (e) {}
|
||||
const newUrl = await browser.currentUrl();
|
||||
return { success: true, action: 'smart_wait_navigation', oldUrl, newUrl, changed: oldUrl !== newUrl };
|
||||
};
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
const browser = require('../browser');
|
||||
|
||||
module.exports = async ({ index }) => {
|
||||
if (index === undefined) throw new Error('Index é obrigatório');
|
||||
await browser.start();
|
||||
await browser.switchTab(index);
|
||||
const url = await browser.currentUrl();
|
||||
const title = await browser.title();
|
||||
return { success: true, action: 'switch_tab', index, url, title };
|
||||
};
|
||||
+4
-16
@@ -1,20 +1,8 @@
|
||||
const browser = require('../browser');
|
||||
|
||||
module.exports = async ({ selector, text }) => {
|
||||
if (!selector || !text) {
|
||||
throw new Error('Selector e text são obrigatórios');
|
||||
}
|
||||
|
||||
module.exports = async ({ selector, text, clear = false, slowly = false }) => {
|
||||
if (!selector || !text) throw new Error('Selector e text são obrigatórios');
|
||||
await browser.start();
|
||||
const p = await browser.ensurePage();
|
||||
|
||||
await p.waitForSelector(selector, { state: 'visible', timeout: 10000 });
|
||||
await p.fill(selector, text);
|
||||
|
||||
return {
|
||||
success: true,
|
||||
action: "type",
|
||||
selector,
|
||||
text
|
||||
};
|
||||
await browser.type(selector, text, { clear, slowly });
|
||||
return { success: true, action: 'type', selector, text, clear, slowly };
|
||||
};
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
const browser = require('../browser');
|
||||
|
||||
module.exports = async ({ selector, filePaths }) => {
|
||||
if (!selector) throw new Error('Selector é obrigatório');
|
||||
if (!filePaths) throw new Error('FilePaths é obrigatório');
|
||||
await browser.start();
|
||||
const result = await browser.uploadFile(selector, filePaths);
|
||||
return { success: true, action: 'upload_file', ...result };
|
||||
};
|
||||
@@ -0,0 +1,8 @@
|
||||
const browser = require('../browser');
|
||||
|
||||
module.exports = async ({ ms }) => {
|
||||
if (!ms || typeof ms !== 'number') throw new Error('MS é obrigatório e deve ser número');
|
||||
await browser.start();
|
||||
await browser.waitForTimeout(ms);
|
||||
return { success: true, action: 'wait', ms };
|
||||
};
|
||||
@@ -1,22 +1,9 @@
|
||||
const browser = require('../browser');
|
||||
|
||||
module.exports = async ({ selector, timeout = 15000 }) => {
|
||||
if (!selector) {
|
||||
throw new Error('Selector é obrigatório');
|
||||
}
|
||||
|
||||
module.exports = async ({ selector, timeout = 15000, state = 'attached' }) => {
|
||||
if (!selector) throw new Error('Selector é obrigatório');
|
||||
await browser.start();
|
||||
const p = await browser.ensurePage();
|
||||
|
||||
const el = await p.waitForSelector(selector, {
|
||||
timeout,
|
||||
state: 'attached'
|
||||
});
|
||||
|
||||
return {
|
||||
success: true,
|
||||
action: "wait_for_selector",
|
||||
selector,
|
||||
found: !!el
|
||||
};
|
||||
const el = await p.waitForSelector(selector, { timeout, state });
|
||||
return { success: true, action: 'wait_for_selector', selector, found: !!el };
|
||||
};
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
const browser = require('../browser');
|
||||
|
||||
module.exports = async ({ pattern, timeout = 30000 }) => {
|
||||
if (!pattern) throw new Error('Pattern é obrigatório');
|
||||
await browser.start();
|
||||
const url = await browser.waitForURL(pattern, { timeout });
|
||||
return { success: true, action: 'wait_for_url', pattern, url };
|
||||
};
|
||||
Reference in New Issue
Block a user