Skip to content

Commit

Permalink
test: improve semantic detection and add a real test suite (#16)
Browse files Browse the repository at this point in the history
* remove old tests from WIP-bot

* update deps and test config

* ignore jest test coverage

* start writing real tests

* make the readme more better

* remove unused deps and update package description

* update package lock

* tests are passing yay

* remove unneeded request headers from POST mock

* just one mock

* add more tests

* nothing
  • Loading branch information
zeke authored Jul 16, 2018
1 parent 0867999 commit dde5445
Show file tree
Hide file tree
Showing 9 changed files with 2,270 additions and 1,350 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
.env
.npmrc
coverage
node_modules
private-key.pem
33 changes: 28 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,36 @@
# Semantic Pull Requests

> Status check that ensures your pull requests have semantic titles
> GitHub status check that ensures your pull requests follow the Conventional Commits spec
🚧 works, but not ready for use 🚧
Using [semantic-release](https://github.com/semantic-release/semantic-release)
and [conventional commit messages](https://conventionalcommits.org)? Install this
[Probot](https://probot.github.io/) app
on your repos to ensure your pull requests are semantic before you merge them.

Check out [the issues](https://github.com/probot/semantic-pull-requests/issues)
to see what remains to make this probot production-ready.
## How it works

<img src="https://user-images.githubusercontent.com/2289/36702733-27eb8a82-1b0d-11e8-946f-8697af711592.png">
Take this PR for example. None of the commit messages are semantic, nor is the PR title, so the status remains yellow:


<img width="580" alt="screen shot 2018-07-14 at 6 22 58 pm" src="https://user-images.githubusercontent.com/2289/42729630-11370698-8793-11e8-922c-db2308e0e98e.png">

<img width="791" alt="screen shot 2018-07-14 at 6 22 10 pm" src="https://user-images.githubusercontent.com/2289/42729629-110812b6-8793-11e8-8c35-188b0952fd66.png">

---

Edit the PR title by adding a semantic prefix like `fix: ` or `feat: ` or any other
[conventional commit type](https://github.com/commitizen/conventional-commit-types/blob/master/index.json). Now use `Squash and Merge` to squash the branch onto master and write a standardized commit message while doing so:

---

<img width="613" alt="screen shot 2018-07-14 at 6 23 11 pm" src="https://user-images.githubusercontent.com/2289/42729631-1164bd36-8793-11e8-9bf9-d2eeb9dd06e1.png">

<img width="785" alt="screen shot 2018-07-14 at 6 23 23 pm" src="https://user-images.githubusercontent.com/2289/42729632-11980b32-8793-11e8-9f8d-bf16c707f542.png">


## Installation

👉 [github.com/apps/semantic-pull-requests](https://github.com/apps/semantic-pull-requests)

## License

Expand Down
100 changes: 100 additions & 0 deletions __tests__/handle-pull-request-change.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
const handlePullRequestChange = require('../lib/handle-pull-request-change')
const nock = require('nock')
const github = require('@octokit/rest')()

// prevent all network activity to ensure mocks are used
nock.disableNetConnect()

describe('handlePullRequestChange', () => {
test('it is a function', () => {
expect(typeof handlePullRequestChange).toBe('function')
})

test('sets `pending` status if PR has no semantic commits and no semantic title', async () => {
const context = buildContext()
context.payload.pull_request.title = 'do a thing'
const commits = [
{commit: {message: 'fix something'}},
{commit: {message: 'fix something else'}}
]
const expectedBody = {
state: 'pending',
target_url: 'https://github.com/probot/semantic-pull-requests',
description: 'add semantic commit or PR title',
context: 'Semantic Pull Request'
}

const mock = nock('https://api.github.com')
.get('/repos/sally/project-x/pulls/123/commits')
.reply(200, commits)
.post('/repos/sally/project-x/statuses/abcdefg', expectedBody)
.reply(200)

await handlePullRequestChange(context)
expect(mock.isDone()).toBe(true)
})

test('sets `success` status if PR has a semantic title', async () => {
const context = buildContext()
context.payload.pull_request.title = 'fix: bananas'
const expectedBody = {
state: 'success',
description: 'good to go',
target_url: 'https://github.com/probot/semantic-pull-requests',
context: 'Semantic Pull Request'
}

// since the title is semantic, no GET request for commits is needed
const mock = nock('https://api.github.com')
.post('/repos/sally/project-x/statuses/abcdefg', expectedBody)
.reply(200)

await handlePullRequestChange(context)
expect(mock.isDone()).toBe(true)
})

test('allows `build:` as a prefix', async () => {
const context = buildContext()
context.payload.pull_request.title = 'build: publish to npm'
const expectedBody = {
state: 'success',
description: 'good to go',
target_url: 'https://github.com/probot/semantic-pull-requests',
context: 'Semantic Pull Request'
}

// since the title is semantic, no GET request for commits is needed
const mock = nock('https://api.github.com')
.post('/repos/sally/project-x/statuses/abcdefg', expectedBody)
.reply(200)

await handlePullRequestChange(context)
expect(mock.isDone()).toBe(true)
})
})

function buildContext (overrides) {
const defaults = {
log: () => { /* no-op */ },

// an instantiated GitHub client like the one probot provides
github: github,

// context.repo() is a probot convenience function
repo: (obj = {}) => {
return Object.assign({owner: 'sally', repo: 'project-x'}, obj)
},

payload: {
pull_request: {
number: 123,
title: 'do a thing',
head: {
sha: 'abcdefg'
}
}
}
}

return Object.assign({}, defaults, overrides)
}
15 changes: 15 additions & 0 deletions __tests__/is-semantic-message.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
const isSemanticMessage = require('../lib/is-semantic-message')

test('returns true when messages are semantic', () => {
expect(isSemanticMessage('fix: something')).toBe(true)
})

test('allows parenthetical scope following the type', () => {
expect(isSemanticMessage('fix(subsystem): something')).toBe(true)
})

test('returns false on bad input', () => {
expect(isSemanticMessage('')).toBe(false)
expect(isSemanticMessage(null)).toBe(false)
expect(isSemanticMessage('non-semantic commit message')).toBe(false)
})
41 changes: 18 additions & 23 deletions lib/handle-pull-request-change.js
Original file line number Diff line number Diff line change
@@ -1,33 +1,28 @@
module.exports = handlePullRequestChange

const simpleCommitMessage = require('simple-commit-message')

async function handlePullRequestChange (context) {
const {title, html_url: htmlUrl, head} = context.payload.pull_request
const isSemantic = isSemanticMessage(title) || await commitsAreSemantic(context)
const status = isSemantic ? 'success' : 'pending'

console.log(`Updating PR "${title}" (${htmlUrl}): ${status}`)

context.github.repos.createStatus(context.repo({
sha: head.sha,
state: status,
target_url: 'https://github.com/probot/semantic-pull-requests',
description: isSemantic ? 'good to go' : 'add semantic commit or PR title' ,
context: 'Semantic Pull Request'
}))
}

function isSemanticMessage(message) {
return simpleCommitMessage.validate(message)
}
const isSemanticMessage = require('./is-semantic-message')

async function commitsAreSemantic (context) {
const commits = await context.github.pullRequests.getCommits(context.repo({
number: context.payload.pull_request.number
}))

console.log('commits[0]', commits[0])
return commits.data
.map(element => element.commit)
.some(commit => isSemanticMessage(commit.message))
}

return commits.data.map(element => element.commit.message).some(isSemanticMessage)
async function handlePullRequestChange (context) {
const {title, head} = context.payload.pull_request
const isSemantic = isSemanticMessage(title) || await commitsAreSemantic(context)
const state = isSemantic ? 'success' : 'pending'
const status = {
sha: head.sha,
state,
target_url: 'https://github.com/probot/semantic-pull-requests',
description: isSemantic ? 'good to go' : 'add semantic commit or PR title',
context: 'Semantic Pull Request'
}
const result = await context.github.repos.createStatus(context.repo(status))
return result
}
11 changes: 11 additions & 0 deletions lib/is-semantic-message.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
const commitTypes = Object.keys(require('conventional-commit-types').types)
const parseCommitMessage = require('parse-commit-message').parse

module.exports = function isSemanticMessage (message) {
try {
const {type} = parseCommitMessage(message)
return commitTypes.includes(type)
} catch (e) {
return false
}
}
Loading

0 comments on commit dde5445

Please sign in to comment.