Skip to content

Commit

Permalink
feat: Use jest-editor-support to show messages etc (#23)
Browse files Browse the repository at this point in the history
* feat: Use jest-editor-support to show messages etc

* feat: Add some failing tests
  • Loading branch information
orta authored and macklinu committed Oct 13, 2017
1 parent e6ccb2e commit 973bcf2
Show file tree
Hide file tree
Showing 13 changed files with 1,729 additions and 1,191 deletions.
38 changes: 7 additions & 31 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,26 +11,10 @@

### Setup Jest

This Danger plugin relies on modifying your Jest configuration.
This Danger plugin relies on modifying your Jest configuration slightly on CI to also output a JSON file of the results.

Install [jest-json-reporter](https://github.com/Vall3y/jest-json-reporter):

```sh
yarn add jest-json-reporter --dev
```

Modify your `package.json` to process test results with jest-json-reporter. You may optionally set the output path of the JSON test results using the `jestJsonReporter.outputFile` path (which otherwise defaults to `./test-results.json`):

```json
{
"jest": {
"testResultsProcessor": "jest-json-reporter"
},
"jestJsonReporter": {
"outputFile": "tests/results.json"
}
}
```
You need to make the `yarn jest` command include: `--outputFile test-results.json --json`. This will run your tests
like normal, but will also create a file with the full test results after.

> You may also want to add the JSON output file to your `.gitignore`, since it doesn't need to be checked into source control.
Expand All @@ -42,25 +26,17 @@ Install this Danger plugin:
yarn add danger-plugin-jest --dev
```

If you set `jestJsonReporter.outputFile` in your `package.json`, make sure that `testResultsJsonPath` matches that path:
By default, this package will assume you've set the filename as `test-results.json`, but you can use any path.

```js
// dangerfile.js
import path from 'path'
import jest from 'danger-plugin-jest'

jest({
testResultsJsonPath: path.resolve(__dirname, 'tests/results.json'),
})
```

If you _did not_ change the `jestJsonReporter.outputFile` path in your `package.json`, you can just do the following:

```js
// dangerfile.js
import jest from 'danger-plugin-jest'

// Default
jest()
// Custom path
jest({ testResultsJsonPath: path.resolve(__dirname, 'tests/results.json') })
```

See [`src/index.ts`](https://github.com/macklinu/danger-plugin-jest/blob/master/src/index.ts) for more details.
Expand Down
4 changes: 2 additions & 2 deletions dangerfile.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
import jest from "./dist";
import jest from './dist'

jest();
jest()
3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@
"url": "https://github.com/macklinu/danger-plugin-jest/issues"
},
"homepage": "https://github.com/macklinu/danger-plugin-jest#readme",
"dependencies": {
"strip-ansi": "^4.0.0"
},
"devDependencies": {
"@types/jest": "^19.2.3",
"@types/node": "^7.0.22",
Expand Down
13 changes: 0 additions & 13 deletions src/TestFailureFormatter.ts

This file was deleted.

337 changes: 337 additions & 0 deletions src/__tests__/__fixtures__/failed-tests-j20.json

Large diffs are not rendered by default.

2,238 changes: 1,118 additions & 1,120 deletions src/__tests__/__fixtures__/failing-tests.json

Large diffs are not rendered by default.

11 changes: 11 additions & 0 deletions src/__tests__/fails.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
it.skip('Fails 1', () => {
expect(1).toEqual(2)
})

it.skip('Fails 2', () => {
expect(' asdasd').toBeLessThanOrEqual(2)
})

it.skip('Fails 4', () => {
expect({ a: 'asda'}).toBeFalsy()
})
32 changes: 29 additions & 3 deletions src/__tests__/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,27 +8,53 @@ beforeEach(() => {
global['message'] = jest.fn()
global['fail'] = jest.fn()
global['warn'] = jest.fn()
global['danger'] = {
github: {
pr: {
head: {
ref: 'branch',
repo: {
full_name: 'repo/slug',
},
},
},
utils: {
fileLinks: (a) => a,
},
},
}
})

test('messages with passing test results', () => {
jestResults({
testResultsJsonPath: fixture('passing-tests.json'),
})
expect(global['message']).toHaveBeenCalled()
expect(global['fail']).not.toHaveBeenCalled()
})

test('fails with failing test results', () => {
jestResults({
testResultsJsonPath: fixture('failing-tests.json'),
})
expect(global['fail']).toHaveBeenCalledWith(expect.stringMatching(/Jest tests failed/))
expect(global['fail']).toHaveBeenCalledWith(expect.stringMatching(/FAIL/))
})

test('fails with failing test results from jest 20', () => {
jestResults({
testResultsJsonPath: fixture('failed-tests-j20.json'),
})
expect(global['fail']).toHaveBeenCalledWith(expect.stringMatching(/FAIL/))
})

test('warns when test results JSON file cannot be read', () => {
jestResults({
testResultsJsonPath: fixture('nonexistent.json'),
})
expect(global['warn']).toHaveBeenCalled()
expect(global['fail']).toHaveBeenCalled()
})

test.skip('Fails 6', () => {
expect({ v: 'asda'}).toContain('s')
})

const fixture = (filename: string) => path.resolve(__dirname, '__fixtures__', filename)
2 changes: 2 additions & 0 deletions src/ambient.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,5 @@
declare function warn(message: string): void
declare function fail(message: string): void
declare function message(message: string): void

declare module 'strip-ansi'
125 changes: 104 additions & 21 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,36 +1,119 @@
import * as fs from 'fs'
import * as path from 'path'

import { TestFailureFormatter } from './TestFailureFormatter'
import * as stripANSI from 'strip-ansi'

export interface IPluginConfig {
testResultsJsonPath?: string
}
import { DangerDSLType } from '../node_modules/danger/distribution/dsl/DangerDSL'
import { IInsideFileTestResults, IJestTestOldResults, IJestTestResults } from './types'
declare var danger: DangerDSLType

interface IJestTestResults {
success: boolean
export interface IPluginConfig {
testResultsJsonPath: string
}

export default function jest(config: IPluginConfig = {}) {
const {
testResultsJsonPath = 'test-results.json',
} = config

export default function jest(config: Partial<IPluginConfig> = {}) {
const { testResultsJsonPath = 'test-results.json' } = config
try {
const jsonFileContents = fs.readFileSync(testResultsJsonPath, 'utf8')
const testResults: IJestTestResults = JSON.parse(jsonFileContents)
if (testResults.success) {
message(':white_check_mark: Jest tests passed')
} else {
const formattedFailures = new TestFailureFormatter(testResults).format()
fail(`:no_good_man: Jest tests failed:
const jsonResults: IJestTestResults = JSON.parse(jsonFileContents)
// console.log(jsonResults)
if (jsonResults.success) {
// tslint:disable-next-line:no-console
console.log('Jest tests passed :+1:')
return
}
// console.log(1)

\`\`\`
${formattedFailures}
\`\`\``)
const isModernFormatResults = jsonResults.testResults[0].testResults
if (isModernFormatResults) {
presentErrorsForNewStyleResults(jsonResults)
} else {
presentErrorsForOldStyleResults(jsonResults as any)
}
} catch (e) {
// tslint:disable-next-line:no-console
console.error(e)
warn('Could not read test results. Danger cannot pass or fail the build.')
fail('[danger-plugin-jest] Could not read test results. Danger cannot pass or fail the build.')
}
}

const presentErrorsForOldStyleResults = (jsonResults: IJestTestOldResults) => {
const failing = jsonResults.testResults.filter((tr) => tr.status === 'failed')

failing.forEach((results) => {
const relativeFilePath = path.relative(process.cwd(), results.name)
const failedAssertions = results.assertionResults.filter((r) => r.status === 'failed')
const failMessage = fileToFailString(relativeFilePath, failedAssertions as any)
fail(failMessage)
})
}

const presentErrorsForNewStyleResults = (jsonResults: IJestTestResults) => {
const failing = jsonResults.testResults.filter((tr) => tr.numFailingTests > 0)

failing.forEach((results) => {
const relativeFilePath = path.relative(process.cwd(), results.testFilePath)
const failedAssertions = results.testResults.filter((r) => r.status === 'failed')
const failMessage = fileToFailString(relativeFilePath, failedAssertions)
fail(failMessage)
})
}

// e.g. https://github.com/orta/danger-plugin-jest/blob/master/src/__tests__/fails.test.ts
const linkToTest = (file: string, msg: string, title: string) => {
const line = lineOfError(msg, file)
const repo = danger.github.pr.head.repo
const urlParts = [
'https://github.com',
repo.full_name,
'blob',
danger.github.pr.head.ref,
`${file}${'line' ? '#L' + line : ''}`,
]
return `<a href='${urlParts.join('/')}'>${title}</a>`
}

const assertionFailString = (file: string, status: IInsideFileTestResults): string => `
<li>
${linkToTest(file, status.failureMessages && status.failureMessages[0], status.title)}
<br/>
${sanitizeShortErrorMessage(status.failureMessages && stripANSI(status.failureMessages[0]))}
<details>
<summary>Full message</summary>
<pre><code>
${status.failureMessages && stripANSI(status.failureMessages.join('\n'))}
</code></pre>
</details>
</li>
<br/>
`

const fileToFailString = (path: string, failedAssertions: IInsideFileTestResults[]): string => `
<b>🃏 FAIL</b> in ${danger.github.utils.fileLinks([path])}
${failedAssertions.map((a) => assertionFailString(path, a)).join('\n\n')}
`

const lineOfError = (msg: string, filePath: string): number | null => {
const filename = path.basename(filePath)
const restOfTrace = msg.split(filename, 2)[1]
return restOfTrace ? parseInt(restOfTrace.split(':')[1], 10) : null
}

const sanitizeShortErrorMessage = (msg: string): string => {
if (msg.includes('does not match stored snapshot')) {
return 'Snapshot has changed'
}

return msg
.split(' at', 1)[0]
.trim()
.split('\n')
.splice(2)
.join('')
.replace(/\s\s+/g, ' ')
.replace('Received:', ', received:')
.replace('., received', ', received')
.split('Difference:')[0]
}
Loading

0 comments on commit 973bcf2

Please sign in to comment.