Skip to content

Commit

Permalink
Include failure or error message on dashboard
Browse files Browse the repository at this point in the history
  • Loading branch information
gstokkink committed Jan 22, 2024
1 parent a62f3b2 commit 2becfc2
Show file tree
Hide file tree
Showing 5 changed files with 147 additions and 50 deletions.
12 changes: 10 additions & 2 deletions src/dashboard.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,10 +55,18 @@ export function dashboardResults(result: TestResult, show: number): string {
table += escapeHTML(testcase.description)
}

if (testcase.details) {
if (testcase.message || testcase.details) {
table += "<br/>\n"
table += "<pre><code>"
table += escapeHTML(testcase.details)

if (testcase.message) {
table += escapeHTML(testcase.message)
}

if (testcase.details) {
table += escapeHTML(testcase.details)
}

table += "</code></pre>"
}

Expand Down
12 changes: 10 additions & 2 deletions src/test_parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ export interface TestCase {
status: TestStatus
name?: string
description?: string
message?: string
details?: string
duration?: string
}
Expand Down Expand Up @@ -235,15 +236,21 @@ async function parseJunitXml(xml: any): Promise<TestResult> {
const name = testcase.$.name
const duration = testcase.$.time

let failure_or_error
let message: string | undefined = undefined
let details: string | undefined = undefined

if (testcase.skipped) {
status = TestStatus.Skip

counts.skipped++
} else if (testcase.failure || testcase.error) {
} else if (failure_or_error = testcase.failure || testcase.error) {
status = TestStatus.Fail
details = (testcase.failure || testcase.error)[0]._

const element = failure_or_error[0]

message = element.$.message
details = element._

counts.failed++
} else {
Expand All @@ -254,6 +261,7 @@ async function parseJunitXml(xml: any): Promise<TestResult> {
status: status,
name: name,
description: classname,
message: message,
details: details,
duration: duration
})
Expand Down
23 changes: 23 additions & 0 deletions test/dashboard.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,4 +54,27 @@ describe("dashboard", async () => {
const actual = dashboardResults(result, TestStatus.Fail)
expect(actual).contains("&lt;no name&gt;")
})

it("includes details and message when present, using proper escaping", async () => {
const result: TestResult = {
counts: { passed: 0, failed: 1, skipped: 0 },
suites: [
{
cases: [
{
status: TestStatus.Fail,
name: "Test",
message: "message escaped <properly>",
details: "details escaped <properly>"
}
]
}
]
}

const actual = dashboardResults(result, TestStatus.Fail)

expect(actual).contains("message escaped &lt;properly&gt;")
expect(actual).contains("details escaped &lt;properly&gt;")
})
})
24 changes: 23 additions & 1 deletion test/junit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,29 @@ describe("junit", async () => {
expect(result.suites[0].cases[1].name).to.eql("passesTestTwo")
expect(result.suites[0].cases[2].name).to.eql("passesTestThree")
expect(result.suites[0].cases[3].name).to.eql("passesTestFour")
expect(result.suites[0].cases[4].name).to.eql("failsTestFive")

const case_with_message = result.suites[0].cases[4]

expect(case_with_message.name).to.eql("failsTestFive")
expect(case_with_message.message).to.be.a("string")
expect(case_with_message.details).to.be.a("string")

const message = "expected:<42> but was:<43>"

expect(case_with_message.message).to.eql(message)

const details = `junit.framework.AssertionFailedError: expected:<42> but was:<43>
\tat test.failsTestFive(Unknown Source)
\tat java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
\tat java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
\tat java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
\tat java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
\tat java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
\tat java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
`

expect(case_with_message.details).to.eql(details)

expect(result.suites[0].cases[5].name).to.eql("failsTestSix")
expect(result.suites[0].cases[6].name).to.eql("failsTestSeven")
expect(result.suites[0].cases[7].name).to.eql("failsTestEight")
Expand Down
126 changes: 81 additions & 45 deletions test/tap.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import * as chai from "chai"
import chaiAsPromised from 'chai-as-promised'
import chaiAsPromised from "chai-as-promised"
import { expect } from "chai"

import { TestStatus, parseTapFile } from "../src/test_parser"
Expand All @@ -17,26 +17,32 @@ describe("tap", async () => {
expect(result.counts.skipped).to.eql(0)

expect(result.suites.length).to.eql(1)
expect(result.suites[0].name).to.eql("Create a new Board and Tile, then place the Tile onto the board.")
expect(result.suites[0].name).to.eql(
"Create a new Board and Tile, then place the Tile onto the board."
)
expect(result.suites[0].cases.length).to.eql(6)
})

it("parses unknown amount and failure", async () => {
const result = await parseTapFile(`${resourcePath}/02-unknown-amount-and-failure.tap`)
const result = await parseTapFile(
`${resourcePath}/02-unknown-amount-and-failure.tap`
)

expect(result.counts.passed).to.eql(5)
expect(result.counts.failed).to.eql(2)
expect(result.counts.skipped).to.eql(0)

expect(result.suites.length).to.eql(2)

expect(result.suites[0].name).to.be.a('undefined')
expect(result.suites[0].name).to.be.a("undefined")
expect(result.suites[0].cases.length).to.eql(1)

expect(result.suites[0].cases[0].status).to.eql(TestStatus.Pass)
expect(result.suites[0].cases[0].name).to.eql("retrieving servers from the database")
expect(result.suites[0].cases[0].name).to.eql(
"retrieving servers from the database"
)

expect(result.suites[1].name).to.eql('need to ping 6 servers')
expect(result.suites[1].name).to.eql("need to ping 6 servers")
expect(result.suites[1].cases.length).to.eql(6)

expect(result.suites[1].cases[0].status).to.eql(TestStatus.Pass)
Expand All @@ -47,14 +53,18 @@ describe("tap", async () => {

expect(result.suites[1].cases[2].status).to.eql(TestStatus.Fail)
expect(result.suites[1].cases[2].name).to.eql("pinged saphire")
expect(result.suites[1].cases[2].details!.substring(0, 37)).to.eql(`message: 'hostname "saphire" unknown'`)
expect(result.suites[1].cases[2].details!.substring(0, 37)).to.eql(
`message: 'hostname "saphire" unknown'`
)

expect(result.suites[1].cases[3].status).to.eql(TestStatus.Pass)
expect(result.suites[1].cases[3].name).to.eql("pinged onyx")

expect(result.suites[1].cases[4].status).to.eql(TestStatus.Fail)
expect(result.suites[1].cases[4].name).to.eql("pinged quartz")
expect(result.suites[1].cases[4].details!.substring(0, 35)).to.eql(`message: 'timeout'\nseverity: fail`)
expect(result.suites[1].cases[4].details!.substring(0, 35)).to.eql(
`message: 'timeout'\nseverity: fail`
)

expect(result.suites[1].cases[5].status).to.eql(TestStatus.Pass)
expect(result.suites[1].cases[5].name).to.eql("pinged gold")
Expand All @@ -79,31 +89,43 @@ describe("tap", async () => {

expect(result.suites.length).to.eql(2)

expect(result.suites[0].name).to.be.a('undefined')
expect(result.suites[0].name).to.be.a("undefined")
expect(result.suites[0].cases.length).to.eql(1)

expect(result.suites[0].cases[0].status).to.eql(TestStatus.Pass)
expect(result.suites[0].cases[0].name).to.eql("approved operating system")
expect(result.suites[0].cases[0].name).to.eql(
"approved operating system"
)

expect(result.suites[1].name).to.eql('$^0 is solaris')
expect(result.suites[1].name).to.eql("$^0 is solaris")
expect(result.suites[1].cases.length).to.eql(4)

expect(result.suites[1].cases[0].status).to.eql(TestStatus.Skip)
expect(result.suites[1].cases[0].name).to.be.a('undefined')
expect(result.suites[1].cases[0].description).to.eql("no /sys directory")
expect(result.suites[1].cases[0].name).to.be.a("undefined")
expect(result.suites[1].cases[0].description).to.eql(
"no /sys directory"
)

expect(result.suites[1].cases[1].status).to.eql(TestStatus.Skip)
expect(result.suites[1].cases[1].description).to.eql("no /sys directory")
expect(result.suites[1].cases[1].description).to.eql(
"no /sys directory"
)

expect(result.suites[1].cases[2].status).to.eql(TestStatus.Skip)
expect(result.suites[1].cases[2].description).to.eql("no /sys directory")
expect(result.suites[1].cases[2].description).to.eql(
"no /sys directory"
)

expect(result.suites[1].cases[3].status).to.eql(TestStatus.Skip)
expect(result.suites[1].cases[3].description).to.eql("no /sys directory")
expect(result.suites[1].cases[3].description).to.eql(
"no /sys directory"
)
})

it("can skip an entire file", async () => {
const result = await parseTapFile(`${resourcePath}/05-skip-everything.tap`)
const result = await parseTapFile(
`${resourcePath}/05-skip-everything.tap`
)

expect(result.counts.passed).to.eql(0)
expect(result.counts.failed).to.eql(0)
Expand All @@ -127,59 +149,71 @@ describe("tap", async () => {
expect(result.suites[0].cases[0].name).to.eql("Creating test program")

expect(result.suites[0].cases[1].status).to.eql(TestStatus.Pass)
expect(result.suites[0].cases[1].name).to.eql("Test program runs, no error")
expect(result.suites[0].cases[1].name).to.eql(
"Test program runs, no error"
)

expect(result.suites[0].cases[2].status).to.eql(TestStatus.Skip)
expect(result.suites[0].cases[2].name).to.eql("infinite loop")
expect(result.suites[0].cases[2].description).to.eql("halting problem unsolved")
expect(result.suites[0].cases[2].description).to.eql(
"halting problem unsolved"
)

expect(result.suites[0].cases[3].status).to.eql(TestStatus.Skip)
expect(result.suites[0].cases[3].name).to.eql("infinite loop 2")
expect(result.suites[0].cases[3].description).to.eql("halting problem unsolved")
expect(result.suites[0].cases[3].description).to.eql(
"halting problem unsolved"
)
})

it("handles creative liberties", async () => {
const result = await parseTapFile(`${resourcePath}/07-creative-liberties.tap`)
const result = await parseTapFile(
`${resourcePath}/07-creative-liberties.tap`
)

expect(result.counts.passed).to.eql(9)
expect(result.counts.failed).to.eql(0)
expect(result.counts.skipped).to.eql(0)

expect(result.suites[0].cases[0].status).to.eql(TestStatus.Pass)
expect(result.suites[0].cases[0].name).to.eql("created Board")
expect(result.suites[0].cases[0].details).to.be.a('undefined')
expect(result.suites[0].cases[0].details).to.be.a("undefined")

expect(result.suites[0].cases[1].status).to.eql(TestStatus.Pass)
expect(result.suites[0].cases[1].name).to.be.a('undefined')
expect(result.suites[0].cases[1].details).to.be.a('undefined')
expect(result.suites[0].cases[1].name).to.be.a("undefined")
expect(result.suites[0].cases[1].details).to.be.a("undefined")

expect(result.suites[0].cases[2].status).to.eql(TestStatus.Pass)
expect(result.suites[0].cases[2].name).to.be.a('undefined')
expect(result.suites[0].cases[2].details).to.be.a('undefined')
expect(result.suites[0].cases[2].name).to.be.a("undefined")
expect(result.suites[0].cases[2].details).to.be.a("undefined")

expect(result.suites[0].cases[3].status).to.eql(TestStatus.Pass)
expect(result.suites[0].cases[3].name).to.be.a('undefined')
expect(result.suites[0].cases[3].details).to.be.a('undefined')
expect(result.suites[0].cases[3].name).to.be.a("undefined")
expect(result.suites[0].cases[3].details).to.be.a("undefined")

expect(result.suites[0].cases[4].status).to.eql(TestStatus.Pass)
expect(result.suites[0].cases[4].name).to.be.a('undefined')
expect(result.suites[0].cases[4].details).to.be.a('undefined')
expect(result.suites[0].cases[4].name).to.be.a("undefined")
expect(result.suites[0].cases[4].details).to.be.a("undefined")

expect(result.suites[0].cases[5].status).to.eql(TestStatus.Pass)
expect(result.suites[0].cases[5].name).to.be.a('undefined')
expect(result.suites[0].cases[5].details).to.be.a('undefined')
expect(result.suites[0].cases[5].name).to.be.a("undefined")
expect(result.suites[0].cases[5].details).to.be.a("undefined")

expect(result.suites[0].cases[6].status).to.eql(TestStatus.Pass)
expect(result.suites[0].cases[6].name).to.be.a('undefined')
expect(result.suites[0].cases[6].details).to.be.a('undefined')
expect(result.suites[0].cases[6].name).to.be.a("undefined")
expect(result.suites[0].cases[6].details).to.be.a("undefined")

expect(result.suites[0].cases[7].status).to.eql(TestStatus.Pass)
expect(result.suites[0].cases[7].name).to.be.a('undefined')
expect(result.suites[0].cases[7].details!.substring(0, 23)).to.eql('message: "Board layout"')
expect(result.suites[0].cases[7].name).to.be.a("undefined")
expect(result.suites[0].cases[7].details!.substring(0, 23)).to.eql(
'message: "Board layout"'
)

expect(result.suites[0].cases[8].status).to.eql(TestStatus.Pass)
expect(result.suites[0].cases[8].name).to.eql('board has 7 tiles + starter tile')
expect(result.suites[0].cases[8].details).to.be.a('undefined')
expect(result.suites[0].cases[8].name).to.eql(
"board has 7 tiles + starter tile"
)
expect(result.suites[0].cases[8].details).to.be.a("undefined")
})

it("handles everything", async () => {
Expand Down Expand Up @@ -226,27 +260,27 @@ describe("tap", async () => {
expect(result.suites[0].cases[11].name).to.eql("no number 12")

expect(result.suites[0].cases[12].status).to.eql(TestStatus.Skip)
expect(result.suites[0].cases[12].name).to.be.a('undefined')
expect(result.suites[0].cases[12].name).to.be.a("undefined")
expect(result.suites[0].cases[12].description).to.eql("skip 13")

expect(result.suites[0].cases[13].status).to.eql(TestStatus.Skip)
expect(result.suites[0].cases[13].name).to.be.a('undefined')
expect(result.suites[0].cases[13].name).to.be.a("undefined")
expect(result.suites[0].cases[13].description).to.eql("skip 14")

expect(result.suites[0].cases[14].status).to.eql(TestStatus.Skip)
expect(result.suites[0].cases[14].name).to.be.a('undefined')
expect(result.suites[0].cases[14].name).to.be.a("undefined")
expect(result.suites[0].cases[14].description).to.eql("skip 15")

expect(result.suites[0].cases[15].status).to.eql(TestStatus.Skip)
expect(result.suites[0].cases[15].name).to.be.a('undefined')
expect(result.suites[0].cases[15].name).to.be.a("undefined")
expect(result.suites[0].cases[15].description).to.eql("skip 16")

expect(result.suites[0].cases[16].status).to.eql(TestStatus.Skip)
expect(result.suites[0].cases[16].name).to.be.a('undefined')
expect(result.suites[0].cases[16].name).to.be.a("undefined")
expect(result.suites[0].cases[16].description).to.eql("skip 17")

expect(result.suites[0].cases[17].status).to.eql(TestStatus.Skip)
expect(result.suites[0].cases[17].name).to.be.a('undefined')
expect(result.suites[0].cases[17].name).to.be.a("undefined")
expect(result.suites[0].cases[17].description).to.eql("number 18")

expect(result.suites[0].cases[18].status).to.eql(TestStatus.Skip)
Expand All @@ -265,6 +299,8 @@ describe("tap", async () => {
})

it("rejects invalid trailer", async () => {
expect(parseTapFile(`${resourcePath}/10-results-after-trailer.tap`)).to.be.rejectedWith(Error)
expect(
parseTapFile(`${resourcePath}/10-results-after-trailer.tap`)
).to.be.rejectedWith(Error)
})
})

0 comments on commit 2becfc2

Please sign in to comment.