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

如何在Nrwl Angular项目中实现Playwright覆盖率上报至SonarCloud

Nrwl Angular 项目中 Playwright 端到端测试覆盖率在 SonarCloud 展示的解决方案

核心思路

针对Nrwl单仓+Angular的场景,绕过babelrc配置限制,直接在Playwright测试流程中集成覆盖率收集逻辑,手动处理Angular编译后的source map转换,生成SonarCloud兼容的覆盖率格式。

具体配置步骤

1. 安装依赖包

npm install --save-dev @cypress/code-coverage istanbul-lib-coverage istanbul-lib-report istanbul-reports source-map ts-node

2. 配置Playwright覆盖率收集

在Playwright测试扩展文件(比如tests/global-setup.ts)中添加覆盖率收集逻辑:

import { test as base } from '@playwright/test';
import * as fs from 'fs';
import * as path from 'path';
import { createCoverageMap } from 'istanbul-lib-coverage';

export const test = base.extend({
  context: async ({ context }, use) => {
    // 初始化覆盖率全局变量
    await context.addInitScript(() => {
      (window as any).__coverage__ = {};
    });

    await use(context);

    // 测试结束后收集覆盖率数据
    const coverage = await context.evaluate(() => (window as any).__coverage__);
    if (coverage) {
      const coverageMap = createCoverageMap(coverage);
      const outputDir = path.join(process.cwd(), 'coverage-playwright');
      if (!fs.existsSync(outputDir)) {
        fs.mkdirSync(outputDir, { recursive: true });
      }
      fs.writeFileSync(
        path.join(outputDir, 'raw-coverage.json'),
        JSON.stringify(coverageMap.toJSON())
      );
    }
  },
});

export { expect } from '@playwright/test';

3. 编写Source Map转换脚本

创建scripts/process-coverage.ts,用于将编译后JS的覆盖率转换为原始TS文件的覆盖率:

import * as fs from 'fs';
import * as path from 'path';
import { createCoverageMap } from 'istanbul-lib-coverage';
import { SourceMapConsumer } from 'source-map';

async function transformCoverage() {
  const rawCoveragePath = path.join(process.cwd(), 'coverage-playwright', 'raw-coverage.json');
  const rawCoverage = JSON.parse(fs.readFileSync(rawCoveragePath, 'utf-8'));
  const coverageMap = createCoverageMap(rawCoverage);

  // 遍历所有JS文件,通过source map映射到TS源文件
  for (const [jsFilePath, jsCoverage] of Object.entries(coverageMap.data)) {
    if (!jsFilePath.endsWith('.js')) continue;

    const sourceMapPath = `${jsFilePath}.map`;
    if (!fs.existsSync(sourceMapPath)) continue;

    const sourceMapContent = JSON.parse(fs.readFileSync(sourceMapPath, 'utf-8'));
    const consumer = await new SourceMapConsumer(sourceMapContent);
    const tsSourcePath = path.join(consumer.sourceRoot || '', consumer.sources[0]);

    // 创建TS文件的覆盖率结构
    const tsCoverage = {
      ...jsCoverage,
      path: tsSourcePath,
      s: {}, // 存储转换后的语句覆盖率
      f: {}, // 存储转换后的函数覆盖率
      b: {}  // 存储转换后的分支覆盖率
    };

    // 转换语句覆盖率
    for (const [jsLine, count] of Object.entries(jsCoverage.s)) {
      const originalPos = consumer.originalPositionFor({
        line: parseInt(jsLine),
        column: 0
      });
      if (originalPos.line) {
        tsCoverage.s[originalPos.line] = count;
      }
    }

    // 移除JS文件覆盖率,添加TS文件覆盖率
    coverageMap.removeFile(jsFilePath);
    coverageMap.addFileCoverage(tsCoverage);
    await consumer.destroy();
  }

  // 生成SonarCloud支持的lcov格式报告
  const reporter = require('istanbul-reports').create('lcov');
  await reporter.execute({
    coverageMap,
    sourceFinder: (filePath: string) => fs.readFileSync(filePath, 'utf-8')
  });

  // 将lcov报告移动到指定目录
  const generatedLcovPath = path.join(process.cwd(), 'lcov.info');
  const targetLcovPath = path.join(process.cwd(), 'coverage-playwright', 'lcov.info');
  if (fs.existsSync(generatedLcovPath)) {
    fs.renameSync(generatedLcovPath, targetLcovPath);
  }
}

transformCoverage().catch(err => {
  console.error('覆盖率转换失败:', err);
  process.exit(1);
});

4. 配置Nrwl运行命令

在nx.json或项目的project.json中添加集成命令:

{
  "targets": {
    "e2e-with-coverage": {
      "executor": "nx:run-commands",
      "options": {
        "commands": [
          "npx playwright test --config=playwright.config.ts",
          "npx ts-node scripts/process-coverage.ts"
        ],
        "parallel": false
      }
    }
  }
}

5. SonarCloud配置

在项目根目录的sonar-project.properties中添加覆盖率路径:

sonar.typescript.lcov.reportPaths=coverage-playwright/lcov.info
sonar.exclusions=**/*.js,**/*.map,**/node_modules/**

执行流程

运行以下命令完成测试与覆盖率收集:

nx run your-project-name:e2e-with-coverage

之后将代码推送到仓库,SonarCloud会自动读取转换后的覆盖率数据并展示。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.16 13:50:04