Extend dart-json report with suites summary and test case results

This commit is contained in:
Michal Dorner 2021-01-06 22:58:54 +01:00
parent e169ffb719
commit cc11acea10
No known key found for this signature in database
GPG key ID: 9EEE04B48DA36786
3 changed files with 116 additions and 27 deletions

View file

@ -3,7 +3,41 @@
exports[`dart-json tests matches report snapshot 1`] = `
Object {
"annotations": Array [],
"summary": "**6** tests were completed in **3.760s** with **1** passed, **1** skipped and **4** failed.",
"summary": "**6** tests were completed in **3.760s** with **1** passed, **1** skipped and **4** failed.
| Result | Suite | Tests | Time | Passed ✔️ | Failed ❌ | Skipped ✖️ |
| :---: | :--- | ---: | ---: | ---: | ---: | ---: |
| ❌ | [test\\\\main_test.dart](#ts-0-test-maintest-dart) | 4 | 74ms | 1 | 3 | 0 |
| ❌ | [test\\\\second_test.dart](#ts-1-test-secondtest-dart) | 2 | 51ms | 0 | 1 | 1 |
# Test Suites
## <a id=\\"user-content-ts-0-test-maintest-dart\\" href=\\"#ts-0-test-maintest-dart\\">test\\\\main_test.dart</a> ❌
### Test 1
| Result | Test | Time |
| :---: | :--- | ---: |
| ✔️ | Test 1 Passing test | 36ms |
### Test 1 Test 1.1
| Result | Test | Time |
| :---: | :--- | ---: |
| ❌ | Test 1 Test 1.1 Failing test | 20ms |
| ❌ | Test 1 Test 1.1 Exception in target unit | 6ms |
### Test 2
| Result | Test | Time |
| :---: | :--- | ---: |
| ❌ | Test 2 Exception in test | 12ms |
## <a id=\\"user-content-ts-1-test-secondtest-dart\\" href=\\"#ts-1-test-secondtest-dart\\">test\\\\second_test.dart</a> ❌
| Result | Test | Time |
| :---: | :--- | ---: |
| ❌ | Timeout test | 37ms |
| ✖️ | Skipped test | 14ms |
",
"title": "Dart tests ❌",
}
`;

6
package-lock.json generated
View file

@ -7867,9 +7867,9 @@
}
},
"lodash": {
"version": "4.17.19",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.19.tgz",
"integrity": "sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ==",
"version": "4.17.20",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz",
"integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==",
"dev": true
},
"lodash.memoize": {

View file

@ -1,6 +1,6 @@
import {ParseOptions, TestResult} from '../test-parser'
import {Icon} from '../../utils/markdown-utils'
import {Align, Icon, link, table} from '../../utils/markdown-utils'
import {slug} from '../../utils/slugger'
import {
ReportEvent,
@ -35,28 +35,27 @@ class TestRun {
class TestSuite {
constructor(readonly suite: Suite) {}
tests = new TestGroup()
groups: {[id: number]: TestGroup} = {}
get count(): number {
return this.tests.count + Object.values(this.groups).reduce((sum, g) => sum + g.count, 0)
return Object.values(this.groups).reduce((sum, g) => sum + g.count, 0)
}
get passed(): number {
return this.tests.passed + Object.values(this.groups).reduce((sum, g) => sum + g.passed, 0)
return Object.values(this.groups).reduce((sum, g) => sum + g.passed, 0)
}
get failed(): number {
return this.tests.failed + Object.values(this.groups).reduce((sum, g) => sum + g.failed, 0)
return Object.values(this.groups).reduce((sum, g) => sum + g.failed, 0)
}
get skipped(): number {
return this.tests.skipped + Object.values(this.groups).reduce((sum, g) => sum + g.skipped, 0)
return Object.values(this.groups).reduce((sum, g) => sum + g.skipped, 0)
}
get time(): number {
return this.tests.time + Object.values(this.groups).reduce((sum, g) => sum + g.time, 0)
return Object.values(this.groups).reduce((sum, g) => sum + g.time, 0)
}
}
class TestGroup {
constructor(readonly group?: Group) {}
tests: TestSuiteTest[] = []
constructor(readonly group: Group) {}
tests: TestCase[] = []
get count(): number {
return this.tests.length
}
@ -74,7 +73,7 @@ class TestGroup {
}
}
class TestSuiteTest {
class TestCase {
constructor(readonly testStart: TestStartEvent) {
this.groupId = testStart.test.groupIDs[testStart.test.groupIDs.length - 1]
}
@ -116,7 +115,7 @@ function getTestRun(content: string): TestRun {
let success = false
let totalTime = 0
const suites: {[id: number]: TestSuite} = {}
const tests: {[id: number]: TestSuiteTest} = {}
const tests: {[id: number]: TestCase} = {}
for (const evt of events) {
if (isSuiteEvent(evt)) {
@ -124,10 +123,9 @@ function getTestRun(content: string): TestRun {
} else if (isGroupEvent(evt)) {
suites[evt.group.suiteID].groups[evt.group.id] = new TestGroup(evt.group)
} else if (isTestStartEvent(evt) && evt.test.url !== null) {
const test: TestSuiteTest = new TestSuiteTest(evt)
const test: TestCase = new TestCase(evt)
const suite = suites[evt.test.suiteID]
const group =
evt.test.groupIDs.length === 0 ? suite.tests : suite.groups[evt.test.groupIDs[evt.test.groupIDs.length - 1]]
const group = suite.groups[evt.test.groupIDs[evt.test.groupIDs.length - 1]]
group.tests.push(test)
tests[evt.test.id] = test
} else if (isTestDoneEvent(evt) && !evt.hidden) {
@ -143,13 +141,70 @@ function getTestRun(content: string): TestRun {
return new TestRun(Object.values(suites), success, totalTime)
}
function getSummary(testRun: TestRun): string {
const tests = testRun.count
const time = `${(testRun.time / 1000).toFixed(3)}s`
const passed = testRun.passed
const skipped = testRun.skipped
const failed = testRun.failed
function getSummary(tr: TestRun): string {
const time = `${(tr.time / 1000).toFixed(3)}s`
const headingLine = `**${tr.count}** tests were completed in **${time}** with **${tr.passed}** passed, **${tr.skipped}** skipped and **${tr.failed}** failed.`
const headingLine = `**${tests}** tests were completed in **${time}** with **${passed}** passed, **${skipped}** skipped and **${failed}** failed.`
return `${headingLine}`
const suitesSummary = tr.suites.map((s, i) => {
const icon = s.failed === 0 ? Icon.success : Icon.fail
const tsTime = `${s.time}ms`
const tsName = s.suite.path
const tsAddr = makeSuiteSlug(i, tsName).link
const tsNameLink = link(tsName, tsAddr)
return [icon, tsNameLink, s.count, tsTime, s.passed, s.failed, s.skipped]
})
const summary = table(
['Result', 'Suite', 'Tests', 'Time', `Passed ${Icon.success}`, `Failed ${Icon.fail}`, `Skipped ${Icon.skip}`],
[Align.Center, Align.Left, Align.Right, Align.Right, Align.Right, Align.Right, Align.Right],
...suitesSummary
)
const suites = tr.suites.map((ts, i) => getSuiteSummary(ts, i)).join('\n')
const suitesSection = `# Test Suites\n\n${suites}`
return `${headingLine}\n${summary}\n${suitesSection}`
}
function getSuiteSummary(ts: TestSuite, index: number): string {
const icon = ts.failed === 0 ? Icon.success : Icon.fail
const groups = Object.values(ts.groups)
groups.sort((a, b) => (a.group.line ?? 0) - (b.group.line ?? 0))
const content = groups
.filter(grp => grp.count > 0)
.map(grp => {
const header = grp.group.name !== null ? `### ${grp.group.name}\n\n` : ''
grp.tests.sort((a, b) => (a.testStart.test.line ?? 0) - (b.testStart.test.line ?? 0))
const tests = table(
['Result', 'Test', 'Time'],
[Align.Center, Align.Left, Align.Right],
...grp.tests.map(tc => {
const name = tc.testStart.test.name
const time = `${tc.time}ms`
const result = getTestCaseIcon(tc)
return [result, name, time]
})
)
return `${header}${tests}\n`
})
.join('\n')
const tsName = ts.suite.path
const tsSlug = makeSuiteSlug(index, tsName)
const tsNameLink = `<a id="${tsSlug.id}" href="${tsSlug.link}">${tsName}</a>`
return `## ${tsNameLink} ${icon}\n\n${content}`
}
function makeSuiteSlug(index: number, name: string): {id: string; link: string} {
// use "ts-$index-" as prefix to avoid slug conflicts after escaping the paths
return slug(`ts-${index}-${name}`)
}
function getTestCaseIcon(test: TestCase): string {
if (test.isFailed) return Icon.fail
if (test.isSkipped) return Icon.skip
return Icon.success
}