/
githubmirror
/
grafana
Обзор
Документация
Войти
/
githubmirror
/
grafana
Код
Задачи
Вики
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
jest.config.codeowner.js
194 строки
6 KB
Paul Marbach
Frontend coverage: show regressed files and HTML report artifact on failure (#124305)
07 май 2026, 21:38
Не верифицирован
07 май 2026, 21:38
f6b3e47
Код
Авторство
О чём код?
const fs = require('fs'); const open = require('open').default; const path = require('path'); const baseConfig = require('./jest.config.js'); const { buildCodeownerDirectoryPath } = require('./scripts/codeowners-manifest/utils.js'); const CODEOWNERS_MANIFEST_FILENAMES_BY_TEAM_PATH = 'codeowners-manifest/filenames-by-team.json'; const codeownerName = process.env.CODEOWNER_NAME; if (!codeownerName) { console.error('ERROR: CODEOWNER_NAME environment variable is required'); process.exit(1); } const outputDir = path.join('./coverage/by-team', buildCodeownerDirectoryPath(codeownerName)); const COVERAGE_SUMMARY_OUTPUT_PATH = './coverage-summary.json'; const codeownersFilePath = path.join(__dirname, CODEOWNERS_MANIFEST_FILENAMES_BY_TEAM_PATH); if (!fs.existsSync(codeownersFilePath)) { console.error(`Codeowners file not found at ${codeownersFilePath} ...`); console.error('Please run: yarn codeowners-manifest first to generate the mapping file'); process.exit(1); } const codeownersData = JSON.parse(fs.readFileSync(codeownersFilePath, 'utf8')); const teamFiles = codeownersData[codeownerName] || []; if (teamFiles.length === 0) { console.error(`ERROR: No files found for team "${codeownerName}"`); console.error('Available teams:', Object.keys(codeownersData).join(', ')); process.exit(1); } const sourceFiles = teamFiles.filter((file) => { const ext = path.extname(file); return ( ['.ts', '.tsx', '.js', '.jsx'].includes(ext) && // exclude all tests and mocks !path.matchesGlob(file, '**/test/**/*') && !file.includes('.test.') && !file.includes('.spec.') && !path.matchesGlob(file, '**/__mocks__/**/*') && // and storybook stories !file.includes('.story.') && // and generated files !file.includes('.gen.ts') && // and type definitions !file.includes('.d.ts') && !file.endsWith('/types.ts') && // and anything in graveyard !path.matchesGlob(file, '**/graveyard/**/*') && // and scripts directory !file.startsWith('scripts/') && // and jest config files !path.matchesGlob(file, '**/jest.config*.js') && !file.endsWith('/module.tsx') ); }); const testFiles = teamFiles.filter((file) => { const ext = path.extname(file); return ['.ts', '.tsx', '.js', '.jsx'].includes(ext) && (file.includes('.test.') || file.includes('.spec.')); }); if (testFiles.length === 0) { console.log(`No test files found for team ${codeownerName}`); process.exit(0); } console.log( `🧪 Collecting coverage for ${sourceFiles.length} testable files and running ${testFiles.length} test files of ${teamFiles.length} files owned by ${codeownerName}.` ); module.exports = { ...baseConfig, collectCoverage: true, collectCoverageFrom: sourceFiles.map((file) => `<rootDir>/${file}`), coverageReporters: ['none'], coverageDirectory: '/tmp/jest-coverage-ignore', coverageProvider: 'babel', reporters: [ 'default', [ 'jest-monocart-coverage', { name: `Coverage Report - ${codeownerName} owned files`, outputDir: outputDir, reports: ['console-summary', 'json', 'lcov', ['html-spa', { subdir: 'html' }]], sourceFilter: (coveredFile) => sourceFiles.includes(coveredFile), all: { dir: ['./packages', './public'], filter: (filePath) => { const relativePath = filePath.replace(process.cwd() + '/', ''); return sourceFiles.includes(relativePath); }, }, cleanCache: true, onEnd: (coverageResults) => { const reportURL = `file://${path.resolve(outputDir)}/html/index.html`; console.log(`📄 Coverage report saved to ${reportURL}`); if (process.env.SHOULD_OPEN_COVERAGE_REPORT === 'true') { openCoverageReport(reportURL); } writeCoverageSummaryArtifact(coverageResults); // TODO: Emit coverage metrics https://github.com/grafana/grafana/issues/111208 }, }, ], ], testRegex: undefined, testMatch: testFiles.map((file) => `<rootDir>/${file}`), }; /** * @typedef {Object} CoverageMetric * @property {number} pct - Percentage value */ /** * @typedef {Object} CoverageSummary * @property {CoverageMetric} lines * @property {CoverageMetric} statements * @property {CoverageMetric} functions * @property {CoverageMetric} branches */ /** * @typedef {Object} CoverageResults * @property {CoverageSummary} summary */ /** * Writes coverage summary artifact for CI/CD consumption * @param {CoverageResults} coverageResults - Coverage results from jest-monocart-coverage */ function writeCoverageSummaryArtifact(coverageResults) { if (!coverageResults || !coverageResults.summary) { return; } const files = {}; if (coverageResults.files) { for (const file of coverageResults.files) { const relativePath = file.sourcePath.replace(process.cwd() + '/', ''); files[relativePath] = { lines: { pct: file.summary.lines.pct }, statements: { pct: file.summary.statements.pct }, functions: { pct: file.summary.functions.pct }, branches: { pct: file.summary.branches.pct }, }; } } const summary = { team: codeownerName, commit: process.env.GITHUB_SHA || 'unknown', timestamp: new Date().toISOString(), summary: { lines: { pct: coverageResults.summary.lines.pct }, statements: { pct: coverageResults.summary.statements.pct }, functions: { pct: coverageResults.summary.functions.pct }, branches: { pct: coverageResults.summary.branches.pct }, }, files, }; try { fs.writeFileSync(COVERAGE_SUMMARY_OUTPUT_PATH, JSON.stringify(summary, null, 2)); console.log(`📊 Coverage summary written to ${COVERAGE_SUMMARY_OUTPUT_PATH}`); } catch (err) { console.error(`Failed to write coverage summary: ${err}`); } } /** * Opens the coverage report in the default browser * @param {string} reportURL - File URL to the coverage report HTML */ async function openCoverageReport(reportURL) { try { await open(reportURL); } catch (err) { console.error(`Failed to open coverage report: ${err}`); } }