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 2 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: 57 additions & 1 deletion test/reporters/src/data.ts
Expand Up @@ -174,4 +174,60 @@ const tasks: Task[] = [
file.tasks = [suite]
suite.tasks = tasks

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

function createSuiteHavingFailedTestWithXmlInError(): File[] {
ChrisTowles marked this conversation as resolved.
Show resolved Hide resolved
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 { files, createSuiteHavingFailedTestWithXmlInError }

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
31 changes: 30 additions & 1 deletion test/reporters/tests/reporters.spec.ts
Expand Up @@ -6,7 +6,7 @@ import { JUnitReporter } from '../../../packages/vitest/src/node/reporters/junit
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, files } from '../src/data'

afterEach(() => {
vi.useRealTimers()
Expand Down Expand Up @@ -103,6 +103,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 test 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