feat: MCP Browser v3.0 - 42 tools with stealth mode, multi-tab, PDF, cookies, storage, network logs, device emulation

This commit is contained in:
2026-03-27 11:58:52 -03:00
parent c1d716bb84
commit 13f7392555
48 changed files with 1269 additions and 714 deletions
+8
View File
@@ -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 };
};
+7
View File
@@ -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 };
};
+7
View File
@@ -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
View File
@@ -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 };
};
+7
View File
@@ -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 };
};
+8
View File
@@ -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 };
};
+12
View File
@@ -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 };
};
+8 -29
View File
@@ -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
View File
@@ -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
View File
@@ -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 };
};
+7
View File
@@ -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
View File
@@ -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 };
};
+11
View File
@@ -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
View File
@@ -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 };
};
+11
View File
@@ -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 };
};
+7
View File
@@ -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 };
};
+7
View File
@@ -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
View File
@@ -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 };
};
+7
View File
@@ -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 };
};
+7
View File
@@ -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 };
};
+7
View File
@@ -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 };
};
+7
View File
@@ -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 };
};
+8
View File
@@ -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 };
};
+16
View File
@@ -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 };
};
+10
View File
@@ -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
View File
@@ -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' };
};
+7
View File
@@ -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 };
};
+8
View 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 };
};
+7
View File
@@ -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
View File
@@ -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 };
};
+8
View 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 };
};
+8
View File
@@ -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 };
};
+8
View File
@@ -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 };
};
+10
View File
@@ -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 };
};
+8
View File
@@ -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
View File
@@ -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
View File
@@ -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 };
};
+4 -20
View File
@@ -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 };
};
+10
View File
@@ -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
View File
@@ -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 };
};
+9
View File
@@ -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 };
};
+8
View File
@@ -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 };
};
+4 -17
View File
@@ -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 };
};
+8
View File
@@ -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 };
};