Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 27 additions & 5 deletions services/teamcity/teamcity-coverage.service.js
Original file line number Diff line number Diff line change
Expand Up @@ -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'

Expand All @@ -38,6 +50,10 @@ export default class TeamCityCoverage extends TeamCityBase {
name: 'server',
example: 'https://teamcity.jetbrains.com',
}),
queryParam({
name: 'metric',
example: 'statement',
}),
],
},
},
Expand All @@ -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
}

Expand All @@ -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(
Expand All @@ -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 })
}
}
27 changes: 27 additions & 0 deletions services/teamcity/teamcity-coverage.tester.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 =>
Expand All @@ -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 =>
Expand Down
Loading