You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何将Playwright长时运行测试的进度推送至前端?

Node.js SSE推送Playwright测试进度失效的解决办法

问题背景

我在Node.js后端用SSE定义了测试进度路由,前端已初始化对应URL。后端路由代码如下:

let clients: Array<any> = [];

router.get('/progress', (req, res) => {
    res.writeHead(200, {
      'Content-Type': 'text/event-stream',
      'Cache-Control': 'no-cache',
      Connection: 'keep-alive',
      'Access-Control-Allow-Origin': '*',
      'Access-Control-Allow-Headers': '*',
    });

    const data = `data: ${JSON.stringify('Starting the test')}\n\n`;

    res.write(data);

    const clientId = Date.now();

    const newClient = {
      id: clientId,
      res,
    };
    clients.push(newClient);
    req.on('close', () => {
      console.log(`${clientId} Connection closed`);
      clients = clients.filter((client) => client.id !== clientId);
    });
  });

前端点击按钮会触发后端运行Playwright长时测试(需遍历CSV文件检查),我想在测试执行过程中向前端推送进度,但把路由文件里的sendEventsToAll函数导入测试代码调用后完全没效果:

export function sendEventsToAll(data: any) {
  console.log('SSE ' + data);
  clients.forEach((client) => client.res.write(`data: ${JSON.stringify(data)}\n\n`));
}

最佳解决方案

问题根源

核心问题是模块引用导致的clients数组实例不统一:如果测试代码和路由文件是独立模块,直接导入的clients是新的引用,不是路由里实际维护的客户端列表,自然推不出消息。

解决步骤

1. 抽离SSE客户端管理为单例模块

新建一个独立的文件(比如sse-client-manager.ts),把客户端列表和操作方法封装在这里,确保所有模块共享同一个实例:

let clients: Array<{ id: number; res: any }> = [];

// 添加新客户端
export function addClient(client: { id: number; res: any }) {
  clients.push(client);
}

// 移除断开的客户端
export function removeClient(clientId: number) {
  clients = clients.filter(client => client.id !== clientId);
}

// 向所有客户端推送消息
export function sendEventsToAll(data: any) {
  console.log('SSE 推送内容:', data);
  clients.forEach(client => {
    // 增加错误处理,避免客户端断开后写入报错
    try {
      client.res.write(`data: ${JSON.stringify(data)}\n\n`);
      // 强制刷新响应,确保消息即时发送
      client.res.flush();
    } catch (err) {
      console.error(`推送失败给客户端${client.id}:`, err);
      removeClient(client.id);
    }
  });
}

2. 修改路由文件,使用单例模块的方法

把原来路由里的客户端操作替换为单例模块的方法:

import { addClient, removeClient, sendEventsToAll } from './sse-client-manager';

router.get('/progress', (req, res) => {
  res.writeHead(200, {
    'Content-Type': 'text/event-stream',
    'Cache-Control': 'no-cache',
    Connection: 'keep-alive',
    'Access-Control-Allow-Origin': '*',
    'Access-Control-Allow-Headers': '*',
  });

  // 发送初始化消息
  const initMsg = `data: ${JSON.stringify('Starting the test')}\n\n`;
  res.write(initMsg);
  res.flush();

  const clientId = Date.now();
  const newClient = { id: clientId, res };
  addClient(newClient);

  // 客户端断开时移除
  req.on('close', () => {
    console.log(`${clientId} 连接已关闭`);
    removeClient(clientId);
  });
});

3. 在Playwright测试代码中调用单例推送方法

现在测试代码里导入同一个单例模块的sendEventsToAll,就能正确推送给所有连接的客户端了:

import { sendEventsToAll } from './sse-client-manager';

// 示例Playwright测试逻辑
async function runPlaywrightCsvTest() {
  // 假设加载CSV数据
  const csvRows = await loadTestCsv();
  const total = csvRows.length;

  for (let index = 0; index < total; index++) {
    const row = csvRows[index];
    // 执行单条数据的Playwright检查
    await runSingleRowCheck(row);

    // 构造进度数据并推送
    const progress = {
      current: index + 1,
      total,
      message: `已完成第 ${index+1}/${total} 条数据检查`,
      timestamp: new Date().toLocaleTimeString()
    };
    sendEventsToAll(progress);

    // 可选:避免推送过于频繁,添加小延迟
    await new Promise(resolve => setTimeout(resolve, 150));
  }

  // 测试完成推送结束消息
  sendEventsToAll({
    completed: true,
    message: '所有CSV数据检查完成',
    timestamp: new Date().toLocaleTimeString()
  });
}

更优替代实现方式

1. Redis Pub/Sub(适合多实例部署)

如果你的后端是多实例集群,内存中的clients数组无法跨实例共享,这时候用Redis的发布订阅解耦:

  • SSE路由启动时订阅Redis的test-progress频道,收到消息后推送给当前客户端
  • Playwright测试代码向test-progress频道发布进度消息
    这样不管测试在哪个实例运行,所有SSE客户端都能收到进度。

2. WebSocket(适合双向交互场景)

如果后续需要前端控制测试(比如暂停、终止),WebSocket比SSE更灵活——它支持双向通信。但如果只是单向推送进度,SSE更轻量、实现更简单。

3. 任务队列+状态查询(适合超长时间任务)

如果测试任务耗时极长(比如几小时),可以把任务放入队列(比如BullMQ),前端通过轮询或者SSE订阅任务状态,后端任务执行时更新队列中的状态。这种方式更稳定,还能支持任务重启、断点续跑。

内容的提问来源于stack exchange,提问作者Zuhair

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.24 06:02:16