diff --git a/services/teamcity/teamcity-coverage.service.js b/services/teamcity/teamcity-coverage.service.js index 726b49e61069a..86a707a1e40b3 100644 --- a/services/teamcity/teamcity-coverage.service.js +++ b/services/teamcity/teamcity-coverage.service.js @@ -17,8 +17,20 @@ const buildStatisticsSchema = Joi.object({ const queryParamSchema = Joi.object({ server: optionalUrl, + metric: Joi.string() + .valid('statement', 'line', 'block', 'method', 'class', 'branch') + .default('statement'), }).required() +const METRIC_CODE = { + statement: 'S', + line: 'L', + block: 'B', + method: 'M', + class: 'C', + branch: 'R', +} + export default class TeamCityCoverage extends TeamCityBase { static category = 'coverage' @@ -38,6 +50,10 @@ export default class TeamCityCoverage extends TeamCityBase { name: 'server', example: 'https://teamcity.jetbrains.com', }), + queryParam({ + name: 'metric', + example: 'statement', + }), ], }, }, @@ -54,13 +70,16 @@ export default class TeamCityCoverage extends TeamCityBase { } } - transform({ data }) { + transform({ data, metric }) { + const code = METRIC_CODE[metric] + const coveredKey = `CodeCoverageAbs${code}Covered` + const totalKey = `CodeCoverageAbs${code}Total` let covered, total for (const p of data.property) { - if (p.name === 'CodeCoverageAbsSCovered') { + if (p.name === coveredKey) { covered = +p.value - } else if (p.name === 'CodeCoverageAbsSTotal') { + } else if (p.name === totalKey) { total = +p.value } @@ -73,7 +92,10 @@ export default class TeamCityCoverage extends TeamCityBase { throw new InvalidResponse({ prettyMessage: 'no coverage data available' }) } - async handle({ buildId }, { server = 'https://teamcity.jetbrains.com' }) { + async handle( + { buildId }, + { server = 'https://teamcity.jetbrains.com', metric }, + ) { // JetBrains Docs: https://confluence.jetbrains.com/display/TCD18/REST+API#RESTAPI-Statistics const buildLocator = `buildType:(id:${buildId})` const apiPath = `app/rest/builds/${encodeURIComponent( @@ -84,7 +106,7 @@ export default class TeamCityCoverage extends TeamCityBase { schema: buildStatisticsSchema, }) - const { coverage } = this.transform({ data }) + const { coverage } = this.transform({ data, metric }) return this.constructor.render({ coverage }) } } diff --git a/services/teamcity/teamcity-coverage.tester.js b/services/teamcity/teamcity-coverage.tester.js index a6515d243d87e..964dd6d3fce6f 100644 --- a/services/teamcity/teamcity-coverage.tester.js +++ b/services/teamcity/teamcity-coverage.tester.js @@ -17,6 +17,15 @@ t.create('specified instance valid buildId') message: isIntegerPercentage, }) +t.create('Coverage (with metric)') + .get( + '/FileHelpersStable.json?server=https://teamcity.jetbrains.com&metric=line', + ) + .expectBadge({ + label: 'coverage', + message: isIntegerPercentage, + }) + t.create('no coverage data for build') .get('/bt234.json') .intercept(nock => @@ -27,6 +36,24 @@ t.create('no coverage data for build') ) .expectBadge({ label: 'coverage', message: 'no coverage data available' }) +t.create('mocked: coverage with line metric') + .get('/MyFakeBuild.json?server=https://teamcity.jetbrains.com&metric=line') + .intercept(nock => + nock('https://teamcity.jetbrains.com/app/rest/builds') + .get(`/${encodeURIComponent('buildType:(id:MyFakeBuild)')}/statistics`) + .query({ guest: 1 }) + .reply(200, { + property: [ + { name: 'CodeCoverageAbsLCovered', value: '85' }, + { name: 'CodeCoverageAbsLTotal', value: '100' }, + ], + }), + ) + .expectBadge({ + label: 'coverage', + message: '85%', + }) + t.create('zero lines covered') .get('/bt345.json') .intercept(nock =>