Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(reporter-junit): escape XML when in error message (fix: #1823) #1890

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
5 changes: 4 additions & 1 deletion packages/vitest/src/node/reporters/junit.ts
Expand Up @@ -112,7 +112,10 @@ export class JUnitReporter implements Reporter {

async writeErrorDetails(error: ErrorWithDiff): Promise<void> {
const errorName = error.name ?? error.nameStr ?? 'Unknown Error'
await this.baseLog(`${errorName}: ${error.message}`)
const errorDetails = `${errorName}: ${error.message}`

// Be sure to escape any XML in the error Details
await this.baseLog(escapeXML(errorDetails))

const stack = parseStacktrace(error)

Expand Down
58 changes: 58 additions & 0 deletions test/reporters/src/data-for-junit.ts
@@ -0,0 +1,58 @@
import { AssertionError } from 'assert'
import type { File, Suite, Task } from 'vitest'

function createSuiteHavingFailedTestWithXmlInError(): File[] {
const file: File = {
id: '1223128da3',
name: 'test/core/test/basic.test.ts',
type: 'suite',
mode: 'run',
filepath: '/vitest/test/core/test/basic.test.ts',
result: { state: 'fail', duration: 145.99284195899963 },
tasks: [],
}

const suite: Suite = {
id: '',
type: 'suite',
name: 'suite',
mode: 'run',
file,
result: { state: 'pass', duration: 1.90183687210083 },
tasks: [],
}

const errorWithXml = new AssertionError({
message: 'error message that has XML in it <tag>',
})

errorWithXml.stack = 'Error: error message that has XML in it <tag>\n'
+ ' at /vitest/test/core/test/basic.test.ts:8:32\n'
+ ' at etc....'

const tasks: Task[] = [
{
id: '123_0',
type: 'test',
name: 'test with xml in error',
mode: 'run',
suite,
fails: undefined,
file,
result: {
state: 'fail',
error: errorWithXml,
duration: 2.123123123,
},
context: null as any,
},
]

file.tasks = [suite]
suite.tasks = tasks

return [file]
}

export { createSuiteHavingFailedTestWithXmlInError }

5 changes: 4 additions & 1 deletion test/reporters/src/data.ts
Expand Up @@ -174,4 +174,7 @@ const tasks: Task[] = [
file.tasks = [suite]
suite.tasks = tasks

export const files = [file]
const files = [file]

export { files }

20 changes: 20 additions & 0 deletions test/reporters/tests/__snapshots__/reporters.spec.ts.snap
Expand Up @@ -236,6 +236,26 @@ AssertionError: expected 2.23606797749979 to equal 2
"
`;

exports[`JUnit reporter with outputFile with XML in error message 1`] = `
"JUNIT report written to <process-cwd>/report_escape_msg_xml.xml
"
`;

exports[`JUnit reporter with outputFile with XML in error message 2`] = `
"<?xml version=\\"1.0\\" encoding=\\"UTF-8\\" ?>
<testsuites>
<testsuite name=\\"test/core/test/basic.test.ts\\" timestamp=\\"2022-01-19T10:10:01.759Z\\" hostname=\\"hostname\\" tests=\\"1\\" failures=\\"1\\" errors=\\"0\\" skipped=\\"0\\" time=\\"0.145992842\\">
<testcase classname=\\"test/core/test/basic.test.ts\\" name=\\"suite &gt; test with xml in error\\" time=\\"0.0021231231\\">
<failure message=\\"error message that has XML in it &lt;tag&gt;\\" type=\\"AssertionError\\">
AssertionError: error message that has XML in it &lt;tag&gt;
❯ vitest/test/core/test/basic.test.ts:8:32
</failure>
</testcase>
</testsuite>
</testsuites>
"
`;

exports[`json reporter (no outputFile entry) 1`] = `
{
"numFailedTestSuites": 0,
Expand Down
30 changes: 30 additions & 0 deletions test/reporters/tests/reporters.spec.ts
Expand Up @@ -7,6 +7,7 @@ import { TapReporter } from '../../../packages/vitest/src/node/reporters/tap'
import { TapFlatReporter } from '../../../packages/vitest/src/node/reporters/tap-flat'
import { getContext } from '../src/context'
import { files } from '../src/data'
import { createSuiteHavingFailedTestWithXmlInError } from '../src/data-for-junit'

afterEach(() => {
vi.useRealTimers()
Expand Down Expand Up @@ -103,6 +104,35 @@ test('JUnit reporter with outputFile', async () => {
rmSync(outputFile)
})

test('JUnit reporter with outputFile with XML in error message', async () => {
// Arrange
const reporter = new JUnitReporter()
const outputFile = resolve('report_escape_msg_xml.xml')
const context = getContext()
context.vitest.config.outputFile = outputFile

vi.mock('os', () => ({
hostname: () => 'hostname',
}))

vi.setSystemTime(1642587001759)

// setup suite with failed test with xml
const filesWithTestHavingXmlInError = createSuiteHavingFailedTestWithXmlInError()

// Act
await reporter.onInit(context.vitest)
await reporter.onFinished(filesWithTestHavingXmlInError)

// Assert
expect(normalizeCwd(context.output)).toMatchSnapshot()
expect(existsSync(outputFile)).toBe(true)
expect(readFileSync(outputFile, 'utf8')).toMatchSnapshot()

// Cleanup
rmSync(outputFile)
})

test('JUnit reporter with outputFile object', async () => {
// Arrange
const reporter = new JUnitReporter()
Expand Down