核心:使用Google Puppeteer
- Puppeteer 是一个 Node 库,它提供了一个高级 API 来通过DevTools协议控制 Chromium 或 Chrome。Puppeteer 默认以headless模式运行,但是可以通过修改配置文件运行“有头”模式
说白了就是Puppeteer可以让代码在一个Chrome浏览器中执行,但是浏览器不需要人为打开
通过Puppeteer可以实现诸多浏览器操作,例如:
- 生成页面 PDF
- 抓取 SPA(单页应用)并生成预渲染内容(即“SSR”(服务器端渲染))
- 自动提交表单,进行 UI 测试,键盘输入等
- 创建一个时时更新的自动化测试环境。 使用最新的 JavaScript 和浏览器功能直接在最新版本的Chrome中执行测试
- 捕获网站的timeline trace,用来帮助分析性能问题
- 测试浏览器扩展
- 对页面进行脚本操作
但是,由于安全策略问题,Puppeteer不支持通过page.evaluate()方法通过node向浏览器注入脚本进行网络请求:包括post、get、websocket连接等等
所以如果要通过Node模拟真实浏览器环境对请求进行测试,可是自定义一个html文件,并在其中提前预置相应的测试脚本,然后在node环境下通过page.evaluate方法对预置脚本进行传参和调用,获取相应的信息
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Connect Test Enviroment</title>
<script>
// 测试WebSocket连接
const testWebSocket = async (url) => {
const ws = new WebSocket(url);
const connectedPromise = new Promise((resolve) => {
ws.addEventListener('open', () => {
resolve(true);
});
});
const errorPromise = new Promise((resolve) => {
ws.addEventListener('error', (error) => {
resolve(false);
});
});
const result = await Promise.race([connectedPromise, errorPromise]);
ws.close();
return result;
};
// 测试Post请求
const testPost = async (url) => {
try {
const response = await fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
});
const responseData = await response.json();
return responseData;
} catch (error) {
return error;
}
};
// 测试Get请求
const testGet = async (url) => {
const requestOptions = {
method: 'GET',
redirect: 'follow',
};
try {
const response = await fetch(url);
return response.status;
} catch (error) {
return error;
}
};
// 挂载至window对象
window.testWebSocket = testWebSocket;
window.testPost = testPost;
window.testGet = testGet;
</script>
</head>
<body></body>
</html>
在node环境中使用如下代码对脚本进行调用
const puppeteer = require('puppeteer');
// 测试websocket连接
const testWebScoket = async (url) => {
const browser = await puppeteer.launch({headless: 'new'});
const page = await browser.newPage();
await page.goto(/** html文件绝对路径,并以file://开头 **/);
const result = await page.evaluate(async (url) => {
const data = await testWebSocket(url);
return data;
}, url);
await browser.close();
return result;
};
// 测试post请求
const testPost = async (url) => {
const browser = await puppeteer.launch({headless: 'new'});
const page = await browser.newPage();
await page.goto(/** html文件绝对路径,并以file://开头 **/);
const result = await page.evaluate(async (url) => {
const data = await window.testPost(url);
return data;
}, url);
await browser.close();
return result;
};
// 测试get请求
const testGet = async (url) => {
const browser = await puppeteer.launch({headless: 'new'});
const page = await browser.newPage();
await page.goto(/** html文件绝对路径,并以file://开头 **/);
const result = await page.evaluate(async (url) => {
const data = await window.testGet(url);
return data;
}, url);
await browser.close();
return result;
};
(async () => {
console.log(await testWebScoket(/** 需要测试的websocket:wss://xxx:xxx **/));
console.log(await testPost(/** 需要测试的url字符串:https://xxx:8810/xxx/xxx/xxx **/));
console.log(await testGet(/** 需要测试的url字符串:https://xxx:8810/xxx/xxx/xxx **/));
})();
即可实现模拟真实浏览器环境
- 踩坑:报错
Puppeteer Evaluation failed: ReferenceError: __awaiter is not defined
通常情况下出现这个问题是因为采用了typescript,然后tsc将代码编译成了版本较低的js,此时需要对tsconfig.json进行修改
{
"compilerOptions": {
"target": "ES2017",
...
}
}