上一篇博客从源码层面分析了playwright vscode插件实现原理,在上一篇博客中提到,backend服务是一个websocket服务。这遍博客将介绍如何封装一个websocket服务,通过发送消息来执行playwright测试。
初始化项目
第一步是初始化项目和安装必要的依赖,这里需要安装playwright, @playwright/test和ws两个依赖包。后面启动执行测试的cli.js文件就是来源于@plawright/test这个包。
mkdir playwright-ws-server
cd playwright-ws-server
npm init -y
npm install ws playwright @playwright/test
实现websocket服务
下面的代码中,通过new WebSocket.Server启动一个websocket服务,监听在8085端口。当监听到message.command==“runtest”时,通过spawn启动node进程,执行playwright的测试文件。并把测试结果通过消息发送出去。
const WebSocket = require('ws');
const { spawn } = require('child_process');
const path = require('path');// WebSocket server setup
const wss = new WebSocket.Server({ port: 8085 });// Event listener for new connections
wss.on('connection', ws => {console.log('Client connected');// Event listener for incoming messagesws.on('message', message => {console.log(`Received message: ${message}`);// Parse the received messagelet command;try {command = JSON.parse(message);} catch (e) {console.error('Invalid JSON:', e);ws.send(JSON.stringify({ error: 'Invalid JSON' }));return;}// Check if the command is "runtest"if (command.action === 'runtest') {const testFilePath = command.testFilePath;const options = command.options;// Construct the Playwright test commandconst nodePath = '/opt/homebrew/bin/node';const cliPath = path.resolve('./node_modules/@playwright/test/cli.js');const configPath = 'playwright.config.js';const args = [cliPath,'test','-c', configPath,testFilePath,`--headed`,`--project=${options.project}`,`--repeat-each=${options.repeatEach}`,`--retries=${options.retries}`,`--timeout=${options.timeout}`,`--workers=${options.workers}`];console.log('Executing command:', `${nodePath} ${args.join(' ')}`);// Spawn the Playwright test processconst testProcess = spawn(nodePath, args, { stdio: 'pipe' });// Capture stdout and stderrtestProcess.stdout.on('data', data => {console.log(`stdout: ${data}`);ws.send(JSON.stringify({ output: data.toString() }));});testProcess.stderr.on('data', data => {console.error(`stderr: ${data}`);ws.send(JSON.stringify({ error: data.toString() }));});// Handle process exittestProcess.on('close', code => {console.log(`Child process exited with code ${code}`);ws.send(JSON.stringify({ exitCode: code }));});} else {ws.send(JSON.stringify({ error: 'Unknown action' }));}});// Event listener for connection closews.on('close', () => {console.log('Client disconnected');});
});console.log('WebSocket server is running on ws://localhost:8085');
node server.js命令启动上面的websocket服务,再安装wscat工具,通过wscat工具给服务发送消息,发送后即可看到测试脚本执行结果。
//安装wscat
npm install -g wscat//连接ws的命令
wscat -c ws://localhost:8080//发送的message
{"action": "runtest","testFilePath": "/Users/taoli/study/playwrightDemo/tests/test-1.spec.ts:3","options": {"headed": true,"project": "chromium","repeatEach": 1,"retries": 0,"timeout": 0,"workers": 1}
}
也可以编写client.js文件来发送消息执行测试。下面是client.js的代码。
const WebSocket = require('ws');const ws = new WebSocket('ws://localhost:8085');ws.on('open', () => {const message = {action: 'runtest',testFilePath: '/Users/taoli/study/playwrightDemo/tests/test-1.spec.ts:3',configPath: 'playwright.config.js',options: { "project": "chromium", "repeatEach": 1, "retries": 0, "timeout": 0, "workers": 1 }};ws.send(JSON.stringify(message));
});ws.on('message', data => {console.log('Received:', data);
});
启动server,在另外一个terminal中执行client.js文件,可以看到websocket接受到的message,实际执行的命令如下图红框所示。stdout部分打印了测试执行结果。在执行测试过程中,因为设置--headed模式,所以可以看到开启了浏览器,在浏览器中打开被测应用进行测试。
spawn提供哪些功能
在前面的介绍中,多次提到了spawn,那么spawn有哪些功能呢?spawn 是 Node.js 的 child_process 模块提供的一个方法,用于创建一个新的进程来执行给定的命令。它与 exec 类似,但 spawn 更适合处理长时间运行的进程,并且可以与子进程进行流式交互。
spawn可以启动任何可执行文件,包括 Node.js 程序。这是因为 spawn 本质上是在操作系统中运行一个新进程,将命令和参数传递给这个新进程,即可完成执行操作。
除了执行 Node.js 程序,spawn 可以用来执行几乎任何可执行文件或命令,包括但不限于:
Shell 命令:执行操作系统的命令,如 ls, grep, ping 等。
其他脚本语言:执行 Python, Ruby, Perl 等脚本语言编写的脚本。
系统服务:启动、停止或重启系统服务。
编译工具:运行编译器如 gcc, javac 等。
应用程序:启动其他应用程序或工具,如 git, docker, npm 等。
可以看到spawn功能非常强大,例如python编写的程序,也可以通过spawn来运行,当然,这个运行也可以封装到一个websocket服务中,通过发送和监听消息来执行。