forked from mirrors/probot
chore(tests): Migrate to Jest
* Add jest, remove mocha/expect * Migrate all tests to Jest * Make test pass * Move tests into `/test/` * Update testing docs * Fix resolver test
This commit is contained in:
parent
6c58a9742b
commit
e7a0c5cd80
|
@ -6,11 +6,9 @@ next: docs/pagination.md
|
|||
|
||||
We highly recommend working in the style of [test-driven development](http://agiledata.org/essays/tdd.html) when creating probot apps. It frustrating to constantly create real GitHub events in order to test a app. Redelivering webhooks is possible and can be accessed in your app's [settings](https://github.com/settings/apps) page under the **Advanced** tab. We do offer the above documented `simulate` method to help make this easier; however, by writing your tests first, you can avoid repeatedly recreating actual events from GitHub to check if your code is working.
|
||||
|
||||
For our testing examples, we use [mocha](https://mochajs.org/) and [expect](https://github.com/mjackson/expect), but there are other options that can perform similar operations. Here's an example of creating a robot instance and mocking out the GitHub API:
|
||||
For our testing examples, we use [jest](https://facebook.github.io/jest/), but there are other options that can perform similar operations. Here's an example of creating a robot instance and mocking out the GitHub API:
|
||||
|
||||
```js
|
||||
// Requiring our testing framework
|
||||
const expect = require('expect')
|
||||
// Requiring probot allows us to mock out a robot instance
|
||||
const {createRobot} = require('probot')
|
||||
// Requireing our app
|
||||
|
@ -31,7 +29,7 @@ describe('your-app', () => {
|
|||
// This is an easy way to mock out the GitHub API
|
||||
github = {
|
||||
issues: {
|
||||
createComment: expect.createSpy().andReturn(Promise.resolve({
|
||||
createComment: jest.fn().mockReturnValue(Promise.resolve({
|
||||
// Whatever the GitHub API should return
|
||||
}))
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load Diff
17
package.json
17
package.json
|
@ -9,12 +9,22 @@
|
|||
},
|
||||
"scripts": {
|
||||
"start": "node ./bin/probot run",
|
||||
"test": "mocha && standard && npm run doc-lint",
|
||||
"test": "jest && standard && npm run doc-lint",
|
||||
"doc-lint": "standard docs/**/*.md",
|
||||
"doc": "jsdoc -c .jsdoc.json",
|
||||
"postpublish": "script/publish-docs",
|
||||
"semantic-release": "semantic-release pre && npm publish && semantic-release post"
|
||||
},
|
||||
"jest": {
|
||||
"testMatch": [ "**/test/**/*.js?(x)", "**/?(*.)(spec|test).js?(x)" ],
|
||||
"modulePathIgnorePatterns": [
|
||||
"<rootDir>/test/fixtures/",
|
||||
"<rootDir>/test/setup.js"
|
||||
],
|
||||
"setupFiles": [
|
||||
"<rootDir>/test/setup.js"
|
||||
]
|
||||
},
|
||||
"author": "Brandon Keepers",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
|
@ -29,6 +39,7 @@
|
|||
"github": "^10.1.0",
|
||||
"github-app": "^3.2.0",
|
||||
"github-webhook-handler": "github:rvagg/github-webhook-handler#v0.6.1",
|
||||
"jest": "^21.2.1",
|
||||
"js-yaml": "^3.9.1",
|
||||
"pkg-conf": "^2.0.0",
|
||||
"promise-events": "^0.1.3",
|
||||
|
@ -39,11 +50,9 @@
|
|||
"devDependencies": {
|
||||
"eslint": "^4.6.1",
|
||||
"eslint-plugin-markdown": "^1.0.0-beta.6",
|
||||
"expect": "^1.20.2",
|
||||
"jsdoc": "^3.5.5",
|
||||
"jsdoc-strip-async-await": "^0.1.0",
|
||||
"minami": "^1.1.1",
|
||||
"mocha": "^3.5.0",
|
||||
"nock": "^9.0.19",
|
||||
"semantic-release": "^8.0.3",
|
||||
"standard": "^10.0.3",
|
||||
|
@ -51,7 +60,7 @@
|
|||
},
|
||||
"standard": {
|
||||
"env": [
|
||||
"mocha"
|
||||
"jest"
|
||||
],
|
||||
"plugins": [
|
||||
"markdown"
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
const fs = require('fs')
|
||||
const path = require('path')
|
||||
const expect = require('expect')
|
||||
const Context = require('../lib/context')
|
||||
|
||||
describe('Context', function () {
|
||||
|
@ -82,7 +81,7 @@ describe('Context', function () {
|
|||
beforeEach(function () {
|
||||
github = {
|
||||
repos: {
|
||||
getContent: expect.createSpy()
|
||||
getContent: jest.fn()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -90,7 +89,7 @@ describe('Context', function () {
|
|||
})
|
||||
|
||||
it('gets a valid configuration', async function () {
|
||||
github.repos.getContent.andReturn(Promise.resolve(readConfig('basic.yml')))
|
||||
github.repos.getContent = jest.fn().mockReturnValue(Promise.resolve(readConfig('basic.yml')))
|
||||
const config = await context.config('test-file.yml')
|
||||
|
||||
expect(github.repos.getContent).toHaveBeenCalledWith({
|
||||
|
@ -108,7 +107,7 @@ describe('Context', function () {
|
|||
it('returns null when the file is missing', async function () {
|
||||
const error = new Error('An error occurred')
|
||||
error.code = 404
|
||||
github.repos.getContent.andReturn(Promise.reject(error))
|
||||
github.repos.getContent = jest.fn().mockReturnValue(Promise.reject(error))
|
||||
|
||||
expect(await context.config('test-file.yml')).toBe(null)
|
||||
})
|
||||
|
@ -116,7 +115,7 @@ describe('Context', function () {
|
|||
it('returns the default config when the file is missing and default config is passed', async function () {
|
||||
const error = new Error('An error occurred')
|
||||
error.code = 404
|
||||
github.repos.getContent.andReturn(Promise.reject(error))
|
||||
github.repos.getContent = jest.fn().mockReturnValue(Promise.reject(error))
|
||||
const defaultConfig = {
|
||||
foo: 5,
|
||||
bar: 7,
|
||||
|
@ -127,7 +126,7 @@ describe('Context', function () {
|
|||
})
|
||||
|
||||
it('throws when the configuration file is malformed', async function () {
|
||||
github.repos.getContent.andReturn(Promise.resolve(readConfig('malformed.yml')))
|
||||
github.repos.getContent = jest.fn().mockReturnValue(Promise.resolve(readConfig('malformed.yml')))
|
||||
|
||||
let e
|
||||
let contents
|
||||
|
@ -137,13 +136,13 @@ describe('Context', function () {
|
|||
e = err
|
||||
}
|
||||
|
||||
expect(contents).toNotExist()
|
||||
expect(e).toExist()
|
||||
expect(contents).toBeUndefined()
|
||||
expect(e).toBeDefined()
|
||||
expect(e.message).toMatch(/^end of the stream or a document separator/)
|
||||
})
|
||||
|
||||
it('throws when loading unsafe yaml', async function () {
|
||||
github.repos.getContent.andReturn(readConfig('evil.yml'))
|
||||
github.repos.getContent = jest.fn().mockReturnValue(readConfig('evil.yml'))
|
||||
|
||||
let e
|
||||
let config
|
||||
|
@ -153,13 +152,13 @@ describe('Context', function () {
|
|||
e = err
|
||||
}
|
||||
|
||||
expect(config).toNotExist()
|
||||
expect(e).toExist()
|
||||
expect(config).toBeUndefined()
|
||||
expect(e).toBeDefined()
|
||||
expect(e.message).toMatch(/unknown tag/)
|
||||
})
|
||||
|
||||
it('returns an empty object when the file is empty', async function () {
|
||||
github.repos.getContent.andReturn(readConfig('empty.yml'))
|
||||
github.repos.getContent = jest.fn().mockReturnValue(readConfig('empty.yml'))
|
||||
|
||||
const contents = await context.config('test-file.yml')
|
||||
|
||||
|
@ -167,7 +166,7 @@ describe('Context', function () {
|
|||
})
|
||||
|
||||
it('overwrites default config settings', async function () {
|
||||
github.repos.getContent.andReturn(Promise.resolve(readConfig('basic.yml')))
|
||||
github.repos.getContent = jest.fn().mockReturnValue(Promise.resolve(readConfig('basic.yml')))
|
||||
const config = await context.config('test-file.yml', {foo: 10})
|
||||
|
||||
expect(github.repos.getContent).toHaveBeenCalledWith({
|
||||
|
@ -183,7 +182,7 @@ describe('Context', function () {
|
|||
})
|
||||
|
||||
it('uses default settings to fill in missing options', async function () {
|
||||
github.repos.getContent.andReturn(Promise.resolve(readConfig('missing.yml')))
|
||||
github.repos.getContent = jest.fn().mockReturnValue(Promise.resolve(readConfig('missing.yml')))
|
||||
const config = await context.config('test-file.yml', {bar: 7})
|
||||
|
||||
expect(github.repos.getContent).toHaveBeenCalledWith({
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
const expect = require('expect')
|
||||
const createProbot = require('..')
|
||||
|
||||
describe('Probot', () => {
|
||||
|
@ -17,7 +16,7 @@ describe('Probot', () => {
|
|||
describe('webhook delivery', () => {
|
||||
it('forwards webhooks to the robot', async () => {
|
||||
const robot = probot.load(() => {})
|
||||
robot.receive = expect.createSpy()
|
||||
robot.receive = jest.fn()
|
||||
probot.webhook.emit('*', event)
|
||||
expect(robot.receive).toHaveBeenCalledWith(event)
|
||||
})
|
||||
|
@ -105,9 +104,9 @@ describe('Probot', () => {
|
|||
|
||||
describe('receive', () => {
|
||||
it('forwards events to each plugin', async () => {
|
||||
const spy = expect.createSpy()
|
||||
const spy = jest.fn()
|
||||
const robot = probot.load(robot => robot.on('push', spy))
|
||||
robot.auth = expect.createSpy().andReturn(Promise.resolve({}))
|
||||
robot.auth = jest.fn().mockReturnValue(Promise.resolve({}))
|
||||
|
||||
await probot.receive(event)
|
||||
|
||||
|
|
|
@ -1,2 +0,0 @@
|
|||
--recursive
|
||||
--require ./test/setup
|
|
@ -1,6 +1,6 @@
|
|||
const fs = require('fs')
|
||||
|
||||
const expect = require('expect')
|
||||
const readFileSync = fs.readFileSync
|
||||
const readdirSync = fs.readdirSync
|
||||
|
||||
const {findPrivateKey} = require('../lib/private-key')
|
||||
|
||||
|
@ -11,25 +11,22 @@ describe('private-key', function () {
|
|||
beforeEach(function () {
|
||||
privateKey = 'I AM PRIVET KEY!?!!~1!'
|
||||
keyfilePath = '/some/path'
|
||||
expect.spyOn(fs, 'readFileSync')
|
||||
.andReturn(privateKey)
|
||||
fs.readFileSync = jest.fn().mockReturnValue(privateKey)
|
||||
})
|
||||
|
||||
afterEach(function () {
|
||||
expect.restoreSpies()
|
||||
fs.readFileSync = readFileSync
|
||||
})
|
||||
|
||||
describe('findPrivateKey()', function () {
|
||||
describe('when a filepath is provided', function () {
|
||||
it('should read the file at given filepath', function () {
|
||||
findPrivateKey(keyfilePath)
|
||||
expect(fs.readFileSync)
|
||||
.toHaveBeenCalledWith(keyfilePath)
|
||||
expect(fs.readFileSync).toHaveBeenCalledWith(keyfilePath)
|
||||
})
|
||||
|
||||
it('should return the key', function () {
|
||||
expect(findPrivateKey(keyfilePath))
|
||||
.toEqual(privateKey)
|
||||
expect(findPrivateKey(keyfilePath)).toEqual(privateKey)
|
||||
})
|
||||
})
|
||||
|
||||
|
@ -43,8 +40,7 @@ describe('private-key', function () {
|
|||
})
|
||||
|
||||
it('should return the key', function () {
|
||||
expect(findPrivateKey())
|
||||
.toEqual(privateKey)
|
||||
expect(findPrivateKey()).toEqual(privateKey)
|
||||
})
|
||||
})
|
||||
|
||||
|
@ -58,8 +54,7 @@ describe('private-key', function () {
|
|||
})
|
||||
|
||||
it('should return the key', function () {
|
||||
expect(findPrivateKey())
|
||||
.toEqual('line 1\nline 2')
|
||||
expect(findPrivateKey()).toEqual('line 1\nline 2')
|
||||
})
|
||||
})
|
||||
|
||||
|
@ -74,47 +69,41 @@ describe('private-key', function () {
|
|||
|
||||
it('should read the file at given filepath', function () {
|
||||
findPrivateKey()
|
||||
expect(fs.readFileSync)
|
||||
.toHaveBeenCalledWith(keyfilePath)
|
||||
expect(fs.readFileSync).toHaveBeenCalledWith(keyfilePath)
|
||||
})
|
||||
|
||||
it('should return the key', function () {
|
||||
expect(findPrivateKey())
|
||||
.toEqual(privateKey)
|
||||
expect(findPrivateKey()).toEqual(privateKey)
|
||||
})
|
||||
})
|
||||
|
||||
describe('when no private key is provided', function () {
|
||||
beforeEach(function () {
|
||||
expect.spyOn(fs, 'readdirSync')
|
||||
.andReturn([
|
||||
'foo.txt',
|
||||
'foo.pem'
|
||||
])
|
||||
fs.readdirSync = jest.fn().mockReturnValue([
|
||||
'foo.txt',
|
||||
'foo.pem'
|
||||
])
|
||||
})
|
||||
|
||||
it('should look for one in the current directory', function () {
|
||||
findPrivateKey()
|
||||
expect(fs.readdirSync)
|
||||
.toHaveBeenCalledWith(process.cwd())
|
||||
expect(fs.readdirSync).toHaveBeenCalledWith(process.cwd())
|
||||
})
|
||||
|
||||
describe('and a key file is present', function () {
|
||||
it('should load the key file', function () {
|
||||
findPrivateKey()
|
||||
expect(fs.readFileSync)
|
||||
.toHaveBeenCalledWith('foo.pem')
|
||||
expect(fs.readFileSync).toHaveBeenCalledWith('foo.pem')
|
||||
})
|
||||
})
|
||||
|
||||
describe('and a key file is not present', function () {
|
||||
beforeEach(function () {
|
||||
fs.readdirSync.restore()
|
||||
fs.readdirSync = readdirSync
|
||||
})
|
||||
|
||||
it('should throw an error', function () {
|
||||
expect(findPrivateKey)
|
||||
.toThrow(Error, /missing private key for GitHub App/i)
|
||||
expect(findPrivateKey).toThrow(/missing private key for GitHub App/i)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
|
@ -1,19 +1,16 @@
|
|||
/* eslint prefer-arrow-callback: off */
|
||||
|
||||
const expect = require('expect')
|
||||
const resolve = require('../lib/resolver')
|
||||
|
||||
const stubPluginPath = require.resolve('./fixtures/plugin/stub-plugin')
|
||||
const basedir = process.cwd()
|
||||
|
||||
describe('resolver', function () {
|
||||
describe('resolver', () => {
|
||||
let stubResolver
|
||||
|
||||
beforeEach(function () {
|
||||
stubResolver = expect.createSpy().andReturn(stubPluginPath)
|
||||
beforeEach(() => {
|
||||
stubResolver = jest.fn().mockReturnValue(stubPluginPath)
|
||||
})
|
||||
|
||||
it('loads the module at the resolved path', function () {
|
||||
it('loads the module at the resolved path', () => {
|
||||
const module = resolve('foo', {resolver: stubResolver})
|
||||
expect(module).toBe(require(stubPluginPath))
|
||||
expect(stubResolver).toHaveBeenCalledWith('foo', {basedir})
|
||||
|
|
|
@ -1,11 +1,9 @@
|
|||
const expect = require('expect')
|
||||
const Context = require('../lib/context')
|
||||
const createRobot = require('../lib/robot')
|
||||
|
||||
describe('Robot', function () {
|
||||
let robot
|
||||
let event
|
||||
let spy
|
||||
|
||||
beforeEach(function () {
|
||||
robot = createRobot()
|
||||
|
@ -18,19 +16,17 @@ describe('Robot', function () {
|
|||
installation: {id: 1}
|
||||
}
|
||||
}
|
||||
|
||||
spy = expect.createSpy()
|
||||
})
|
||||
|
||||
describe('constructor', () => {
|
||||
it('takes a logger', () => {
|
||||
const logger = {
|
||||
trace: expect.createSpy(),
|
||||
debug: expect.createSpy(),
|
||||
info: expect.createSpy(),
|
||||
warn: expect.createSpy(),
|
||||
error: expect.createSpy(),
|
||||
fatal: expect.createSpy()
|
||||
trace: jest.fn(),
|
||||
debug: jest.fn(),
|
||||
info: jest.fn(),
|
||||
warn: jest.fn(),
|
||||
error: jest.fn(),
|
||||
fatal: jest.fn()
|
||||
}
|
||||
robot = createRobot({logger})
|
||||
|
||||
|
@ -41,16 +37,18 @@ describe('Robot', function () {
|
|||
|
||||
describe('on', function () {
|
||||
it('calls callback when no action is specified', async function () {
|
||||
const spy = jest.fn()
|
||||
robot.on('test', spy)
|
||||
|
||||
expect(spy).toNotHaveBeenCalled()
|
||||
expect(spy).toHaveBeenCalledTimes(0)
|
||||
await robot.receive(event)
|
||||
expect(spy).toHaveBeenCalled()
|
||||
expect(spy.calls[0].arguments[0]).toBeA(Context)
|
||||
expect(spy.calls[0].arguments[0].payload).toBe(event.payload)
|
||||
expect(spy.mock.calls[0][0]).toBeInstanceOf(Context)
|
||||
expect(spy.mock.calls[0][0].payload).toBe(event.payload)
|
||||
})
|
||||
|
||||
it('calls callback with same action', async function () {
|
||||
const spy = jest.fn()
|
||||
robot.on('test.foo', spy)
|
||||
|
||||
await robot.receive(event)
|
||||
|
@ -58,13 +56,15 @@ describe('Robot', function () {
|
|||
})
|
||||
|
||||
it('does not call callback with different action', async function () {
|
||||
const spy = jest.fn()
|
||||
robot.on('test.nope', spy)
|
||||
|
||||
await robot.receive(event)
|
||||
expect(spy).toNotHaveBeenCalled()
|
||||
expect(spy).toHaveBeenCalledTimes(0)
|
||||
})
|
||||
|
||||
it('calls callback with *', async function () {
|
||||
const spy = jest.fn()
|
||||
robot.on('*', spy)
|
||||
|
||||
await robot.receive(event)
|
||||
|
@ -80,17 +80,18 @@ describe('Robot', function () {
|
|||
}
|
||||
}
|
||||
|
||||
const spy = jest.fn()
|
||||
robot.on(['test.foo', 'arrayTest.bar'], spy)
|
||||
|
||||
await robot.receive(event)
|
||||
await robot.receive(event2)
|
||||
expect(spy.calls.length).toEqual(2)
|
||||
expect(spy.mock.calls.length).toEqual(2)
|
||||
})
|
||||
})
|
||||
|
||||
describe('receive', () => {
|
||||
it('delivers the event', async () => {
|
||||
const spy = expect.createSpy()
|
||||
const spy = jest.fn()
|
||||
robot.on('test', spy)
|
||||
|
||||
await robot.receive(event)
|
||||
|
@ -99,7 +100,7 @@ describe('Robot', function () {
|
|||
})
|
||||
|
||||
it('waits for async events to resolve', async () => {
|
||||
const spy = expect.createSpy()
|
||||
const spy = jest.fn()
|
||||
|
||||
robot.on('test', () => {
|
||||
return new Promise(resolve => {
|
||||
|
@ -134,7 +135,7 @@ describe('Robot', function () {
|
|||
|
||||
beforeEach(() => {
|
||||
error = new Error('testing')
|
||||
robot.log.error = expect.createSpy()
|
||||
robot.log.error = jest.fn()
|
||||
})
|
||||
|
||||
it('logs errors thrown from handlers', async () => {
|
||||
|
@ -148,7 +149,7 @@ describe('Robot', function () {
|
|||
// Expected
|
||||
}
|
||||
|
||||
const arg = robot.log.error.calls[0].arguments[0]
|
||||
const arg = robot.log.error.mock.calls[0][0]
|
||||
expect(arg.err).toBe(error)
|
||||
expect(arg.event).toBe(event)
|
||||
})
|
||||
|
@ -163,7 +164,7 @@ describe('Robot', function () {
|
|||
}
|
||||
|
||||
expect(robot.log.error).toHaveBeenCalled()
|
||||
const arg = robot.log.error.calls[0].arguments[0]
|
||||
const arg = robot.log.error.mock.calls[0][0]
|
||||
expect(arg.err).toBe(error)
|
||||
expect(arg.event).toBe(event)
|
||||
})
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
const expect = require('expect')
|
||||
const serializers = require('../lib/serializers')
|
||||
|
||||
describe('serializers', () => {
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
const expect = require('expect')
|
||||
const request = require('supertest')
|
||||
const createServer = require('../lib/server')
|
||||
|
||||
|
@ -7,7 +6,7 @@ describe('server', function () {
|
|||
let webhook
|
||||
|
||||
beforeEach(() => {
|
||||
webhook = expect.createSpy().andCall((req, res, next) => next())
|
||||
webhook = jest.fn((req, res, next) => next())
|
||||
server = createServer(webhook)
|
||||
|
||||
// Error handler to avoid printing logs
|
||||
|
@ -24,7 +23,7 @@ describe('server', function () {
|
|||
|
||||
describe('webhook handler', () => {
|
||||
it('should 500 on a webhook error', () => {
|
||||
webhook.andCall((req, res, callback) => callback(new Error('webhook error')))
|
||||
webhook.mockImplementation((req, res, callback) => callback(new Error('webhook error')))
|
||||
return request(server).post('/').expect(500)
|
||||
})
|
||||
})
|
||||
|
|
Loading…
Reference in New Issue