Skip to content

Commit

Permalink
fix: prevent crashing when directory of outputFile does not exist (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
AriPerkkio committed Mar 19, 2022
1 parent a6ced30 commit 5eac4ca
Show file tree
Hide file tree
Showing 4 changed files with 202 additions and 9 deletions.
9 changes: 7 additions & 2 deletions packages/vitest/src/node/reporters/json.ts
@@ -1,5 +1,5 @@
import { promises as fs } from 'fs'
import { resolve } from 'pathe'
import { existsSync, promises as fs } from 'fs'
import { dirname, resolve } from 'pathe'
import type { Vitest } from '../../node'
import type { File, Reporter } from '../../types'
import { getSuites, getTests } from '../../utils'
Expand Down Expand Up @@ -90,6 +90,11 @@ export class JsonReporter implements Reporter {
async writeReport(report: string) {
if (this.ctx.config.outputFile) {
const reportFile = resolve(this.ctx.config.root, this.ctx.config.outputFile)

const outputDirectory = dirname(reportFile)
if (!existsSync(outputDirectory))
await fs.mkdir(outputDirectory, { recursive: true })

await fs.writeFile(reportFile, report, 'utf-8')
this.ctx.log(`JSON report written to ${reportFile}`)
}
Expand Down
9 changes: 7 additions & 2 deletions packages/vitest/src/node/reporters/junit.ts
@@ -1,6 +1,6 @@
import { promises as fs } from 'fs'
import { existsSync, promises as fs } from 'fs'
import { hostname } from 'os'
import { relative, resolve } from 'pathe'
import { dirname, relative, resolve } from 'pathe'

import type { Vitest } from '../../node'
import type { ErrorWithDiff, Reporter, Task } from '../../types'
Expand Down Expand Up @@ -46,6 +46,11 @@ export class JUnitReporter implements Reporter {

if (this.ctx.config.outputFile) {
this.reportFile = resolve(this.ctx.config.root, this.ctx.config.outputFile)

const outputDirectory = dirname(this.reportFile)
if (!existsSync(outputDirectory))
await fs.mkdir(outputDirectory, { recursive: true })

const fileFd = await fs.open(this.reportFile, 'w+')

this.baseLog = async(text: string) => await fs.writeFile(fileFd, `${text}\n`)
Expand Down
128 changes: 128 additions & 0 deletions test/reporters/tests/__snapshots__/reporters.spec.ts.snap
Expand Up @@ -65,6 +65,41 @@ AssertionError: expected 2.23606797749979 to equal 2
"
`;
exports[`JUnit reporter with outputFile in non-existing directory 1`] = `
"JUNIT report written to <process-cwd>/junitReportDirectory/deeply/nested/report.xml
"
`;
exports[`JUnit reporter with outputFile in non-existing directory 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=\\"8\\" failures=\\"1\\" errors=\\"0\\" skipped=\\"1\\" time=\\"0.1459928420\\">
<testcase classname=\\"test/core/test/basic.test.ts\\" name=\\"suite &gt; inner suite &gt; Math.sqrt()\\" time=\\"0.0014422860\\">
<failure message=\\"expected 2.23606797749979 to equal 2\\" type=\\"AssertionError\\">
AssertionError: expected 2.23606797749979 to equal 2
vitest/test/core/test/basic.test.ts:8:32
</failure>
</testcase>
<testcase classname=\\"test/core/test/basic.test.ts\\" name=\\"suite &gt; JSON\\" time=\\"0.0010237110\\">
</testcase>
<testcase classname=\\"test/core/test/basic.test.ts\\" name=\\"suite &gt; async with timeout\\">
<skipped/>
</testcase>
<testcase classname=\\"test/core/test/basic.test.ts\\" name=\\"suite &gt; timeout\\" time=\\"0.1005059841\\">
</testcase>
<testcase classname=\\"test/core/test/basic.test.ts\\" name=\\"suite &gt; callback setup success \\" time=\\"0.0201848750\\">
</testcase>
<testcase classname=\\"test/core/test/basic.test.ts\\" name=\\"suite &gt; callback test success \\" time=\\"0.0003324542\\">
</testcase>
<testcase classname=\\"test/core/test/basic.test.ts\\" name=\\"suite &gt; callback setup success done(false)\\" time=\\"0.0197386060\\">
</testcase>
<testcase classname=\\"test/core/test/basic.test.ts\\" name=\\"suite &gt; callback test success done(false)\\" time=\\"0.0001923509\\">
</testcase>
</testsuite>
</testsuites>
"
`;
exports[`json reporter 1`] = `
{
"numFailedTestSuites": 0,
Expand Down Expand Up @@ -246,6 +281,99 @@ exports[`json reporter with outputFile 2`] = `
}"
`;
exports[`json reporter with outputFile in non-existing directory 1`] = `
"JSON report written to <process-cwd>/jsonReportDirectory/deeply/nested/report.json
"
`;
exports[`json reporter with outputFile in non-existing directory 2`] = `
"{
\\"numTotalTestSuites\\": 3,
\\"numPassedTestSuites\\": 3,
\\"numFailedTestSuites\\": 0,
\\"numPendingTestSuites\\": 0,
\\"numTotalTests\\": 8,
\\"numPassedTests\\": 7,
\\"numFailedTests\\": 1,
\\"numPendingTests\\": 0,
\\"numTodoTests\\": 0,
\\"startTime\\": 1642587001759,
\\"success\\": false,
\\"testResults\\": [
{
\\"perfStats\\": {
\\"runtime\\": 1.4422860145568848
},
\\"displayName\\": \\"Math.sqrt()\\",
\\"failureMessage\\": \\"expected 2.23606797749979 to equal 2\\",
\\"skipped\\": false,
\\"status\\": \\"fail\\",
\\"testFilePath\\": \\"/vitest/test/core/test/basic.test.ts\\"
},
{
\\"perfStats\\": {
\\"runtime\\": 1.0237109661102295
},
\\"displayName\\": \\"JSON\\",
\\"skipped\\": false,
\\"status\\": \\"pass\\",
\\"testFilePath\\": \\"/vitest/test/core/test/basic.test.ts\\"
},
{
\\"perfStats\\": {},
\\"displayName\\": \\"async with timeout\\",
\\"skipped\\": true,
\\"testFilePath\\": \\"/vitest/test/core/test/basic.test.ts\\"
},
{
\\"perfStats\\": {
\\"runtime\\": 100.50598406791687
},
\\"displayName\\": \\"timeout\\",
\\"skipped\\": false,
\\"status\\": \\"pass\\",
\\"testFilePath\\": \\"/vitest/test/core/test/basic.test.ts\\"
},
{
\\"perfStats\\": {
\\"runtime\\": 20.184875011444092
},
\\"displayName\\": \\"callback setup success \\",
\\"skipped\\": false,
\\"status\\": \\"pass\\",
\\"testFilePath\\": \\"/vitest/test/core/test/basic.test.ts\\"
},
{
\\"perfStats\\": {
\\"runtime\\": 0.33245420455932617
},
\\"displayName\\": \\"callback test success \\",
\\"skipped\\": false,
\\"status\\": \\"pass\\",
\\"testFilePath\\": \\"/vitest/test/core/test/basic.test.ts\\"
},
{
\\"perfStats\\": {
\\"runtime\\": 19.738605976104736
},
\\"displayName\\": \\"callback setup success done(false)\\",
\\"skipped\\": false,
\\"status\\": \\"pass\\",
\\"testFilePath\\": \\"/vitest/test/core/test/basic.test.ts\\"
},
{
\\"perfStats\\": {
\\"runtime\\": 0.1923508644104004
},
\\"displayName\\": \\"callback test success done(false)\\",
\\"skipped\\": false,
\\"status\\": \\"pass\\",
\\"testFilePath\\": \\"/vitest/test/core/test/basic.test.ts\\"
}
]
}"
`;
exports[`tap reporter 1`] = `
"TAP version 13
1..1
Expand Down
65 changes: 60 additions & 5 deletions test/reporters/tests/reporters.spec.ts
@@ -1,4 +1,4 @@
import { existsSync, readFileSync, rmSync } from 'fs'
import { existsSync, readFileSync, rmSync, rmdirSync } from 'fs'
import { afterEach, expect, test, vi } from 'vitest'
import { normalize, resolve } from 'pathe'
import { JsonReporter } from '../../../packages/vitest/src/node/reporters/json'
Expand Down Expand Up @@ -75,15 +75,41 @@ test('JUnit reporter with outputFile', async() => {
await reporter.onFinished(files)

// Assert
const output = context.output.replace(normalize(process.cwd()), '<process-cwd>')
expect(output).toMatchSnapshot()
expect(normalizeCwd(context.output)).toMatchSnapshot()
expect(existsSync(outputFile)).toBe(true)
expect(readFileSync(outputFile, 'utf8')).toMatchSnapshot()

// Cleanup
rmSync(outputFile)
})

test('JUnit reporter with outputFile in non-existing directory', async() => {
// Arrange
const reporter = new JUnitReporter()
const rootDirectory = resolve('junitReportDirectory')
const outputFile = `${rootDirectory}/deeply/nested/report.xml`
const context = getContext()
context.vitest.config.outputFile = outputFile

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

vi.setSystemTime(1642587001759)

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

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

// Cleanup
rmdirSync(rootDirectory, { recursive: true })
})

test('json reporter', async() => {
// Arrange
const reporter = new JsonReporter()
Expand Down Expand Up @@ -113,11 +139,40 @@ test('json reporter with outputFile', async() => {
await reporter.onFinished(files)

// Assert
const output = context.output.replace(normalize(process.cwd()), '<process-cwd>')
expect(output).toMatchSnapshot()
expect(normalizeCwd(context.output)).toMatchSnapshot()
expect(existsSync(outputFile)).toBe(true)
expect(readFileSync(outputFile, 'utf8')).toMatchSnapshot()

// Cleanup
rmSync(outputFile)
})

test('json reporter with outputFile in non-existing directory', async() => {
// Arrange
const reporter = new JsonReporter()
const rootDirectory = resolve('jsonReportDirectory')
const outputFile = `${rootDirectory}/deeply/nested/report.json`
const context = getContext()
context.vitest.config.outputFile = outputFile

vi.setSystemTime(1642587001759)

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

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

// Cleanup
rmdirSync(rootDirectory, { recursive: true })
})

/**
* Ensure environment and OS specific paths are consistent in snapshots
*/
function normalizeCwd(text: string) {
return text.replace(normalize(process.cwd()), '<process-cwd>')
}

0 comments on commit 5eac4ca

Please sign in to comment.