recorder
与Selenium IDE浏览器插件类似,可用于自动化测试及脚本生成(Puppeteer脚本);前者为Web自动化测试框架Selenium的配套浏览器插件支持Java、Python等测试脚本的生成。
下面介绍两种录制回放方式
导入json配置文件,通过Chrome DevTool工具回放
Puppeteer 是一个 Node library,提供了一套完整的通过 DevTools 协议操纵 Chrome 或 Chromium 的 API。Puppeteer 默认以 无头(headless) 的方式运行, 也可以使用 GUI 的方式运行 Chrome 和 Chromium。
熟悉爬虫或者 UI 自动化的同学可能会联想到 PhantomJS、CasperJS 或者 Selenium,而作为 Chrome DevTools 团队亲自出品和维护的 puppeteer 不管是在功能的完整性、稳定性、兼容性、安全性还是性能都将成为碾压其他工具的存在。
可参考 nodejs环境配置
npm install -g puppeteer
npm install -g url
npm install -g @puppeteer/replay
禁用headless模式(启用GUI模式)
const browser = await puppeteer.launch();
改为
const browser = await puppeteer.launch({
headless: false, // GUI模式
});
node test.js
recorder导出的puppeteer脚本如下
const puppeteer = require('puppeteer'); // v13.0.0 or later
(async () => {
const browser = await puppeteer.launch({
headless: false, // GUI模式
});
const page = await browser.newPage();
const timeout = 5000;
page.setDefaultTimeout(timeout);
{
const targetPage = page;
await targetPage.setViewport({"width":1752,"height":884})
}
{
const targetPage = page;
const promises = [];
promises.push(targetPage.waitForNavigation());
await targetPage.goto("https://www.baidu.com/");
await Promise.all(promises);
}
{
const targetPage = page;
await scrollIntoViewIfNeeded([["#kw"],["xpath///*[@id=\"kw\"]"]], targetPage, timeout);
const element = await waitForSelectors([["#kw"],["xpath///*[@id=\"kw\"]"]], targetPage, { timeout, visible: true });
const type = await element.evaluate(el => el.type);
if (["select-one"].includes(type)) {
await element.select("你好");
} else if (["textarea","text","url","tel","search","password","number","email"].includes(type)) {
await element.type("你好");
} else {
await element.focus();
await element.evaluate((el, value) => {
el.value = value;
el.dispatchEvent(new Event('input', { bubbles: true }));
el.dispatchEvent(new Event('change', { bubbles: true }));
}, "你好");
}
}
{
const targetPage = page;
await targetPage.keyboard.down("Enter");
}
{
const targetPage = page;
await targetPage.keyboard.up("Enter");
}
{
const targetPage = page;
await scrollIntoViewIfNeeded([["aria/你好,汉语词语,百度百科[role=\"link\"]"],["#\\31 > div > div > h3 > a"],["xpath///*[@id=\"1\"]/div/div/h3/a"],["text/你好(汉语词语) - 百度百科"]], targetPage, timeout);
const element = await waitForSelectors([["aria/你好,汉语词语,百度百科[role=\"link\"]"],["#\\31 > div > div > h3 > a"],["xpath///*[@id=\"1\"]/div/div/h3/a"],["text/你好(汉语词语) - 百度百科"]], targetPage, { timeout, visible: true });
await element.click({
offset: {
x: 99,
y: 14,
},
});
}
await browser.close();
async function waitForSelectors(selectors, frame, options) {
for (const selector of selectors) {
try {
return await waitForSelector(selector, frame, options);
} catch (err) {
console.error(err);
}
}
throw new Error('Could not find element for selectors: ' + JSON.stringify(selectors));
}
async function scrollIntoViewIfNeeded(selectors, frame, timeout) {
const element = await waitForSelectors(selectors, frame, { visible: false, timeout });
if (!element) {
throw new Error(
'The element could not be found.'
);
}
await waitForConnected(element, timeout);
const isInViewport = await element.isIntersectingViewport({threshold: 0});
if (isInViewport) {
return;
}
await element.evaluate(element => {
element.scrollIntoView({
block: 'center',
inline: 'center',
behavior: 'auto',
});
});
await waitForInViewport(element, timeout);
}
async function waitForConnected(element, timeout) {
await waitForFunction(async () => {
return await element.getProperty('isConnected');
}, timeout);
}
async function waitForInViewport(element, timeout) {
await waitForFunction(async () => {
return await element.isIntersectingViewport({threshold: 0});
}, timeout);
}
async function waitForSelector(selector, frame, options) {
if (!Array.isArray(selector)) {
selector = [selector];
}
if (!selector.length) {
throw new Error('Empty selector provided to waitForSelector');
}
let element = null;
for (let i = 0; i < selector.length; i++) {
const part = selector[i];
if (element) {
element = await element.waitForSelector(part, options);
} else {
element = await frame.waitForSelector(part, options);
}
if (!element) {
throw new Error('Could not find element: ' + selector.join('>>'));
}
if (i < selector.length - 1) {
element = (await element.evaluateHandle(el => el.shadowRoot ? el.shadowRoot : el)).asElement();
}
}
if (!element) {
throw new Error('Could not find element: ' + selector.join('|'));
}
return element;
}
async function waitForElement(step, frame, timeout) {
const count = step.count || 1;
const operator = step.operator || '>=';
const comp = {
'==': (a, b) => a === b,
'>=': (a, b) => a >= b,
'<=': (a, b) => a <= b,
};
const compFn = comp[operator];
await waitForFunction(async () => {
const elements = await querySelectorsAll(step.selectors, frame);
return compFn(elements.length, count);
}, timeout);
}
async function querySelectorsAll(selectors, frame) {
for (const selector of selectors) {
const result = await querySelectorAll(selector, frame);
if (result.length) {
return result;
}
}
return [];
}
async function querySelectorAll(selector, frame) {
if (!Array.isArray(selector)) {
selector = [selector];
}
if (!selector.length) {
throw new Error('Empty selector provided to querySelectorAll');
}
let elements = [];
for (let i = 0; i < selector.length; i++) {
const part = selector[i];
if (i === 0) {
elements = await frame.$$(part);
} else {
const tmpElements = elements;
elements = [];
for (const el of tmpElements) {
elements.push(...(await el.$$(part)));
}
}
if (elements.length === 0) {
return [];
}
if (i < selector.length - 1) {
const tmpElements = [];
for (const el of elements) {
const newEl = (await el.evaluateHandle(el => el.shadowRoot ? el.shadowRoot : el)).asElement();
if (newEl) {
tmpElements.push(newEl);
}
}
elements = tmpElements;
}
}
return elements;
}
async function waitForFunction(fn, timeout) {
let isActive = true;
const timeoutId = setTimeout(() => {
isActive = false;
}, timeout);
while (isActive) {
const result = await fn();
if (result) {
clearTimeout(timeoutId);
return;
}
await new Promise(resolve => setTimeout(resolve, 100));
}
throw new Error('Timed out');
}
})().catch(err => {
console.error(err);
process.exit(1);
});
参考:
chrome开发者工具-recorder-录制回放
Puppeteer 入门指引