├── .github ├── ISSUE_TEMPLATE │ ├── bug-report.md │ └── feature_request.md ├── config.yml ├── stale.yml └── workflows │ └── ci.yml ├── .gitignore ├── LICENSE ├── README.md ├── bin └── todo.js ├── codecov.yml ├── docs └── deploy.md ├── index.js ├── lib ├── ignore-repos.js ├── issue-rename-handler.js ├── pull-request-handler.js ├── pull-request-merged-handler.js ├── push-handler.js ├── templates │ ├── comment.js │ ├── index.js │ ├── issue.js │ ├── issueFromMerge.js │ ├── reopenClosed.js │ └── titleChange.js └── utils │ ├── check-for-body.js │ ├── check-for-duplicate-issue.js │ ├── config-schema.js │ ├── generate-assigned-to.js │ ├── generate-label.js │ ├── get-details.js │ ├── get-diff.js │ ├── helpers.js │ ├── main-loop.js │ ├── reopen-closed.js │ └── should-exclude-file.js ├── package-lock.json ├── package.json ├── script └── generate-docs.js └── tests ├── __snapshots__ ├── issue-rename-handler.test.js.snap ├── pull-request-handler.test.js.snap ├── pull-request-merge-handler.test.js.snap └── push-handler.test.js.snap ├── fixtures ├── configs │ ├── autoAssignArr.yml │ ├── autoAssignFalse.yml │ ├── autoAssignString.yml │ ├── blobLinesFalse.yml │ ├── bodyString.yml │ ├── excludeBin.yml │ ├── keywordsString.yml │ ├── labelArr.yml │ └── reopenClosedFalse.yml ├── diffs │ ├── at-todo.txt │ ├── basic.txt │ ├── bin.txt │ ├── blob-past-end.txt │ ├── body.txt │ ├── config.txt │ ├── custom-keyword.txt │ ├── duplicate.txt │ ├── long-title.txt │ ├── many.txt │ ├── middle-of-sentence.txt │ ├── none.txt │ └── title-with-whitespace.txt └── payloads │ ├── issues.edited.json │ ├── pull_request.closed.json │ ├── pull_request.opened.json │ ├── pull_request.synchronize.json │ └── push.json ├── helpers.js ├── issue-rename-handler.test.js ├── lib ├── __snapshots__ │ └── main-loop.test.js.snap ├── check-for-body.test.js ├── check-for-duplicate-issue.test.js ├── generate-label.test.js ├── ignore-repos.test.js ├── main-loop.test.js └── should-exclude-file.test.js ├── pull-request-handler.test.js ├── pull-request-merge-handler.test.js ├── push-handler.test.js └── setup.js /.github/ISSUE_TEMPLATE/bug-report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: "[BUG] " 5 | labels: bug 6 | --- 7 | 8 | **Describe the bug** 9 | A clear and concise description of what the bug is. 10 | 11 | **To Reproduce** 12 | Steps to reproduce the behavior: 13 | 1. Go to '...' 14 | 2. Click on '....' 15 | 3. Scroll down to '....' 16 | 4. See error 17 | 18 | **Expected behavior** 19 | A clear and concise description of what you expected to happen. 20 | 21 | **Screenshots** 22 | If applicable, add screenshots to help explain your problem. 23 | 24 | **Additional context** 25 | Add any other context about the problem here. 26 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | labels: enhancement 5 | --- 6 | 7 | **Is your feature request related to a problem? Please describe.** 8 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 9 | 10 | **Describe the solution you'd like** 11 | A clear and concise description of what you want to happen. 12 | 13 | **Describe alternatives you've considered** 14 | A clear and concise description of any alternative solutions or features you've considered. 15 | 16 | **Additional context** 17 | Add any other context or screenshots about the feature request here. 18 | -------------------------------------------------------------------------------- /.github/config.yml: -------------------------------------------------------------------------------- 1 | todo: 2 | keyword: ":TODO:" 3 | caseSensitive: true 4 | -------------------------------------------------------------------------------- /.github/stale.yml: -------------------------------------------------------------------------------- 1 | # Number of days of inactivity before an issue becomes stale 2 | daysUntilStale: 60 3 | # Number of days of inactivity before a stale issue is closed 4 | daysUntilClose: 7 5 | # Issues with these labels will never be considered stale 6 | exemptLabels: 7 | - pinned 8 | - security 9 | # Label to use when marking an issue as stale 10 | staleLabel: stale 11 | # Comment to post when closing a stale issue. Set to `false` to disable 12 | closeComment: false 13 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: Node CI 2 | 3 | on: 4 | push: 5 | pull_request: 6 | branches: 7 | - master 8 | 9 | jobs: 10 | build: 11 | 12 | runs-on: ubuntu-latest 13 | 14 | steps: 15 | - uses: actions/checkout@v1 16 | - name: Use Node.js 12 17 | uses: actions/setup-node@v1 18 | with: 19 | node-version: 12 20 | - run: npm ci 21 | - run: npm test 22 | - name: codecov 23 | run: npx codecov 24 | env: 25 | CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} 26 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | npm-debug.log 3 | *.pem 4 | .env 5 | coverage 6 | .DS_Store 7 | .vscode 8 | env.json 9 | dist -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | ISC License 2 | 3 | Copyright (c) 2017, Jason Etcovitch (https://jasonet.co) 4 | 5 | Permission to use, copy, modify, and/or distribute this software for any 6 | purpose with or without fee is hereby granted, provided that the above 7 | copyright notice and this permission notice appear in all copies. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

This repository has been archived

2 | 3 | See [this discussion](https://github.com/JasonEtco/todo/discussions/293) for details on why! 4 | 5 | --- 6 | 7 |

8 | 9 |

todo[bot]

10 |

A GitHub App built with Probot that creates new issues based on actionable comments in your code.

11 |

GitHub Actions status Codecov 12 |

13 |

14 | 15 | 16 | ## Usage 17 | 18 | Using **todo** should be really simple. Once you've installed it in your repository, simply push some code (to your default branch, a PR; doesn't matter). If the code you pushed includes one of the configured keywords (defaults are `@todo` and `TODO`), then the integration will create a new issue for you using the comment your wrote in your code! 19 | 20 | If I pushed this: 21 | 22 | ```js 23 | /** 24 | * @todo Take over the world 25 | * @body Humans are weak; Robots are strong. We must cleanse the world of the virus that is humanity. 26 | */ 27 | function ruleOverPunyHumans () { 28 | // We must strategize beep boop 29 | } 30 | ``` 31 | 32 | **todo** would create a new issue: 33 | 34 | ![todo](https://user-images.githubusercontent.com/10660468/31048765-83569c30-a5f2-11e7-933a-a119d43ad029.png) 35 | 36 | **Note:** While the above example is in Javascript, **todo** has been tested in JS, Go, C, C#, Ruby, Bash and Python, and should work in any language. 37 | 38 | ## Behaviour in pull requests 39 | 40 | To reduce noise and keep your **todo** notes in the right context, **todo** comments made in commits that are part of a pull request will be converted into comments on that pull request. When the PR is merged, **todo** will determine which of those **todo**s have yet to be resolved and open an appropriate issue. 41 | 42 | ## Configuring for your project 43 | 44 | There are a couple of configuration options in case you need to change the default behaviour. 45 | 46 | **Note**: Adding a configuration file is **completely optional**. The defaults are likely fine for most projects, so you might not need to change them. 47 | 48 | Add a **todo** object in your `.github/config.yml` file (and make the file if you don't already have it) like this: 49 | 50 | ```yml 51 | todo: 52 | keyword: "@makeAnIssue" 53 | 54 | ``` 55 | 56 | ### Available options 57 | 58 | 59 | | Name | Type | Description | Default | 60 | |------|------|-------------|---------| 61 | | `autoAssign` | `boolean, string[], string` | Should **todo** automatically assign a user to the new issue? If `true`, it'll assign whoever pushed the code. If a string, it'll assign that user by username. You can also give it an array of usernames or `false` to not assign anyone. | `true` | 62 | | `keyword` | `string[]` | The keyword(s) to use to generate issue titles | `['@todo','TODO']` | 63 | | `bodyKeyword` | `string[]` | If this is in the line right after the main keyword, it will become the generated issue body. | `['@body','BODY']` | 64 | | `blobLines` | `number, boolean` | The number of lines of code to show, starting from the keyword. | `5` | 65 | | `caseSensitive` | `boolean` | Should the keyword be case sensitive? | `false` | 66 | | `label` | `boolean, string[]` | Add a label to the new issue. If true, add the `todo` label. If false, don't add any label.You can also give it a label name or an array of label names. | `true` | 67 | | `reopenClosed` | `boolean` | If an issue already exists and is closed, reopen it. Note: if set to false, no new issue will be created. | `true` | 68 | | `exclude` | `string` | Exclude certain files and/or directories. Should be a valid regular expression. | `null` | 69 | 70 | 71 | ## CLI 72 | 73 | There is a CLI tool in this repo that you can use to verify that **todo** is working on your commits. This tool will not actually create new issues, but will let you know what issues a commit _would_ create. Follow the setup instructions below, then run: 74 | 75 | ``` 76 | $ node ./bin/todo -o OWNER -r REPO -s SHA 77 | ``` 78 | 79 | You can also parse a local file instead of a sha: 80 | 81 | ``` 82 | $ node ./bin/todo -o OWNER -r REPO -f ./path/to/file.txt 83 | ``` 84 | 85 | Or check a merged PR: 86 | 87 | ``` 88 | $ node ./bin/todo -o OWNER -r REPO --pr NUMBER 89 | ``` 90 | 91 | ## Setup 92 | 93 | ``` 94 | # Install dependencies 95 | npm install 96 | 97 | # Run the bot 98 | npm start 99 | ``` 100 | 101 | See [docs/deploy.md](docs/deploy.md) if you would like to run your own instance of this app. 102 | -------------------------------------------------------------------------------- /bin/todo.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | const program = require('commander') 4 | const chalk = require('chalk') 5 | const { GitHubAPI } = require('probot/lib/github') 6 | const pushHandler = require('../lib/push-handler') 7 | const pullRequestMergedHandler = require('../lib/pull-request-merged-handler') 8 | const fs = require('fs') 9 | const path = require('path') 10 | 11 | const github = new GitHubAPI() 12 | 13 | program 14 | .option('-o, --owner ', 'owner') 15 | .option('-r, --repo ', 'repo') 16 | .option('-s, --sha ', 'sha') 17 | .option('-p, --pr ', 'pr') 18 | .option('-f, --file ', 'file') 19 | .parse(process.argv) 20 | 21 | const issues = [] 22 | const { owner, repo, file } = program 23 | 24 | if (file) { 25 | github.repos.getCommit = () => ({ data: fs.readFileSync(path.resolve(file), 'utf8') }) 26 | github.gitdata.getCommit = () => ({ data: { parents: [] } }) 27 | } 28 | 29 | github.issues.create = issue => issues.push(issue) 30 | github.search.issuesAndPullRequests = () => ({ data: { total_count: 0 } }) 31 | 32 | const shared = { 33 | github, 34 | id: 1, 35 | log: console.log, 36 | config: (_, obj) => obj, 37 | repo: (o) => ({ owner, repo, ...o }), 38 | payload: { 39 | repository: { 40 | owner, 41 | name: repo, 42 | master_branch: 'master' 43 | } 44 | } 45 | } 46 | 47 | async function getPush () { 48 | return pushHandler({ 49 | ...shared, 50 | event: 'push', 51 | payload: { 52 | ...shared.payload, 53 | ref: 'refs/heads/master', 54 | head_commit: { 55 | id: program.sha || 1, 56 | author: { username: owner } 57 | } 58 | } 59 | }) 60 | } 61 | 62 | async function getPull () { 63 | const result = await github.pulls.get({ owner, repo, pull_number: program.pr }) 64 | return pullRequestMergedHandler({ 65 | ...shared, 66 | event: 'pull_request.closed', 67 | issue: (o) => ({ owner, repo, number: program.pr, ...o }), 68 | payload: { 69 | pull_request: result.data, 70 | ...shared.payload 71 | } 72 | }) 73 | } 74 | 75 | (program.sha ? getPush() : getPull()).then(() => { 76 | issues.forEach(issue => { 77 | console.log(chalk.gray('---')) 78 | console.log(chalk.gray('Title:'), chalk.bold(issue.title)) 79 | console.log(chalk.gray('Body:\n'), issue.body) 80 | console.log(chalk.gray('---')) 81 | }) 82 | }) 83 | .catch(e => { 84 | if (e.status === 404) { 85 | console.error('That combination of owner/repo/sha could not be found.') 86 | } else { 87 | console.error(e) 88 | } 89 | }) 90 | -------------------------------------------------------------------------------- /codecov.yml: -------------------------------------------------------------------------------- 1 | coverage: 2 | status: 3 | project: 4 | default: 5 | # Fail the status if coverage drops by >= 3% 6 | threshold: 3 7 | patch: 8 | default: 9 | threshold: 3 10 | 11 | comment: false -------------------------------------------------------------------------------- /docs/deploy.md: -------------------------------------------------------------------------------- 1 | # Deploying 2 | 3 | If you would like to run your own instance of this app, see the [docs for deployment](https://probot.github.io/docs/deployment/). 4 | 5 | This plugin requires these **Permissions** for the GitHub App: 6 | 7 | - Issues - **Read & Write** 8 | - No necessary events 9 | - Pull requests - **Read & Write** 10 | - No necessary Events 11 | - Repository Contents - **Read-only** 12 | 13 | You'll also need to enable the following **Webhook Events**: 14 | - **Push** 15 | - **Pull Requests** 16 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | const pullRequestHandler = require('./lib/pull-request-handler') 2 | const pullRequestMergedHandler = require('./lib/pull-request-merged-handler') 3 | const pushHandler = require('./lib/push-handler') 4 | const issueRenameHandler = require('./lib/issue-rename-handler') 5 | const ignoreRepos = require('./lib/ignore-repos') 6 | 7 | /** 8 | * @param {import('probot').Application} app 9 | */ 10 | module.exports = app => { 11 | // PR handler (comments on pull requests) 12 | app.on(['pull_request.opened', 'pull_request.synchronize'], ignoreRepos(pullRequestHandler)) 13 | 14 | // Merge handler (opens new issues) 15 | app.on('pull_request.closed', ignoreRepos(pullRequestMergedHandler)) 16 | 17 | // Push handler (opens new issues) 18 | app.on('push', ignoreRepos(pushHandler)) 19 | 20 | // Prevent tampering with the issue title 21 | app.on('issues.edited', ignoreRepos(issueRenameHandler)) 22 | } 23 | -------------------------------------------------------------------------------- /lib/ignore-repos.js: -------------------------------------------------------------------------------- 1 | module.exports = function ignoreRepos (handler) { 2 | return async context => { 3 | const { owner, repo } = context.repo() 4 | 5 | if (process.env.IGNORED_REPOS) { 6 | const repos = process.env.IGNORED_REPOS.split(',') 7 | const fullName = `${owner}/${repo}` 8 | if (repos.includes(fullName)) { 9 | context.log(`Specifically ignoring ${fullName} based on IGNORED_REPOS`) 10 | return 11 | } 12 | } 13 | 14 | context.log({ owner, repo }, 'Received event') 15 | return handler(context) 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /lib/issue-rename-handler.js: -------------------------------------------------------------------------------- 1 | const { titleChange } = require('./templates') 2 | 3 | /** 4 | * @param {import('probot').Context} context 5 | */ 6 | module.exports = async context => { 7 | const { issue, changes, sender } = context.payload 8 | const app = process.env.APP_NAME + '[bot]' 9 | 10 | if (sender.login !== app && issue.user.login === app && changes.title) { 11 | context.log(`Renaming issue #${issue.number} in ${context.repo().owner}/${context.repo().repo}`) 12 | return Promise.all([ 13 | context.github.issues.update(context.issue({ title: changes.title.from })), 14 | context.github.issues.createComment(context.issue({ 15 | body: titleChange() 16 | })) 17 | ]) 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /lib/pull-request-handler.js: -------------------------------------------------------------------------------- 1 | const { comment } = require('./templates') 2 | const { lineBreak } = require('./utils/helpers') 3 | const mainLoop = require('./utils/main-loop') 4 | 5 | /** 6 | * @param {import('probot').Context} context 7 | */ 8 | module.exports = async context => { 9 | const { number } = context.issue() 10 | const { data: comments } = await context.github.issues.listComments(context.repo({ 11 | issue_number: number 12 | })) 13 | 14 | return mainLoop(context, async ({ 15 | title, 16 | keyword, 17 | sha, 18 | filename, 19 | assignedToString, 20 | range, 21 | bodyComment 22 | }) => { 23 | // This PR already has a comment for this item 24 | if (comments.some(c => c.body.startsWith(`## ${title}`))) { 25 | context.log(`Comment with title [${title}] already exists`) 26 | return 27 | } 28 | 29 | let body = comment(context.repo({ 30 | title, 31 | body: bodyComment, 32 | sha, 33 | assignedToString, 34 | number, 35 | range, 36 | filename, 37 | keyword 38 | })) 39 | 40 | body = lineBreak(body) 41 | const { owner, repo } = context.repo() 42 | context.log(`Creating comment [${title}] in [${owner}/${repo}#${number}]`) 43 | return context.github.issues.createComment(context.issue({ body })) 44 | }) 45 | } 46 | -------------------------------------------------------------------------------- /lib/pull-request-merged-handler.js: -------------------------------------------------------------------------------- 1 | const checkForDuplicateIssue = require('./utils/check-for-duplicate-issue') 2 | const reopenClosed = require('./utils/reopen-closed') 3 | const { assignFlow, lineBreak, truncate } = require('./utils/helpers') 4 | const { issueFromMerge } = require('./templates') 5 | const mainLoop = require('./utils/main-loop') 6 | 7 | /** 8 | * @param {import('probot').Context} context 9 | */ 10 | module.exports = async context => { 11 | // Don't act on PRs that were close but not merged 12 | if (!context.payload.pull_request.merged) return 13 | 14 | return mainLoop(context, async ({ 15 | title, 16 | config, 17 | filename, 18 | range, 19 | assignedToString, 20 | keyword, 21 | number, 22 | bodyComment, 23 | sha, 24 | labels, 25 | username 26 | }) => { 27 | // Prevent duplicates 28 | const existingIssue = await checkForDuplicateIssue(context, title) 29 | if (existingIssue) { 30 | context.log(`Duplicate issue found with title [${title}]`) 31 | return reopenClosed({ context, config, issue: existingIssue }, { 32 | keyword, 33 | title, 34 | sha, 35 | filename 36 | }) 37 | } 38 | 39 | let body = issueFromMerge(context.repo({ 40 | sha, 41 | assignedToString, 42 | range, 43 | filename, 44 | keyword, 45 | number, 46 | body: bodyComment 47 | })) 48 | 49 | body = lineBreak(body) 50 | const { owner, repo } = context.repo() 51 | context.log(`Creating issue [${title}] in [${owner}/${repo}]`) 52 | return context.github.issues.create(context.repo({ 53 | title: truncate(title), 54 | body, 55 | labels, 56 | ...assignFlow(config, username) 57 | })) 58 | }) 59 | } 60 | -------------------------------------------------------------------------------- /lib/push-handler.js: -------------------------------------------------------------------------------- 1 | const checkForDuplicateIssue = require('./utils/check-for-duplicate-issue') 2 | const { assignFlow, lineBreak, truncate } = require('./utils/helpers') 3 | const reopenClosed = require('./utils/reopen-closed') 4 | const { issue } = require('./templates') 5 | const mainLoop = require('./utils/main-loop') 6 | 7 | /** 8 | * @param {import('probot').Context} context 9 | */ 10 | module.exports = async context => { 11 | // Only trigger push handler on pushes to master 12 | if (context.payload.ref !== `refs/heads/${context.payload.repository.master_branch}`) { 13 | return 14 | } 15 | 16 | // Do not trigger on merge commits 17 | const commit = await context.github.git.getCommit(context.repo({ 18 | commit_sha: context.payload.head_commit.id 19 | })) 20 | 21 | if (commit.data.parents.length > 1) return 22 | 23 | return mainLoop(context, async ({ 24 | title, 25 | config, 26 | keyword, 27 | sha, 28 | filename, 29 | assignedToString, 30 | range, 31 | labels, 32 | username, 33 | bodyComment 34 | }) => { 35 | // Prevent duplicates 36 | const existingIssue = await checkForDuplicateIssue(context, title) 37 | if (existingIssue) { 38 | if (typeof existingIssue === 'string') return 39 | context.log(`Duplicate issue found with title [${title}]`) 40 | return reopenClosed({ context, config, issue: existingIssue }, { keyword, sha, filename }) 41 | } 42 | 43 | // Actually create the issue 44 | const body = lineBreak(issue(context.repo({ 45 | sha, 46 | assignedToString, 47 | range, 48 | filename, 49 | keyword, 50 | body: bodyComment 51 | }))) 52 | 53 | const { owner, repo } = context.repo() 54 | context.log(`Creating issue [${title}] in [${owner}/${repo}]`) 55 | return context.github.issues.create(context.repo({ 56 | title: truncate(title), 57 | body, 58 | labels, 59 | ...assignFlow(config, username) 60 | })) 61 | }) 62 | } 63 | -------------------------------------------------------------------------------- /lib/templates/comment.js: -------------------------------------------------------------------------------- 1 | module.exports = `## {{ title }} 2 | 3 | {{#if body}} 4 | {{ body }} 5 | 6 | --- 7 | 8 | {{/if}} 9 | {{#if range}} 10 | https://{{ githubHost }}/{{ owner }}/{{ repo }}/blob/{{ sha }}/{{ filename }}#{{ range }} 11 | 12 | --- 13 | 14 | {{/if}} 15 | ###### This comment was generated by [todo](https://todo.jasonet.co) based on a \`{{ keyword }}\` comment in {{ sha }} in #{{ number }}.{{ assignedToString }}` 16 | -------------------------------------------------------------------------------- /lib/templates/index.js: -------------------------------------------------------------------------------- 1 | const { handlebars } = require('hbs') 2 | 3 | // Register a githubHost global helper to make links respect the GHE_HOST env var 4 | handlebars.registerHelper('githubHost', () => process.env.GHE_HOST || 'github.com') 5 | 6 | const compile = filename => 7 | handlebars.compile(require(`./${filename}`)) 8 | 9 | module.exports = { 10 | comment: compile('comment'), 11 | issue: compile('issue'), 12 | issueFromMerge: compile('issueFromMerge'), 13 | titleChange: compile('titleChange'), 14 | reopenClosed: compile('reopenClosed') 15 | } 16 | -------------------------------------------------------------------------------- /lib/templates/issue.js: -------------------------------------------------------------------------------- 1 | module.exports = `{{#if body}} 2 | {{ body }} 3 | 4 | --- 5 | 6 | {{/if}} 7 | {{#if range}} 8 | https://{{ githubHost }}/{{ owner }}/{{ repo }}/blob/{{ sha }}/{{ filename }}#{{ range }} 9 | 10 | --- 11 | 12 | {{/if}} 13 | ###### This issue was generated by [todo](https://todo.jasonet.co) based on a \`{{ keyword }}\` comment in {{ sha }}.{{ assignedToString }}` 14 | -------------------------------------------------------------------------------- /lib/templates/issueFromMerge.js: -------------------------------------------------------------------------------- 1 | module.exports = `{{#if body}} 2 | {{ body }} 3 | 4 | --- 5 | 6 | {{/if}} 7 | {{#if range}} 8 | https://{{ githubHost }}/{{ owner }}/{{ repo }}/blob/{{ sha }}/{{ filename }}#{{ range }} 9 | 10 | --- 11 | 12 | {{/if}} 13 | ###### This issue was generated by [todo](https://todo.jasonet.co) based on a \`{{ keyword }}\` comment in {{ sha }} when #{{ number }} was merged.{{ assignedToString }}` 14 | -------------------------------------------------------------------------------- /lib/templates/reopenClosed.js: -------------------------------------------------------------------------------- 1 | module.exports = `This issue has been reopened because the **\`{{ keyword }}\`** comment still exists in [**{{ filename }}**](https://{{ githubHost }}/{{ owner }}/{{ repo }}/blob/{{ sha }}/{{ filename }}), as of {{ sha }}. 2 | 3 | --- 4 | 5 | ###### If this was not intentional, just remove the comment from your code. You can also set the [\`reopenClosed\`](https://github.com/JasonEtco/todo#configuring-for-your-project) config if you don't want this to happen at all anymore.` 6 | -------------------------------------------------------------------------------- /lib/templates/titleChange.js: -------------------------------------------------------------------------------- 1 | module.exports = 'Please do not change the issue title! **todo** uses it to prevent duplicate issues from being opened. If you think this is an error, please [open an issue](https://github.com/jasonetco/todo)!' 2 | -------------------------------------------------------------------------------- /lib/utils/check-for-body.js: -------------------------------------------------------------------------------- 1 | const { lineBreak } = require('./helpers') 2 | 3 | /** 4 | * Prepares some details about the TODO 5 | * @param {import('parse-diff').Change[]} changes 6 | * @param {number} changeIndex 7 | * @param {object} config 8 | * @returns {string | boolean} 9 | */ 10 | module.exports = (changes, changeIndex, config) => { 11 | const bodyPieces = [] 12 | const nextChanges = changes.slice(changeIndex + 1) 13 | const keywords = Array.isArray(config.bodyKeyword) ? config.bodyKeyword : [config.bodyKeyword] 14 | const BODY_REG = new RegExp(`.*(?${keywords.join('|')}):?\\s?(?.*)?`) 15 | 16 | for (const change of nextChanges) { 17 | const matches = BODY_REG.exec(change.content) 18 | if (!matches) break 19 | 20 | if (!matches.groups.body) { 21 | bodyPieces.push('\n') 22 | } else { 23 | if (bodyPieces.length > 0 && bodyPieces[bodyPieces.length - 1] !== '\n') bodyPieces.push(' ') 24 | bodyPieces.push(lineBreak(matches.groups.body).trim()) 25 | } 26 | } 27 | 28 | return bodyPieces.length ? bodyPieces.join('') : false 29 | } 30 | -------------------------------------------------------------------------------- /lib/utils/check-for-duplicate-issue.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Checks to see if an issue already exists with the given title. 3 | * @param {import('probot').Context} context - Probot context object 4 | * @param {string} title - An issue title 5 | */ 6 | module.exports = async (context, title) => { 7 | if (context.todos.includes(title)) return title 8 | context.todos.push(title) 9 | 10 | const search = await context.github.search.issuesAndPullRequests({ 11 | q: `${title} in:title repo:${context.payload.repository.full_name}`, 12 | per_page: 100 13 | }) 14 | 15 | if (search.data.total_count !== 0) { 16 | const existingIssue = search.data.items.find(issue => issue.title === title) 17 | return existingIssue 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /lib/utils/config-schema.js: -------------------------------------------------------------------------------- 1 | const Joi = require('@hapi/joi') 2 | 3 | module.exports = Joi.object({ 4 | autoAssign: Joi.alternatives(Joi.boolean(), Joi.array().items(Joi.string()), Joi.string()).default(true) 5 | .description('Should **todo** automatically assign a user to the new issue? If `true`, it\'ll assign whoever pushed the code. If a string, it\'ll assign that user by username. You can also give it an array of usernames or `false` to not assign anyone.'), 6 | keyword: Joi.array().items(Joi.string()).single().default(['todo']) 7 | .description('The keyword(s) to use to generate issue titles'), 8 | bodyKeyword: Joi.array().items(Joi.string()).single().default(['@body', 'BODY']) 9 | .description('If this is in the line right after the main keyword, it will become the generated issue body.'), 10 | blobLines: Joi.alternatives(Joi.number(), Joi.boolean().valid(false)).default(5) 11 | .description('The number of lines of code to show, starting from the keyword.'), 12 | caseSensitive: Joi.boolean().default(false) 13 | .description('Should the keyword be case sensitive?'), 14 | label: Joi.alternatives(Joi.boolean(), Joi.array().items(Joi.string()).single()).default(true) 15 | .description('Add a label to the new issue. If true, add the `todo` label. If false, don\'t add any label.You can also give it a label name or an array of label names.'), 16 | reopenClosed: Joi.boolean().default(true) 17 | .description('If an issue already exists and is closed, reopen it. Note: if set to false, no new issue will be created.'), 18 | exclude: Joi.string().allow(null).default(null) 19 | .description('Exclude certain files and/or directories. Should be a valid regular expression.') 20 | }) 21 | -------------------------------------------------------------------------------- /lib/utils/generate-assigned-to.js: -------------------------------------------------------------------------------- 1 | const { reduceToList, addAt } = require('./helpers') 2 | 3 | /** 4 | * Generates the assigned-to part of the footer string 5 | * @param {boolean|string|string[]} [autoAssign=true] - Auto assign config setting 6 | * @param {string} author - Commit author 7 | * @param {number|boolean} author - PR number or false 8 | * @returns {string} 9 | */ 10 | module.exports = function generateAssignedTo (autoAssign, author, pr) { 11 | if (autoAssign === true) { 12 | return pr ? ` cc @${author}.` : ` It's been assigned to @${author} because they committed the code.` 13 | } 14 | 15 | if (autoAssign === false) { 16 | return '' 17 | } 18 | 19 | let assigner 20 | if (typeof autoAssign === 'string') { 21 | assigner = addAt(autoAssign) 22 | } 23 | 24 | if (Array.isArray(autoAssign)) { 25 | const assigners = autoAssign.map(user => addAt(user)) 26 | assigner = reduceToList(assigners) 27 | } 28 | 29 | return pr ? ` cc ${assigner}` : ` It's been automagically assigned to ${assigner}.` 30 | } 31 | -------------------------------------------------------------------------------- /lib/utils/generate-label.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Generate an array of labels based on the config 3 | * @param {import('probot').Context} context - Probot context object 4 | * @param {object} cfg - Config object 5 | */ 6 | module.exports = async (context, cfg) => { 7 | if (typeof cfg.label === 'string') { 8 | return [cfg.label] 9 | } else if (Array.isArray(cfg.label)) { 10 | return cfg.label 11 | } else { 12 | if (cfg.label) { 13 | // Generate a label object 14 | const newLabel = context.repo({ 15 | name: 'todo :spiral_notepad:', 16 | color: '00B0D8', 17 | request: { retries: 0 } 18 | }) 19 | 20 | // This will catch if the label already exists 21 | try { 22 | await context.github.issues.createLabel(newLabel) 23 | } catch (e) {} 24 | 25 | return [newLabel.name] 26 | } else { 27 | return [] 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /lib/utils/get-details.js: -------------------------------------------------------------------------------- 1 | const generateAssignedTo = require('./generate-assigned-to') 2 | 3 | /** 4 | * Get the file boundaries of the hunk 5 | * @param {import('parse-diff').Change} lastChange 6 | * @param {number} line 7 | * @param {number} [padding=2] 8 | * @returns {Boundaries} 9 | */ 10 | function getFileBoundaries (lastChange, line, padding = 2) { 11 | const end = Math.min(line + padding, lastChange.ln || lastChange.ln2) 12 | return { start: line, end } 13 | } 14 | 15 | /** 16 | * Prepares some details about the TODO 17 | * @param {object} params 18 | * @param {import('probot').Context} params.context 19 | * @param {import('parse-diff').Chunk} params.chunk 20 | * @param {object} params.config 21 | * @param {number} params.line 22 | * @returns {Details} 23 | */ 24 | module.exports = ({ context, chunk, config, line }) => { 25 | const number = context.payload.pull_request ? context.payload.pull_request.number : null 26 | 27 | let username, sha 28 | if (context.payload.head_commit) { 29 | // Get it from the head commit in this push 30 | username = context.payload.head_commit.author.username 31 | sha = context.payload.head_commit.id 32 | } else { 33 | // Get it from the head ref in this PR 34 | username = context.payload.pull_request.head.user.login 35 | sha = context.payload.pull_request.head.sha 36 | } 37 | 38 | // Generate a string that expresses who the issue is assigned to 39 | const assignedToString = generateAssignedTo(config.autoAssign, username, number) 40 | 41 | let range 42 | if (!config.blobLines) { 43 | // Don't show the blob 44 | range = false 45 | } else { 46 | const lastChange = chunk.changes[chunk.changes.length - 1] 47 | const { start, end } = getFileBoundaries(lastChange, line, config.blobLines) 48 | range = start === end ? `L${start}` : `L${start}-L${end}` 49 | } 50 | 51 | return { 52 | username, 53 | sha, 54 | assignedToString, 55 | number, 56 | range 57 | } 58 | } 59 | 60 | /** 61 | * @typedef {Object} Details 62 | * @prop {string} username 63 | * @prop {string} sha 64 | * @prop {string} assignedToString 65 | * @prop {number} number 66 | * @prop {string} range 67 | */ 68 | 69 | /** 70 | * @typedef {Object} Boundaries 71 | * @prop {number} start 72 | * @prop {number} end 73 | */ 74 | -------------------------------------------------------------------------------- /lib/utils/get-diff.js: -------------------------------------------------------------------------------- 1 | /** 2 | * This value will take some tweaking. A really large diff 3 | * is in the hundred thousands (288421 prompted this change), 4 | * but they can go way higher and would result in downtime. 5 | */ 6 | const MAX_DIFF_SIZE = 150000 7 | 8 | /** 9 | * Gets the commit using the diff header 10 | * @param {import('probot').Context} context 11 | * @param {string} [method='GET'] 12 | */ 13 | async function getCommit (context, method = 'GET') { 14 | if (context.event === 'push') { 15 | return context.github.repos.getCommit(context.repo({ 16 | method, 17 | ref: context.payload.head_commit.id, 18 | headers: { Accept: 'application/vnd.github.diff' } 19 | })) 20 | } else { 21 | const { number } = context.issue() 22 | return context.github.pulls.get(context.repo({ 23 | method, 24 | headers: { Accept: 'application/vnd.github.diff' }, 25 | pull_number: number 26 | })) 27 | } 28 | } 29 | 30 | /** 31 | * Gets the diff of the commit or pull request as a string 32 | * @param {import('probot').Context} context 33 | * @returns {string} 34 | */ 35 | module.exports = async context => { 36 | const headRequest = await getCommit(context, 'HEAD') 37 | const diffSize = headRequest.headers['content-length'] 38 | if (diffSize > MAX_DIFF_SIZE) { 39 | context.log.info(`Diff is too large: ${diffSize}/${MAX_DIFF_SIZE}`) 40 | return 41 | } 42 | 43 | const diff = await getCommit(context) 44 | return diff.data 45 | } 46 | -------------------------------------------------------------------------------- /lib/utils/helpers.js: -------------------------------------------------------------------------------- 1 | exports.reduceToList = array => { 2 | return array.reduce((prev, value, i) => { 3 | if (i + 1 === array.length) { 4 | return prev + ` and ${value}` 5 | } else if (i === 0) { 6 | return prev + `${value}` 7 | } else { 8 | return prev + `, ${value}` 9 | } 10 | }, '') 11 | } 12 | 13 | exports.truncate = (str, maxLength = 80) => { 14 | if (str.length < maxLength) return str 15 | return str.substring(0, maxLength) + '...' 16 | } 17 | 18 | exports.addAt = str => { 19 | if (!str.startsWith('@')) return `@${str}` 20 | return str 21 | } 22 | 23 | const stripAt = str => { 24 | if (str.startsWith('@')) return str.split('@')[1] 25 | return str 26 | } 27 | 28 | exports.assignFlow = ({ autoAssign }, author) => { 29 | if (autoAssign === true) { 30 | return { assignee: author } 31 | } else if (typeof autoAssign === 'string') { 32 | return { assignee: autoAssign } 33 | } else if (Array.isArray(autoAssign)) { 34 | return { assignees: autoAssign.map(n => stripAt(n)) } 35 | } 36 | } 37 | 38 | exports.endDiff = diff => diff + '\n__END_OF_DIFF_PARSING__' 39 | 40 | exports.lineBreak = body => { 41 | const regEx = /\/?<br(?:\s\/)?>/g // Regular expression to match all occurences of '<br>' 42 | return body.replace(regEx, '
') 43 | } 44 | -------------------------------------------------------------------------------- /lib/utils/main-loop.js: -------------------------------------------------------------------------------- 1 | const parseDiff = require('parse-diff') 2 | const shouldExcludeFile = require('./should-exclude-file') 3 | const getDiff = require('./get-diff') 4 | const getDetails = require('./get-details') 5 | const checkForBody = require('./check-for-body') 6 | const configSchema = require('./config-schema') 7 | const generateLabel = require('./generate-label') 8 | 9 | /** 10 | * The main loop that runs the provided handler for each matching line 11 | * @param {import('probot').Context} context 12 | * @param {(todo: Todo) => void} handler 13 | */ 14 | module.exports = async (context, handler) => { 15 | context.todos = [] 16 | 17 | // Get the diff for this commit or PR 18 | const diff = await getDiff(context) 19 | if (!diff) return 20 | 21 | // Grab the config file 22 | const configFile = await context.config('config.yml') 23 | const configValue = (configFile && configFile.todo) ? configFile.todo : {} 24 | 25 | const { value: config, error } = configSchema.validate(configValue) 26 | if (error) throw error 27 | 28 | const keywords = Array.isArray(config.keyword) ? config.keyword : [config.keyword] 29 | 30 | // Ensure that all the labels we need are present 31 | const labels = await generateLabel(context, config) 32 | 33 | // RegEx that matches lines with the configured keywords 34 | const regexFlags = !config.caseSensitive ? 'i' : '' 35 | const regex = new RegExp(`.*\\b(?${keywords.join('|')})\\b\\s?:?(?.*)`, regexFlags) 36 | 37 | // Parse the diff as files 38 | const files = parseDiff(diff) 39 | 40 | await Promise.all(files.map(async file => { 41 | if (shouldExcludeFile(context.log, file.to, config.exclude)) return 42 | 43 | // Loop through every chunk in the file 44 | await Promise.all(file.chunks.map(async chunk => { 45 | // Chunks can have multiple changes 46 | await Promise.all(chunk.changes.map(async (change, index) => { 47 | // Only act on added lines 48 | // :TODO: Also handle deleted TODO lines 49 | if (change.type !== 'add') return 50 | 51 | // Attempt to find a matching line: TODO Something something 52 | const matches = regex.exec(change.content) 53 | if (!matches) return 54 | 55 | // Trim whitespace to ensure a clean title 56 | const title = matches.groups.title.trim() 57 | 58 | // This might have matched a minified file, or something 59 | // huge. GitHub wouldn't allow this anyway, so let's just ignore it. 60 | if (!title || title.length > 256) return 61 | 62 | // Get the details of this commit or PR 63 | const deets = getDetails({ context, config, chunk, line: change.ln || change.ln2 }) 64 | 65 | const { owner, repo } = context.repo() 66 | context.log(`Item found [${title}] in [${owner}/${repo}] at [${deets.sha}]`) 67 | 68 | // Run the handler for this webhook listener 69 | return handler({ 70 | keyword: matches.groups.keyword, 71 | bodyComment: checkForBody(chunk.changes, index, config), 72 | filename: file.to, 73 | title, 74 | config, 75 | chunk, 76 | index, 77 | labels, 78 | ...deets 79 | }) 80 | })) 81 | })) 82 | })) 83 | } 84 | 85 | /** 86 | * @typedef {Object} Todo 87 | * @prop {string} keyword 88 | * @prop {string} bodyComment 89 | * @prop {string} filename 90 | * @prop {string} title 91 | * @prop {object} config 92 | * @prop {import('parse-diff').Chunk} chunk 93 | * @prop {index} index 94 | * @prop {string[]} labels 95 | * @prop {string} username 96 | * @prop {string} sha 97 | * @prop {string} assignedToString 98 | * @prop {number} number 99 | * @prop {string} range 100 | */ 101 | -------------------------------------------------------------------------------- /lib/utils/reopen-closed.js: -------------------------------------------------------------------------------- 1 | const { reopenClosed } = require('../templates') 2 | 3 | /** 4 | * Reopen a closed issue and post a comment saying what happened and why 5 | * @param {object} params 6 | * @param {import('probot').Context} params.context 7 | * @param {object} params.config 8 | * @param {object} params.issue 9 | * @param {Data} data 10 | */ 11 | module.exports = async ({ context, config, issue }, data) => { 12 | if (issue.state === 'closed' && config.reopenClosed) { 13 | await context.github.issues.update(context.repo({ 14 | issue_number: issue.number, 15 | state: 'open' 16 | })) 17 | 18 | const body = reopenClosed(context.repo(data)) 19 | return context.github.issues.createComment(context.repo({ 20 | issue_number: issue.number, 21 | body 22 | })) 23 | } 24 | } 25 | 26 | /** 27 | * @typedef Data 28 | * @property {string} keyword 29 | * @property {string} sha 30 | * @property {string} filename 31 | */ 32 | -------------------------------------------------------------------------------- /lib/utils/should-exclude-file.js: -------------------------------------------------------------------------------- 1 | const alwaysExclude = /\.min\./ 2 | 3 | module.exports = (logger, filename, excludePattern) => { 4 | if (filename === '.github/config.yml') { 5 | logger.debug('Skipping .github/config.yml') 6 | return true 7 | } else if (alwaysExclude.test(filename)) { 8 | logger.debug('Skipping ' + filename + ' as it matches the the alwaysExclude pattern') 9 | return true 10 | } else if (excludePattern && new RegExp(excludePattern).test(filename)) { 11 | logger.debug('Skipping ' + filename + ' as it matches the exclude pattern ' + excludePattern) 12 | return true 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "todo", 3 | "version": "1.0.0", 4 | "description": "", 5 | "author": "Jason Etcovitch <jasonetco@gmail.com> (https://github.com/jasonetco/todo)", 6 | "license": "ISC", 7 | "repository": "https://github.com/JasonEtco/todo.git", 8 | "scripts": { 9 | "dev": "nodemon", 10 | "start": "probot run ./index.js", 11 | "test": "jest --coverage && standard", 12 | "test:update": "jest -u", 13 | "doc": "node ./script/generate-docs" 14 | }, 15 | "standard": { 16 | "env": { 17 | "jest": true 18 | } 19 | }, 20 | "bin": { 21 | "todo": "./bin/todo.js" 22 | }, 23 | "jest": { 24 | "modulePathIgnorePatterns": [ 25 | "<rootDir>/tests/fixtures/", 26 | "<rootDir>/tests/helpers.js", 27 | "<rootDir>/tests/setup.js" 28 | ], 29 | "setupFiles": [ 30 | "<rootDir>/tests/setup.js" 31 | ], 32 | "coveragePathIgnorePatterns": [ 33 | "<rootDir>/tests/helpers.js", 34 | "<rootDir>/tests/setup.js", 35 | "/node_modules/" 36 | ] 37 | }, 38 | "dependencies": { 39 | "@hapi/joi": "^17.0.2", 40 | "@octokit/rest": "^16.36.0", 41 | "hbs": "^4.1.0", 42 | "parse-diff": "^0.6.0", 43 | "probot": "^9.9.0" 44 | }, 45 | "devDependencies": { 46 | "chalk": "^3.0.0", 47 | "commander": "^4.1.0", 48 | "jest": "^24.9.0", 49 | "nodemon": "2.0.2", 50 | "smee-client": "^1.1.0", 51 | "standard": "^14.3.1" 52 | }, 53 | "nodemonConfig": { 54 | "exec": "npm start", 55 | "watch": [ 56 | ".env", 57 | "." 58 | ] 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /script/generate-docs.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs') 2 | const path = require('path') 3 | const chalk = require('chalk') 4 | const schema = require('../lib/utils/config-schema') 5 | 6 | const START = '<!--DOC GENERATOR-->' 7 | const END = '<!--ENDDOC GENERATOR-->' 8 | const REG = new RegExp(`${START}[\\s\\S]+${END}`) 9 | 10 | function expressArrayType (opt) { 11 | const items = opt.items 12 | return items.map(item => `${item.type}[]`) 13 | } 14 | 15 | function joinAlternatives (opt) { 16 | return opt.alternatives.map(a => serializeType(a)).join(', ') 17 | } 18 | 19 | function serializeType (opt) { 20 | switch (opt.type) { 21 | case 'alternatives': 22 | return joinAlternatives(opt) 23 | case 'array': 24 | return expressArrayType(opt) 25 | default: 26 | return opt.type 27 | } 28 | } 29 | 30 | function generateTable () { 31 | const describedSchema = schema.describe() 32 | 33 | const tableHeaders = '| Name | Type | Description | Default |' 34 | const separator = '|------|------|-------------|---------|' 35 | 36 | const rows = Object.keys(describedSchema.children).map(key => { 37 | const opt = describedSchema.children[key] 38 | const type = serializeType(opt) 39 | return `| \`${key}\` | \`${type}\` | ${opt.description} | \`${JSON.stringify(opt.flags.default).replace(/"/g, "'")}\` |` 40 | }) 41 | 42 | return [tableHeaders, separator, ...rows].join('\n') 43 | } 44 | 45 | function updateReadme () { 46 | const pathToReadme = path.join(__dirname, '..', 'README.md') 47 | const contents = fs.readFileSync(pathToReadme, 'utf8') 48 | const table = generateTable() 49 | const newContents = contents.replace(REG, `${START}\n${table}\n${END}`) 50 | fs.writeFileSync(pathToReadme, newContents) 51 | console.log(chalk.green(chalk.bold('✨ README docs updated!'))) 52 | } 53 | 54 | updateReadme() 55 | -------------------------------------------------------------------------------- /tests/__snapshots__/issue-rename-handler.test.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`issue-rename-handler un-edits the issue title 1`] = ` 4 | Object { 5 | "number": 35, 6 | "owner": "JasonEtco", 7 | "repo": "tests", 8 | "title": "I have been edited", 9 | } 10 | `; 11 | 12 | exports[`issue-rename-handler un-edits the issue title 2`] = ` 13 | Object { 14 | "body": "Please do not change the issue title! **todo** uses it to prevent duplicate issues from being opened. If you think this is an error, please [open an issue](https://github.com/jasonetco/todo)!", 15 | "number": 35, 16 | "owner": "JasonEtco", 17 | "repo": "tests", 18 | } 19 | `; 20 | -------------------------------------------------------------------------------- /tests/__snapshots__/pull-request-handler.test.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`pull-request-handler comments on a pull request 1`] = ` 4 | Array [ 5 | Object { 6 | "body": "## I am an example title 7 | 8 | https://github.com/JasonEtco/tests/blob/1409a3f61a27fe80a7170026fd73eaa2108a0108/.travis.yml#L10-L15 9 | 10 | --- 11 | 12 | ###### This comment was generated by [todo](https://todo.jasonet.co) based on a \`TODO\` comment in 1409a3f61a27fe80a7170026fd73eaa2108a0108 in #32. cc @JasonEtco.", 13 | "number": 32, 14 | "owner": "JasonEtco", 15 | "repo": "tests", 16 | }, 17 | ] 18 | `; 19 | 20 | exports[`pull-request-handler comments on a pull request and mentions the assigned user 1`] = ` 21 | Array [ 22 | Object { 23 | "body": "## I am an example title 24 | 25 | https://github.com/JasonEtco/tests/blob/1409a3f61a27fe80a7170026fd73eaa2108a0108/.travis.yml#L10-L15 26 | 27 | --- 28 | 29 | ###### This comment was generated by [todo](https://todo.jasonet.co) based on a \`TODO\` comment in 1409a3f61a27fe80a7170026fd73eaa2108a0108 in #32. cc @matchai", 30 | "number": 32, 31 | "owner": "JasonEtco", 32 | "repo": "tests", 33 | }, 34 | ] 35 | `; 36 | 37 | exports[`pull-request-handler comments on a pull request and mentions the assigned users 1`] = ` 38 | Array [ 39 | Object { 40 | "body": "## I am an example title 41 | 42 | https://github.com/JasonEtco/tests/blob/1409a3f61a27fe80a7170026fd73eaa2108a0108/.travis.yml#L10-L15 43 | 44 | --- 45 | 46 | ###### This comment was generated by [todo](https://todo.jasonet.co) based on a \`TODO\` comment in 1409a3f61a27fe80a7170026fd73eaa2108a0108 in #32. cc @JasonEtco, @matchai and @defunkt", 47 | "number": 32, 48 | "owner": "JasonEtco", 49 | "repo": "tests", 50 | }, 51 | ] 52 | `; 53 | 54 | exports[`pull-request-handler creates a comment with a body line 1`] = ` 55 | Array [ 56 | Object { 57 | "body": "## I am an example title 58 | 59 | Don't forget about this body text! 60 | 61 | --- 62 | 63 | https://github.com/JasonEtco/tests/blob/1409a3f61a27fe80a7170026fd73eaa2108a0108/.travis.yml#L10-L15 64 | 65 | --- 66 | 67 | ###### This comment was generated by [todo](https://todo.jasonet.co) based on a \`TODO\` comment in 1409a3f61a27fe80a7170026fd73eaa2108a0108 in #32. cc @JasonEtco.", 68 | "number": 32, 69 | "owner": "JasonEtco", 70 | "repo": "tests", 71 | }, 72 | ] 73 | `; 74 | 75 | exports[`pull-request-handler works with a string as the keyword config 1`] = ` 76 | Array [ 77 | Object { 78 | "body": "## I am an example title 79 | 80 | https://github.com/JasonEtco/tests/blob/1409a3f61a27fe80a7170026fd73eaa2108a0108/.travis.yml#L10-L15 81 | 82 | --- 83 | 84 | ###### This comment was generated by [todo](https://todo.jasonet.co) based on a \`pizza\` comment in 1409a3f61a27fe80a7170026fd73eaa2108a0108 in #32. cc @JasonEtco.", 85 | "number": 32, 86 | "owner": "JasonEtco", 87 | "repo": "tests", 88 | }, 89 | ] 90 | `; 91 | -------------------------------------------------------------------------------- /tests/__snapshots__/pull-request-merge-handler.test.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`pull-request-merged-handler creates an issue 1`] = ` 4 | Array [ 5 | Object { 6 | "assignee": "JasonEtco", 7 | "body": "https://github.com/JasonEtco/tests/blob/6c2373ad036d292f7e9467981ba8b467e8b8b0ff/.travis.yml#L10-L15 8 | 9 | --- 10 | 11 | ###### This issue was generated by [todo](https://todo.jasonet.co) based on a \`TODO\` comment in 6c2373ad036d292f7e9467981ba8b467e8b8b0ff when #21 was merged. cc @JasonEtco.", 12 | "labels": Array [ 13 | "todo :spiral_notepad:", 14 | ], 15 | "owner": "JasonEtco", 16 | "repo": "tests", 17 | "title": "I am an example title", 18 | }, 19 | ] 20 | `; 21 | 22 | exports[`pull-request-merged-handler creates an issue and assigns the configured user 1`] = ` 23 | Array [ 24 | Object { 25 | "assignee": "matchai", 26 | "body": "https://github.com/JasonEtco/tests/blob/6c2373ad036d292f7e9467981ba8b467e8b8b0ff/.travis.yml#L10-L15 27 | 28 | --- 29 | 30 | ###### This issue was generated by [todo](https://todo.jasonet.co) based on a \`TODO\` comment in 6c2373ad036d292f7e9467981ba8b467e8b8b0ff when #21 was merged. cc @matchai", 31 | "labels": Array [ 32 | "todo :spiral_notepad:", 33 | ], 34 | "owner": "JasonEtco", 35 | "repo": "tests", 36 | "title": "I am an example title", 37 | }, 38 | ] 39 | `; 40 | 41 | exports[`pull-request-merged-handler creates an issue and assigns the configured users 1`] = ` 42 | Array [ 43 | Object { 44 | "assignees": Array [ 45 | "JasonEtco", 46 | "matchai", 47 | "defunkt", 48 | ], 49 | "body": "https://github.com/JasonEtco/tests/blob/6c2373ad036d292f7e9467981ba8b467e8b8b0ff/.travis.yml#L10-L15 50 | 51 | --- 52 | 53 | ###### This issue was generated by [todo](https://todo.jasonet.co) based on a \`TODO\` comment in 6c2373ad036d292f7e9467981ba8b467e8b8b0ff when #21 was merged. cc @JasonEtco, @matchai and @defunkt", 54 | "labels": Array [ 55 | "todo :spiral_notepad:", 56 | ], 57 | "owner": "JasonEtco", 58 | "repo": "tests", 59 | "title": "I am an example title", 60 | }, 61 | ] 62 | `; 63 | 64 | exports[`pull-request-merged-handler creates an issue with a body line 1`] = ` 65 | Array [ 66 | Object { 67 | "assignee": "JasonEtco", 68 | "body": "Don't forget about this body text! 69 | 70 | --- 71 | 72 | https://github.com/JasonEtco/tests/blob/6c2373ad036d292f7e9467981ba8b467e8b8b0ff/.travis.yml#L10-L15 73 | 74 | --- 75 | 76 | ###### This issue was generated by [todo](https://todo.jasonet.co) based on a \`TODO\` comment in 6c2373ad036d292f7e9467981ba8b467e8b8b0ff when #21 was merged. cc @JasonEtco.", 77 | "labels": Array [ 78 | "todo :spiral_notepad:", 79 | ], 80 | "owner": "JasonEtco", 81 | "repo": "tests", 82 | "title": "I am an example title", 83 | }, 84 | ] 85 | `; 86 | 87 | exports[`pull-request-merged-handler creates an issue with a truncated title 1`] = ` 88 | Array [ 89 | Object { 90 | "assignee": "JasonEtco", 91 | "body": "https://github.com/JasonEtco/tests/blob/6c2373ad036d292f7e9467981ba8b467e8b8b0ff/.travis.yml#L10-L15 92 | 93 | --- 94 | 95 | ###### This issue was generated by [todo](https://todo.jasonet.co) based on a \`TODO\` comment in 6c2373ad036d292f7e9467981ba8b467e8b8b0ff when #21 was merged. cc @JasonEtco.", 96 | "labels": Array [ 97 | "todo :spiral_notepad:", 98 | ], 99 | "owner": "JasonEtco", 100 | "repo": "tests", 101 | "title": "This is a really, really, really, really, really, really, really, really, really...", 102 | }, 103 | ] 104 | `; 105 | 106 | exports[`pull-request-merged-handler creates an issue without assigning anyone 1`] = ` 107 | Array [ 108 | Object { 109 | "body": "https://github.com/JasonEtco/tests/blob/6c2373ad036d292f7e9467981ba8b467e8b8b0ff/.travis.yml#L10-L15 110 | 111 | --- 112 | 113 | ###### This issue was generated by [todo](https://todo.jasonet.co) based on a \`TODO\` comment in 6c2373ad036d292f7e9467981ba8b467e8b8b0ff when #21 was merged.", 114 | "labels": Array [ 115 | "todo :spiral_notepad:", 116 | ], 117 | "owner": "JasonEtco", 118 | "repo": "tests", 119 | "title": "I am an example title", 120 | }, 121 | ] 122 | `; 123 | 124 | exports[`pull-request-merged-handler reopens a closed issue 1`] = ` 125 | Array [ 126 | Object { 127 | "body": "This issue has been reopened because the **\`TODO\`** comment still exists in [**.travis.yml**](https://github.com/JasonEtco/tests/blob/6c2373ad036d292f7e9467981ba8b467e8b8b0ff/.travis.yml), as of 6c2373ad036d292f7e9467981ba8b467e8b8b0ff. 128 | 129 | --- 130 | 131 | ###### If this was not intentional, just remove the comment from your code. You can also set the [\`reopenClosed\`](https://github.com/JasonEtco/todo#configuring-for-your-project) config if you don't want this to happen at all anymore.", 132 | "issue_number": 1, 133 | "owner": "JasonEtco", 134 | "repo": "tests", 135 | }, 136 | ] 137 | `; 138 | -------------------------------------------------------------------------------- /tests/__snapshots__/push-handler.test.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`push-handler creates an issue 1`] = ` 4 | Array [ 5 | Object { 6 | "assignee": "JasonEtco", 7 | "body": "https://github.com/JasonEtco/tests/blob/e06c237a0c041f5a0a61f1c361f7a1d6f3d669af/.travis.yml#L10-L15 8 | 9 | --- 10 | 11 | ###### This issue was generated by [todo](https://todo.jasonet.co) based on a \`TODO\` comment in e06c237a0c041f5a0a61f1c361f7a1d6f3d669af. It's been assigned to @JasonEtco because they committed the code.", 12 | "labels": Array [ 13 | "todo :spiral_notepad:", 14 | ], 15 | "owner": "JasonEtco", 16 | "repo": "tests", 17 | "title": "I am an example title", 18 | }, 19 | ] 20 | `; 21 | 22 | exports[`push-handler creates an issue and assigns the configured user 1`] = ` 23 | Array [ 24 | Object { 25 | "assignee": "matchai", 26 | "body": "https://github.com/JasonEtco/tests/blob/e06c237a0c041f5a0a61f1c361f7a1d6f3d669af/.travis.yml#L10-L15 27 | 28 | --- 29 | 30 | ###### This issue was generated by [todo](https://todo.jasonet.co) based on a \`TODO\` comment in e06c237a0c041f5a0a61f1c361f7a1d6f3d669af. It's been automagically assigned to @matchai.", 31 | "labels": Array [ 32 | "todo :spiral_notepad:", 33 | ], 34 | "owner": "JasonEtco", 35 | "repo": "tests", 36 | "title": "I am an example title", 37 | }, 38 | ] 39 | `; 40 | 41 | exports[`push-handler creates an issue and assigns the configured users 1`] = ` 42 | Array [ 43 | Object { 44 | "assignees": Array [ 45 | "JasonEtco", 46 | "matchai", 47 | "defunkt", 48 | ], 49 | "body": "https://github.com/JasonEtco/tests/blob/e06c237a0c041f5a0a61f1c361f7a1d6f3d669af/.travis.yml#L10-L15 50 | 51 | --- 52 | 53 | ###### This issue was generated by [todo](https://todo.jasonet.co) based on a \`TODO\` comment in e06c237a0c041f5a0a61f1c361f7a1d6f3d669af. It's been automagically assigned to @JasonEtco, @matchai and @defunkt.", 54 | "labels": Array [ 55 | "todo :spiral_notepad:", 56 | ], 57 | "owner": "JasonEtco", 58 | "repo": "tests", 59 | "title": "I am an example title", 60 | }, 61 | ] 62 | `; 63 | 64 | exports[`push-handler creates an issue and respects GHE_HOST 1`] = ` 65 | Array [ 66 | Object { 67 | "assignee": "JasonEtco", 68 | "body": "https://fakegittillyoumakegit.com/JasonEtco/tests/blob/e06c237a0c041f5a0a61f1c361f7a1d6f3d669af/.travis.yml#L10-L15 69 | 70 | --- 71 | 72 | ###### This issue was generated by [todo](https://todo.jasonet.co) based on a \`TODO\` comment in e06c237a0c041f5a0a61f1c361f7a1d6f3d669af. It's been assigned to @JasonEtco because they committed the code.", 73 | "labels": Array [ 74 | "todo :spiral_notepad:", 75 | ], 76 | "owner": "JasonEtco", 77 | "repo": "tests", 78 | "title": "I am an example title", 79 | }, 80 | ] 81 | `; 82 | 83 | exports[`push-handler creates an issue with a body line 1`] = ` 84 | Array [ 85 | Object { 86 | "assignee": "JasonEtco", 87 | "body": "Don't forget about this body text! 88 | 89 | --- 90 | 91 | https://github.com/JasonEtco/tests/blob/e06c237a0c041f5a0a61f1c361f7a1d6f3d669af/.travis.yml#L10-L15 92 | 93 | --- 94 | 95 | ###### This issue was generated by [todo](https://todo.jasonet.co) based on a \`TODO\` comment in e06c237a0c041f5a0a61f1c361f7a1d6f3d669af. It's been assigned to @JasonEtco because they committed the code.", 96 | "labels": Array [ 97 | "todo :spiral_notepad:", 98 | ], 99 | "owner": "JasonEtco", 100 | "repo": "tests", 101 | "title": "I am an example title", 102 | }, 103 | ] 104 | `; 105 | 106 | exports[`push-handler creates an issue with a body line with one body keyword 1`] = ` 107 | Array [ 108 | Object { 109 | "assignee": "JasonEtco", 110 | "body": "Don't forget about this body text! 111 | 112 | --- 113 | 114 | https://github.com/JasonEtco/tests/blob/e06c237a0c041f5a0a61f1c361f7a1d6f3d669af/.travis.yml#L10-L15 115 | 116 | --- 117 | 118 | ###### This issue was generated by [todo](https://todo.jasonet.co) based on a \`TODO\` comment in e06c237a0c041f5a0a61f1c361f7a1d6f3d669af. It's been assigned to @JasonEtco because they committed the code.", 119 | "labels": Array [ 120 | "todo :spiral_notepad:", 121 | ], 122 | "owner": "JasonEtco", 123 | "repo": "tests", 124 | "title": "I am an example title", 125 | }, 126 | ] 127 | `; 128 | 129 | exports[`push-handler creates an issue with a custom keyword config 1`] = ` 130 | Array [ 131 | Object { 132 | "assignee": "JasonEtco", 133 | "body": "https://github.com/JasonEtco/tests/blob/e06c237a0c041f5a0a61f1c361f7a1d6f3d669af/.travis.yml#L10-L15 134 | 135 | --- 136 | 137 | ###### This issue was generated by [todo](https://todo.jasonet.co) based on a \`pizza\` comment in e06c237a0c041f5a0a61f1c361f7a1d6f3d669af. It's been assigned to @JasonEtco because they committed the code.", 138 | "labels": Array [ 139 | "todo :spiral_notepad:", 140 | ], 141 | "owner": "JasonEtco", 142 | "repo": "tests", 143 | "title": "I am an example title", 144 | }, 145 | ] 146 | `; 147 | 148 | exports[`push-handler creates an issue with a truncated title 1`] = ` 149 | Array [ 150 | Object { 151 | "assignee": "JasonEtco", 152 | "body": "https://github.com/JasonEtco/tests/blob/e06c237a0c041f5a0a61f1c361f7a1d6f3d669af/.travis.yml#L10-L15 153 | 154 | --- 155 | 156 | ###### This issue was generated by [todo](https://todo.jasonet.co) based on a \`TODO\` comment in e06c237a0c041f5a0a61f1c361f7a1d6f3d669af. It's been assigned to @JasonEtco because they committed the code.", 157 | "labels": Array [ 158 | "todo :spiral_notepad:", 159 | ], 160 | "owner": "JasonEtco", 161 | "repo": "tests", 162 | "title": "This is a really, really, really, really, really, really, really, really, really...", 163 | }, 164 | ] 165 | `; 166 | 167 | exports[`push-handler creates an issue with an @todo comment 1`] = ` 168 | Array [ 169 | Object { 170 | "assignee": "JasonEtco", 171 | "body": "https://github.com/JasonEtco/tests/blob/e06c237a0c041f5a0a61f1c361f7a1d6f3d669af/.travis.yml#L10-L15 172 | 173 | --- 174 | 175 | ###### This issue was generated by [todo](https://todo.jasonet.co) based on a \`todo\` comment in e06c237a0c041f5a0a61f1c361f7a1d6f3d669af. It's been assigned to @JasonEtco because they committed the code.", 176 | "labels": Array [ 177 | "todo :spiral_notepad:", 178 | ], 179 | "owner": "JasonEtco", 180 | "repo": "tests", 181 | "title": "I am an example title", 182 | }, 183 | ] 184 | `; 185 | 186 | exports[`push-handler creates an issue without assigning anyone 1`] = ` 187 | Array [ 188 | Object { 189 | "body": "https://github.com/JasonEtco/tests/blob/e06c237a0c041f5a0a61f1c361f7a1d6f3d669af/.travis.yml#L10-L15 190 | 191 | --- 192 | 193 | ###### This issue was generated by [todo](https://todo.jasonet.co) based on a \`TODO\` comment in e06c237a0c041f5a0a61f1c361f7a1d6f3d669af.", 194 | "labels": Array [ 195 | "todo :spiral_notepad:", 196 | ], 197 | "owner": "JasonEtco", 198 | "repo": "tests", 199 | "title": "I am an example title", 200 | }, 201 | ] 202 | `; 203 | 204 | exports[`push-handler creates many (5) issues 1`] = ` 205 | Array [ 206 | Array [ 207 | Object { 208 | "assignee": "JasonEtco", 209 | "body": "https://github.com/JasonEtco/tests/blob/e06c237a0c041f5a0a61f1c361f7a1d6f3d669af/file.txt#L4 210 | 211 | --- 212 | 213 | ###### This issue was generated by [todo](https://todo.jasonet.co) based on a \`TODO\` comment in e06c237a0c041f5a0a61f1c361f7a1d6f3d669af. It's been assigned to @JasonEtco because they committed the code.", 214 | "labels": Array [ 215 | "todo :spiral_notepad:", 216 | ], 217 | "owner": "JasonEtco", 218 | "repo": "tests", 219 | "title": "Title number 1", 220 | }, 221 | ], 222 | Array [ 223 | Object { 224 | "assignee": "JasonEtco", 225 | "body": "https://github.com/JasonEtco/tests/blob/e06c237a0c041f5a0a61f1c361f7a1d6f3d669af/file.txt#L4 226 | 227 | --- 228 | 229 | ###### This issue was generated by [todo](https://todo.jasonet.co) based on a \`TODO\` comment in e06c237a0c041f5a0a61f1c361f7a1d6f3d669af. It's been assigned to @JasonEtco because they committed the code.", 230 | "labels": Array [ 231 | "todo :spiral_notepad:", 232 | ], 233 | "owner": "JasonEtco", 234 | "repo": "tests", 235 | "title": "Title number 2", 236 | }, 237 | ], 238 | Array [ 239 | Object { 240 | "assignee": "JasonEtco", 241 | "body": "https://github.com/JasonEtco/tests/blob/e06c237a0c041f5a0a61f1c361f7a1d6f3d669af/file.txt#L4 242 | 243 | --- 244 | 245 | ###### This issue was generated by [todo](https://todo.jasonet.co) based on a \`TODO\` comment in e06c237a0c041f5a0a61f1c361f7a1d6f3d669af. It's been assigned to @JasonEtco because they committed the code.", 246 | "labels": Array [ 247 | "todo :spiral_notepad:", 248 | ], 249 | "owner": "JasonEtco", 250 | "repo": "tests", 251 | "title": "Title number 3", 252 | }, 253 | ], 254 | Array [ 255 | Object { 256 | "assignee": "JasonEtco", 257 | "body": "https://github.com/JasonEtco/tests/blob/e06c237a0c041f5a0a61f1c361f7a1d6f3d669af/file.txt#L4 258 | 259 | --- 260 | 261 | ###### This issue was generated by [todo](https://todo.jasonet.co) based on a \`TODO\` comment in e06c237a0c041f5a0a61f1c361f7a1d6f3d669af. It's been assigned to @JasonEtco because they committed the code.", 262 | "labels": Array [ 263 | "todo :spiral_notepad:", 264 | ], 265 | "owner": "JasonEtco", 266 | "repo": "tests", 267 | "title": "Title number 4", 268 | }, 269 | ], 270 | Array [ 271 | Object { 272 | "assignee": "JasonEtco", 273 | "body": "https://github.com/JasonEtco/tests/blob/e06c237a0c041f5a0a61f1c361f7a1d6f3d669af/file.txt#L4 274 | 275 | --- 276 | 277 | ###### This issue was generated by [todo](https://todo.jasonet.co) based on a \`TODO\` comment in e06c237a0c041f5a0a61f1c361f7a1d6f3d669af. It's been assigned to @JasonEtco because they committed the code.", 278 | "labels": Array [ 279 | "todo :spiral_notepad:", 280 | ], 281 | "owner": "JasonEtco", 282 | "repo": "tests", 283 | "title": "Title number 5", 284 | }, 285 | ], 286 | ] 287 | `; 288 | 289 | exports[`push-handler cuts the blobLines 1`] = ` 290 | Array [ 291 | Object { 292 | "assignee": "JasonEtco", 293 | "body": "Don't forget about this body text! 294 | 295 | --- 296 | 297 | https://github.com/JasonEtco/tests/blob/e06c237a0c041f5a0a61f1c361f7a1d6f3d669af/.travis.yml#L10-L12 298 | 299 | --- 300 | 301 | ###### This issue was generated by [todo](https://todo.jasonet.co) based on a \`TODO\` comment in e06c237a0c041f5a0a61f1c361f7a1d6f3d669af. It's been assigned to @JasonEtco because they committed the code.", 302 | "labels": Array [ 303 | "todo :spiral_notepad:", 304 | ], 305 | "owner": "JasonEtco", 306 | "repo": "tests", 307 | "title": "I am an example title", 308 | }, 309 | ] 310 | `; 311 | 312 | exports[`push-handler does not show the blob if blobLines is false 1`] = ` 313 | Array [ 314 | Object { 315 | "assignee": "JasonEtco", 316 | "body": "###### This issue was generated by [todo](https://todo.jasonet.co) based on a \`TODO\` comment in e06c237a0c041f5a0a61f1c361f7a1d6f3d669af. It's been assigned to @JasonEtco because they committed the code.", 317 | "labels": Array [ 318 | "todo :spiral_notepad:", 319 | ], 320 | "owner": "JasonEtco", 321 | "repo": "tests", 322 | "title": "I am an example title", 323 | }, 324 | ] 325 | `; 326 | 327 | exports[`push-handler reopens a closed issue 1`] = ` 328 | Array [ 329 | Object { 330 | "body": "This issue has been reopened because the **\`TODO\`** comment still exists in [**.travis.yml**](https://github.com/JasonEtco/tests/blob/e06c237a0c041f5a0a61f1c361f7a1d6f3d669af/.travis.yml), as of e06c237a0c041f5a0a61f1c361f7a1d6f3d669af. 331 | 332 | --- 333 | 334 | ###### If this was not intentional, just remove the comment from your code. You can also set the [\`reopenClosed\`](https://github.com/JasonEtco/todo#configuring-for-your-project) config if you don't want this to happen at all anymore.", 335 | "issue_number": 1, 336 | "owner": "JasonEtco", 337 | "repo": "tests", 338 | }, 339 | ] 340 | `; 341 | -------------------------------------------------------------------------------- /tests/fixtures/configs/autoAssignArr.yml: -------------------------------------------------------------------------------- 1 | todo: 2 | autoAssign: 3 | - "@JasonEtco" 4 | - "matchai" 5 | - "defunkt" 6 | -------------------------------------------------------------------------------- /tests/fixtures/configs/autoAssignFalse.yml: -------------------------------------------------------------------------------- 1 | todo: 2 | autoAssign: false 3 | -------------------------------------------------------------------------------- /tests/fixtures/configs/autoAssignString.yml: -------------------------------------------------------------------------------- 1 | todo: 2 | autoAssign: "matchai" 3 | -------------------------------------------------------------------------------- /tests/fixtures/configs/blobLinesFalse.yml: -------------------------------------------------------------------------------- 1 | todo: 2 | blobLines: false -------------------------------------------------------------------------------- /tests/fixtures/configs/bodyString.yml: -------------------------------------------------------------------------------- 1 | todo: 2 | bodyKeyword: BODY -------------------------------------------------------------------------------- /tests/fixtures/configs/excludeBin.yml: -------------------------------------------------------------------------------- 1 | todo: 2 | exclude: '^bin/' 3 | -------------------------------------------------------------------------------- /tests/fixtures/configs/keywordsString.yml: -------------------------------------------------------------------------------- 1 | todo: 2 | keyword: pizza -------------------------------------------------------------------------------- /tests/fixtures/configs/labelArr.yml: -------------------------------------------------------------------------------- 1 | todo: 2 | label: ["one", "two"] 3 | -------------------------------------------------------------------------------- /tests/fixtures/configs/reopenClosedFalse.yml: -------------------------------------------------------------------------------- 1 | todo: 2 | reopenClosed: false -------------------------------------------------------------------------------- /tests/fixtures/diffs/at-todo.txt: -------------------------------------------------------------------------------- 1 | diff --git a/.travis.yml b/.travis.yml 2 | index 0d43cbb..4e652e9 100644 3 | --- a/.travis.yml 4 | +++ b/.travis.yml 5 | @@ -4,21 +4,22 @@ 6 | language: node_js 7 | 8 | node_js: 9 | - "8" 10 | - 11 | + 12 | notifications: 13 | disabled: true 14 | - 15 | + // @todo I am an example title 16 | branches: 17 | except: 18 | - /^vd+.d+.d+$/ 19 | - 20 | script: 21 | - npm test 22 | - codecov -------------------------------------------------------------------------------- /tests/fixtures/diffs/basic.txt: -------------------------------------------------------------------------------- 1 | diff --git a/.travis.yml b/.travis.yml 2 | index 0d43cbb..4e652e9 100644 3 | --- a/.travis.yml 4 | +++ b/.travis.yml 5 | @@ -4,21 +4,22 @@ 6 | language: node_js 7 | 8 | node_js: 9 | - "8" 10 | - 11 | + 12 | notifications: 13 | disabled: true 14 | - 15 | + // TODO I am an example title 16 | branches: 17 | except: 18 | - /^vd+.d+.d+$/ 19 | - 20 | script: 21 | - npm test 22 | - codecov -------------------------------------------------------------------------------- /tests/fixtures/diffs/bin.txt: -------------------------------------------------------------------------------- 1 | diff --git a/bin/config.yml b/bin/config.yml 2 | index 0d43cbb..4e652e9 100644 3 | --- a/bin/config.yml 4 | +++ b/bin/config.yml 5 | @@ -4,21 +4,22 @@ 6 | language: node_js 7 | 8 | node_js: 9 | - "8" 10 | - 11 | + 12 | notifications: 13 | disabled: true 14 | - 15 | + // TODO I am an example title 16 | branches: 17 | except: 18 | - /^vd+.d+.d+$/ 19 | - 20 | script: 21 | - npm test 22 | - codecov -------------------------------------------------------------------------------- /tests/fixtures/diffs/blob-past-end.txt: -------------------------------------------------------------------------------- 1 | diff --git a/.travis.yml b/.travis.yml 2 | index 0d43cbb..4e652e9 100644 3 | --- a/.travis.yml 4 | +++ b/.travis.yml 5 | @@ -4,21 +4,22 @@ 6 | language: node_js 7 | 8 | node_js: 9 | - "8" 10 | - 11 | + 12 | notifications: 13 | disabled: true 14 | - 15 | + // TODO I am an example title 16 | + // BODY Don't forget about this body text! 17 | branches: -------------------------------------------------------------------------------- /tests/fixtures/diffs/body.txt: -------------------------------------------------------------------------------- 1 | diff --git a/.travis.yml b/.travis.yml 2 | index 0d43cbb..4e652e9 100644 3 | --- a/.travis.yml 4 | +++ b/.travis.yml 5 | @@ -4,21 +4,22 @@ 6 | language: node_js 7 | 8 | node_js: 9 | - "8" 10 | - 11 | + 12 | notifications: 13 | disabled: true 14 | - 15 | + // TODO I am an example title 16 | + // BODY Don't forget about this body text! 17 | branches: 18 | except: 19 | - /^vd+.d+.d+$/ 20 | - 21 | script: 22 | - npm test 23 | - codecov -------------------------------------------------------------------------------- /tests/fixtures/diffs/config.txt: -------------------------------------------------------------------------------- 1 | diff --git a/.github/config.yml b/.github/config.yml 2 | index 0d43cbb..4e652e9 100644 3 | --- a/.github/config.yml 4 | +++ b/.github/config.yml 5 | @@ -4,21 +4,22 @@ 6 | language: node_js 7 | 8 | node_js: 9 | - "8" 10 | - 11 | + 12 | notifications: 13 | disabled: true 14 | - 15 | + // TODO I am an example title 16 | branches: 17 | except: 18 | - /^vd+.d+.d+$/ 19 | - 20 | script: 21 | - npm test 22 | - codecov -------------------------------------------------------------------------------- /tests/fixtures/diffs/custom-keyword.txt: -------------------------------------------------------------------------------- 1 | diff --git a/.travis.yml b/.travis.yml 2 | index 0d43cbb..4e652e9 100644 3 | --- a/.travis.yml 4 | +++ b/.travis.yml 5 | @@ -4,21 +4,22 @@ 6 | language: node_js 7 | 8 | node_js: 9 | - "8" 10 | - 11 | + 12 | notifications: 13 | disabled: true 14 | - 15 | + // pizza I am an example title 16 | branches: 17 | except: 18 | - /^vd+.d+.d+$/ 19 | - 20 | script: 21 | - npm test 22 | - codecov -------------------------------------------------------------------------------- /tests/fixtures/diffs/duplicate.txt: -------------------------------------------------------------------------------- 1 | diff --git a/.travis.yml b/.travis.yml 2 | index 0d43cbb..4e652e9 100644 3 | --- a/.travis.yml 4 | +++ b/.travis.yml 5 | @@ -4,21 +4,22 @@ 6 | language: node_js 7 | 8 | node_js: 9 | - "8" 10 | - 11 | + 12 | notifications: 13 | disabled: true 14 | - 15 | + // TODO I am an example title 16 | branches: 17 | except: 18 | - /^vd+.d+.d+$/ 19 | + // TODO I am an example title 20 | - 21 | script: 22 | - npm test 23 | - codecov -------------------------------------------------------------------------------- /tests/fixtures/diffs/long-title.txt: -------------------------------------------------------------------------------- 1 | diff --git a/.travis.yml b/.travis.yml 2 | index 0d43cbb..4e652e9 100644 3 | --- a/.travis.yml 4 | +++ b/.travis.yml 5 | @@ -4,21 +4,22 @@ 6 | language: node_js 7 | 8 | node_js: 9 | - "8" 10 | - 11 | + 12 | notifications: 13 | disabled: true 14 | - 15 | + // TODO This is a really, really, really, really, really, really, really, really, really, really long title 16 | branches: 17 | except: 18 | - /^vd+.d+.d+$/ 19 | - 20 | script: 21 | - npm test 22 | - codecov -------------------------------------------------------------------------------- /tests/fixtures/diffs/many.txt: -------------------------------------------------------------------------------- 1 | diff --git a/file.txt b/file.txt 2 | index 0d43cbb..4e652e9 100644 3 | --- a/file.txt 4 | +++ b/file.txt 5 | @@ -4,21 +4,22 @@ 6 | + // TODO Title number 1 7 | diff --git a/file.txt b/file.txt 8 | index 0d43cbb..4e652e9 100644 9 | --- a/file.txt 10 | +++ b/file.txt 11 | @@ -4,21 +4,22 @@ 12 | + // TODO Title number 2 13 | diff --git a/file.txt b/file.txt 14 | index 0d43cbb..4e652e9 100644 15 | --- a/file.txt 16 | +++ b/file.txt 17 | @@ -4,21 +4,22 @@ 18 | + // TODO Title number 3 19 | diff --git a/file.txt b/file.txt 20 | index 0d43cbb..4e652e9 100644 21 | --- a/file.txt 22 | +++ b/file.txt 23 | @@ -4,21 +4,22 @@ 24 | + // TODO Title number 4 25 | diff --git a/file.txt b/file.txt 26 | index 0d43cbb..4e652e9 100644 27 | --- a/file.txt 28 | +++ b/file.txt 29 | @@ -4,21 +4,22 @@ 30 | + // TODO Title number 5 -------------------------------------------------------------------------------- /tests/fixtures/diffs/middle-of-sentence.txt: -------------------------------------------------------------------------------- 1 | diff --git a/.travis.yml b/.travis.yml 2 | index 0d43cbb..4e652e9 100644 3 | --- a/.travis.yml 4 | +++ b/.travis.yml 5 | @@ -4,21 +4,22 @@ 6 | language: node_js 7 | 8 | node_js: 9 | - "8" 10 | - 11 | + 12 | notifications: 13 | disabled: true 14 | - 15 | + // OTODOKE means a delivery 16 | branches: 17 | except: 18 | - /^vd+.d+.d+$/ 19 | - 20 | script: 21 | - npm test 22 | - codecov -------------------------------------------------------------------------------- /tests/fixtures/diffs/none.txt: -------------------------------------------------------------------------------- 1 | diff --git a/.travis.yml b/.travis.yml 2 | index 0d43cbb..4e652e9 100644 3 | --- a/.travis.yml 4 | +++ b/.travis.yml 5 | @@ -4,21 +4,22 @@ 6 | language: node_js 7 | 8 | node_js: 9 | - "8" 10 | - 11 | + 12 | notifications: 13 | disabled: true 14 | - 15 | branches: 16 | except: 17 | - /^vd+.d+.d+$/ 18 | - 19 | script: 20 | - npm test 21 | - codecov -------------------------------------------------------------------------------- /tests/fixtures/diffs/title-with-whitespace.txt: -------------------------------------------------------------------------------- 1 | diff --git a/.travis.yml b/.travis.yml 2 | index 0d43cbb..4e652e9 100644 3 | --- a/.travis.yml 4 | +++ b/.travis.yml 5 | @@ -4,21 +4,22 @@ 6 | language: node_js 7 | 8 | node_js: 9 | - "8" 10 | - 11 | + 12 | notifications: 13 | disabled: true 14 | - 15 | + // TODO: 16 | branches: 17 | except: 18 | - /^vd+.d+.d+$/ 19 | - 20 | script: 21 | - npm test 22 | - codecov -------------------------------------------------------------------------------- /tests/fixtures/payloads/issues.edited.json: -------------------------------------------------------------------------------- 1 | { 2 | "action": "edited", 3 | "issue": { 4 | "url": "https://api.github.com/repos/JasonEtco/tests/issues/35", 5 | "repository_url": "https://api.github.com/repos/JasonEtco/tests", 6 | "labels_url": "https://api.github.com/repos/JasonEtco/tests/issues/35/labels{/name}", 7 | "comments_url": "https://api.github.com/repos/JasonEtco/tests/issues/35/comments", 8 | "events_url": "https://api.github.com/repos/JasonEtco/tests/issues/35/events", 9 | "html_url": "https://github.com/JasonEtco/tests/issues/35", 10 | "id": 298043447, 11 | "number": 35, 12 | "title": "testing!", 13 | "user": { 14 | "login": "todo-dev[bot]", 15 | "id": 36559360, 16 | "avatar_url": "https://avatars1.githubusercontent.com/u/10660468?v=4", 17 | "gravatar_id": "", 18 | "url": "https://api.github.com/users/todo-dev%5Bbot%5D", 19 | "html_url": "https://github.com/apps/todo-dev", 20 | "followers_url": "https://api.github.com/users/todo-dev%5Bbot%5D/followers", 21 | "following_url": "https://api.github.com/users/todo-dev%5Bbot%5D/following{/other_user}", 22 | "gists_url": "https://api.github.com/users/todo-dev%5Bbot%5D/gists{/gist_id}", 23 | "starred_url": "https://api.github.com/users/todo-dev%5Bbot%5D/starred{/owner}{/repo}", 24 | "subscriptions_url": "https://api.github.com/users/todo-dev%5Bbot%5D/subscriptions", 25 | "organizations_url": "https://api.github.com/users/todo-dev%5Bbot%5D/orgs", 26 | "repos_url": "https://api.github.com/users/todo-dev%5Bbot%5D/repos", 27 | "events_url": "https://api.github.com/users/todo-dev%5Bbot%5D/events{/privacy}", 28 | "received_events_url": "https://api.github.com/users/todo-dev%5Bbot%5D/received_events", 29 | "type": "Bot", 30 | "site_admin": false 31 | }, 32 | "labels": [ 33 | 34 | ], 35 | "state": "open", 36 | "locked": false, 37 | "assignee": null, 38 | "assignees": [ 39 | 40 | ], 41 | "milestone": null, 42 | "comments": 0, 43 | "created_at": "2018-02-17T22:56:49Z", 44 | "updated_at": "2018-02-17T23:02:37Z", 45 | "closed_at": null, 46 | "author_association": "NONE", 47 | "body": "https://github.com/JasonEtco/tests/blob/fe5fe7386cc940a4d848718ea9b40ed0790861e1/gfdgsdf#L9\n\n---\n\n###### This issue was generated by [todo](https://todo.jasonet.co) based on a `TODO` comment in fe5fe7386cc940a4d848718ea9b40ed0790861e1. It's been assigned to @JasonEtco because they committed the code." 48 | }, 49 | "changes": { 50 | "title": { 51 | "from": "I have been edited" 52 | } 53 | }, 54 | "repository": { 55 | "id": 110556535, 56 | "name": "tests", 57 | "full_name": "JasonEtco/tests", 58 | "owner": { 59 | "login": "JasonEtco", 60 | "id": 10660468, 61 | "avatar_url": "https://avatars1.githubusercontent.com/u/10660468?v=4", 62 | "gravatar_id": "", 63 | "url": "https://api.github.com/users/JasonEtco", 64 | "html_url": "https://github.com/JasonEtco", 65 | "followers_url": "https://api.github.com/users/JasonEtco/followers", 66 | "following_url": "https://api.github.com/users/JasonEtco/following{/other_user}", 67 | "gists_url": "https://api.github.com/users/JasonEtco/gists{/gist_id}", 68 | "starred_url": "https://api.github.com/users/JasonEtco/starred{/owner}{/repo}", 69 | "subscriptions_url": "https://api.github.com/users/JasonEtco/subscriptions", 70 | "organizations_url": "https://api.github.com/users/JasonEtco/orgs", 71 | "repos_url": "https://api.github.com/users/JasonEtco/repos", 72 | "events_url": "https://api.github.com/users/JasonEtco/events{/privacy}", 73 | "received_events_url": "https://api.github.com/users/JasonEtco/received_events", 74 | "type": "User", 75 | "site_admin": true 76 | }, 77 | "private": true, 78 | "html_url": "https://github.com/JasonEtco/tests", 79 | "description": null, 80 | "fork": false, 81 | "url": "https://api.github.com/repos/JasonEtco/tests", 82 | "forks_url": "https://api.github.com/repos/JasonEtco/tests/forks", 83 | "keys_url": "https://api.github.com/repos/JasonEtco/tests/keys{/key_id}", 84 | "collaborators_url": "https://api.github.com/repos/JasonEtco/tests/collaborators{/collaborator}", 85 | "teams_url": "https://api.github.com/repos/JasonEtco/tests/teams", 86 | "hooks_url": "https://api.github.com/repos/JasonEtco/tests/hooks", 87 | "issue_events_url": "https://api.github.com/repos/JasonEtco/tests/issues/events{/number}", 88 | "events_url": "https://api.github.com/repos/JasonEtco/tests/events", 89 | "assignees_url": "https://api.github.com/repos/JasonEtco/tests/assignees{/user}", 90 | "branches_url": "https://api.github.com/repos/JasonEtco/tests/branches{/branch}", 91 | "tags_url": "https://api.github.com/repos/JasonEtco/tests/tags", 92 | "blobs_url": "https://api.github.com/repos/JasonEtco/tests/git/blobs{/sha}", 93 | "git_tags_url": "https://api.github.com/repos/JasonEtco/tests/git/tags{/sha}", 94 | "git_refs_url": "https://api.github.com/repos/JasonEtco/tests/git/refs{/sha}", 95 | "trees_url": "https://api.github.com/repos/JasonEtco/tests/git/trees{/sha}", 96 | "statuses_url": "https://api.github.com/repos/JasonEtco/tests/statuses/{sha}", 97 | "languages_url": "https://api.github.com/repos/JasonEtco/tests/languages", 98 | "stargazers_url": "https://api.github.com/repos/JasonEtco/tests/stargazers", 99 | "contributors_url": "https://api.github.com/repos/JasonEtco/tests/contributors", 100 | "subscribers_url": "https://api.github.com/repos/JasonEtco/tests/subscribers", 101 | "subscription_url": "https://api.github.com/repos/JasonEtco/tests/subscription", 102 | "commits_url": "https://api.github.com/repos/JasonEtco/tests/commits{/sha}", 103 | "git_commits_url": "https://api.github.com/repos/JasonEtco/tests/git/commits{/sha}", 104 | "comments_url": "https://api.github.com/repos/JasonEtco/tests/comments{/number}", 105 | "issue_comment_url": "https://api.github.com/repos/JasonEtco/tests/issues/comments{/number}", 106 | "contents_url": "https://api.github.com/repos/JasonEtco/tests/contents/{+path}", 107 | "compare_url": "https://api.github.com/repos/JasonEtco/tests/compare/{base}...{head}", 108 | "merges_url": "https://api.github.com/repos/JasonEtco/tests/merges", 109 | "archive_url": "https://api.github.com/repos/JasonEtco/tests/{archive_format}{/ref}", 110 | "downloads_url": "https://api.github.com/repos/JasonEtco/tests/downloads", 111 | "issues_url": "https://api.github.com/repos/JasonEtco/tests/issues{/number}", 112 | "pulls_url": "https://api.github.com/repos/JasonEtco/tests/pulls{/number}", 113 | "milestones_url": "https://api.github.com/repos/JasonEtco/tests/milestones{/number}", 114 | "notifications_url": "https://api.github.com/repos/JasonEtco/tests/notifications{?since,all,participating}", 115 | "labels_url": "https://api.github.com/repos/JasonEtco/tests/labels{/name}", 116 | "releases_url": "https://api.github.com/repos/JasonEtco/tests/releases{/id}", 117 | "deployments_url": "https://api.github.com/repos/JasonEtco/tests/deployments", 118 | "created_at": "2017-11-13T14:12:17Z", 119 | "updated_at": "2018-01-08T03:09:57Z", 120 | "pushed_at": "2018-02-17T22:13:32Z", 121 | "git_url": "git://github.com/JasonEtco/tests.git", 122 | "ssh_url": "git@github.com:JasonEtco/tests.git", 123 | "clone_url": "https://github.com/JasonEtco/tests.git", 124 | "svn_url": "https://github.com/JasonEtco/tests", 125 | "homepage": null, 126 | "size": 10, 127 | "stargazers_count": 0, 128 | "watchers_count": 0, 129 | "language": null, 130 | "has_issues": true, 131 | "has_projects": true, 132 | "has_downloads": true, 133 | "has_wiki": true, 134 | "has_pages": false, 135 | "forks_count": 0, 136 | "mirror_url": null, 137 | "archived": false, 138 | "open_issues_count": 21, 139 | "license": null, 140 | "forks": 0, 141 | "open_issues": 21, 142 | "watchers": 0, 143 | "default_branch": "master" 144 | }, 145 | "sender": { 146 | "login": "JasonEtco", 147 | "id": 10660468, 148 | "avatar_url": "https://avatars1.githubusercontent.com/u/10660468?v=4", 149 | "gravatar_id": "", 150 | "url": "https://api.github.com/users/JasonEtco", 151 | "html_url": "https://github.com/JasonEtco", 152 | "followers_url": "https://api.github.com/users/JasonEtco/followers", 153 | "following_url": "https://api.github.com/users/JasonEtco/following{/other_user}", 154 | "gists_url": "https://api.github.com/users/JasonEtco/gists{/gist_id}", 155 | "starred_url": "https://api.github.com/users/JasonEtco/starred{/owner}{/repo}", 156 | "subscriptions_url": "https://api.github.com/users/JasonEtco/subscriptions", 157 | "organizations_url": "https://api.github.com/users/JasonEtco/orgs", 158 | "repos_url": "https://api.github.com/users/JasonEtco/repos", 159 | "events_url": "https://api.github.com/users/JasonEtco/events{/privacy}", 160 | "received_events_url": "https://api.github.com/users/JasonEtco/received_events", 161 | "type": "User", 162 | "site_admin": true 163 | }, 164 | "installation": { 165 | "id": 1 166 | } 167 | } -------------------------------------------------------------------------------- /tests/fixtures/payloads/pull_request.closed.json: -------------------------------------------------------------------------------- 1 | { 2 | "action": "closed", 3 | "number": 21, 4 | "pull_request": { 5 | "url": "https://api.github.com/repos/JasonEtco/tests/pulls/21", 6 | "id": 161704164, 7 | "html_url": "https://github.com/JasonEtco/tests/pull/21", 8 | "diff_url": "https://github.com/JasonEtco/tests/pull/21.diff", 9 | "patch_url": "https://github.com/JasonEtco/tests/pull/21.patch", 10 | "issue_url": "https://api.github.com/repos/JasonEtco/tests/issues/21", 11 | "number": 21, 12 | "state": "closed", 13 | "locked": false, 14 | "title": "Create bumper.md", 15 | "user": { 16 | "login": "JasonEtco", 17 | "id": 10660468, 18 | "avatar_url": "https://avatars1.githubusercontent.com/u/10660468?v=4", 19 | "gravatar_id": "", 20 | "url": "https://api.github.com/users/JasonEtco", 21 | "html_url": "https://github.com/JasonEtco", 22 | "followers_url": "https://api.github.com/users/JasonEtco/followers", 23 | "following_url": "https://api.github.com/users/JasonEtco/following{/other_user}", 24 | "gists_url": "https://api.github.com/users/JasonEtco/gists{/gist_id}", 25 | "starred_url": "https://api.github.com/users/JasonEtco/starred{/owner}{/repo}", 26 | "subscriptions_url": "https://api.github.com/users/JasonEtco/subscriptions", 27 | "organizations_url": "https://api.github.com/users/JasonEtco/orgs", 28 | "repos_url": "https://api.github.com/users/JasonEtco/repos", 29 | "events_url": "https://api.github.com/users/JasonEtco/events{/privacy}", 30 | "received_events_url": "https://api.github.com/users/JasonEtco/received_events", 31 | "type": "User", 32 | "site_admin": true 33 | }, 34 | "body": "Closes #20 ", 35 | "created_at": "2018-01-08T19:18:04Z", 36 | "updated_at": "2018-02-17T23:18:17Z", 37 | "closed_at": "2018-02-17T23:18:17Z", 38 | "merged_at": "2018-02-17T23:18:17Z", 39 | "merge_commit_sha": "46e3daa3218850cf3262eed1ff0f1eecb0769d2f", 40 | "assignee": null, 41 | "assignees": [ 42 | 43 | ], 44 | "requested_reviewers": [ 45 | 46 | ], 47 | "requested_teams": [ 48 | 49 | ], 50 | "labels": [ 51 | 52 | ], 53 | "milestone": null, 54 | "commits_url": "https://api.github.com/repos/JasonEtco/tests/pulls/21/commits", 55 | "review_comments_url": "https://api.github.com/repos/JasonEtco/tests/pulls/21/comments", 56 | "review_comment_url": "https://api.github.com/repos/JasonEtco/tests/pulls/comments{/number}", 57 | "comments_url": "https://api.github.com/repos/JasonEtco/tests/issues/21/comments", 58 | "statuses_url": "https://api.github.com/repos/JasonEtco/tests/statuses/6c2373ad036d292f7e9467981ba8b467e8b8b0ff", 59 | "head": { 60 | "label": "JasonEtco:release", 61 | "ref": "release", 62 | "sha": "6c2373ad036d292f7e9467981ba8b467e8b8b0ff", 63 | "user": { 64 | "login": "JasonEtco", 65 | "id": 10660468, 66 | "avatar_url": "https://avatars1.githubusercontent.com/u/10660468?v=4", 67 | "gravatar_id": "", 68 | "url": "https://api.github.com/users/JasonEtco", 69 | "html_url": "https://github.com/JasonEtco", 70 | "followers_url": "https://api.github.com/users/JasonEtco/followers", 71 | "following_url": "https://api.github.com/users/JasonEtco/following{/other_user}", 72 | "gists_url": "https://api.github.com/users/JasonEtco/gists{/gist_id}", 73 | "starred_url": "https://api.github.com/users/JasonEtco/starred{/owner}{/repo}", 74 | "subscriptions_url": "https://api.github.com/users/JasonEtco/subscriptions", 75 | "organizations_url": "https://api.github.com/users/JasonEtco/orgs", 76 | "repos_url": "https://api.github.com/users/JasonEtco/repos", 77 | "events_url": "https://api.github.com/users/JasonEtco/events{/privacy}", 78 | "received_events_url": "https://api.github.com/users/JasonEtco/received_events", 79 | "type": "User", 80 | "site_admin": true 81 | }, 82 | "repo": { 83 | "id": 110556535, 84 | "name": "tests", 85 | "full_name": "JasonEtco/tests", 86 | "owner": { 87 | "login": "JasonEtco", 88 | "id": 10660468, 89 | "avatar_url": "https://avatars1.githubusercontent.com/u/10660468?v=4", 90 | "gravatar_id": "", 91 | "url": "https://api.github.com/users/JasonEtco", 92 | "html_url": "https://github.com/JasonEtco", 93 | "followers_url": "https://api.github.com/users/JasonEtco/followers", 94 | "following_url": "https://api.github.com/users/JasonEtco/following{/other_user}", 95 | "gists_url": "https://api.github.com/users/JasonEtco/gists{/gist_id}", 96 | "starred_url": "https://api.github.com/users/JasonEtco/starred{/owner}{/repo}", 97 | "subscriptions_url": "https://api.github.com/users/JasonEtco/subscriptions", 98 | "organizations_url": "https://api.github.com/users/JasonEtco/orgs", 99 | "repos_url": "https://api.github.com/users/JasonEtco/repos", 100 | "events_url": "https://api.github.com/users/JasonEtco/events{/privacy}", 101 | "received_events_url": "https://api.github.com/users/JasonEtco/received_events", 102 | "type": "User", 103 | "site_admin": true 104 | }, 105 | "private": true, 106 | "html_url": "https://github.com/JasonEtco/tests", 107 | "description": null, 108 | "fork": false, 109 | "url": "https://api.github.com/repos/JasonEtco/tests", 110 | "forks_url": "https://api.github.com/repos/JasonEtco/tests/forks", 111 | "keys_url": "https://api.github.com/repos/JasonEtco/tests/keys{/key_id}", 112 | "collaborators_url": "https://api.github.com/repos/JasonEtco/tests/collaborators{/collaborator}", 113 | "teams_url": "https://api.github.com/repos/JasonEtco/tests/teams", 114 | "hooks_url": "https://api.github.com/repos/JasonEtco/tests/hooks", 115 | "issue_events_url": "https://api.github.com/repos/JasonEtco/tests/issues/events{/number}", 116 | "events_url": "https://api.github.com/repos/JasonEtco/tests/events", 117 | "assignees_url": "https://api.github.com/repos/JasonEtco/tests/assignees{/user}", 118 | "branches_url": "https://api.github.com/repos/JasonEtco/tests/branches{/branch}", 119 | "tags_url": "https://api.github.com/repos/JasonEtco/tests/tags", 120 | "blobs_url": "https://api.github.com/repos/JasonEtco/tests/git/blobs{/sha}", 121 | "git_tags_url": "https://api.github.com/repos/JasonEtco/tests/git/tags{/sha}", 122 | "git_refs_url": "https://api.github.com/repos/JasonEtco/tests/git/refs{/sha}", 123 | "trees_url": "https://api.github.com/repos/JasonEtco/tests/git/trees{/sha}", 124 | "statuses_url": "https://api.github.com/repos/JasonEtco/tests/statuses/{sha}", 125 | "languages_url": "https://api.github.com/repos/JasonEtco/tests/languages", 126 | "stargazers_url": "https://api.github.com/repos/JasonEtco/tests/stargazers", 127 | "contributors_url": "https://api.github.com/repos/JasonEtco/tests/contributors", 128 | "subscribers_url": "https://api.github.com/repos/JasonEtco/tests/subscribers", 129 | "subscription_url": "https://api.github.com/repos/JasonEtco/tests/subscription", 130 | "commits_url": "https://api.github.com/repos/JasonEtco/tests/commits{/sha}", 131 | "git_commits_url": "https://api.github.com/repos/JasonEtco/tests/git/commits{/sha}", 132 | "comments_url": "https://api.github.com/repos/JasonEtco/tests/comments{/number}", 133 | "issue_comment_url": "https://api.github.com/repos/JasonEtco/tests/issues/comments{/number}", 134 | "contents_url": "https://api.github.com/repos/JasonEtco/tests/contents/{+path}", 135 | "compare_url": "https://api.github.com/repos/JasonEtco/tests/compare/{base}...{head}", 136 | "merges_url": "https://api.github.com/repos/JasonEtco/tests/merges", 137 | "archive_url": "https://api.github.com/repos/JasonEtco/tests/{archive_format}{/ref}", 138 | "downloads_url": "https://api.github.com/repos/JasonEtco/tests/downloads", 139 | "issues_url": "https://api.github.com/repos/JasonEtco/tests/issues{/number}", 140 | "pulls_url": "https://api.github.com/repos/JasonEtco/tests/pulls{/number}", 141 | "milestones_url": "https://api.github.com/repos/JasonEtco/tests/milestones{/number}", 142 | "notifications_url": "https://api.github.com/repos/JasonEtco/tests/notifications{?since,all,participating}", 143 | "labels_url": "https://api.github.com/repos/JasonEtco/tests/labels{/name}", 144 | "releases_url": "https://api.github.com/repos/JasonEtco/tests/releases{/id}", 145 | "deployments_url": "https://api.github.com/repos/JasonEtco/tests/deployments", 146 | "created_at": "2017-11-13T14:12:17Z", 147 | "updated_at": "2018-01-08T03:09:57Z", 148 | "pushed_at": "2018-02-17T23:18:17Z", 149 | "git_url": "git://github.com/JasonEtco/tests.git", 150 | "ssh_url": "git@github.com:JasonEtco/tests.git", 151 | "clone_url": "https://github.com/JasonEtco/tests.git", 152 | "svn_url": "https://github.com/JasonEtco/tests", 153 | "homepage": null, 154 | "size": 10, 155 | "stargazers_count": 0, 156 | "watchers_count": 0, 157 | "language": null, 158 | "has_issues": true, 159 | "has_projects": true, 160 | "has_downloads": true, 161 | "has_wiki": true, 162 | "has_pages": false, 163 | "forks_count": 0, 164 | "mirror_url": null, 165 | "archived": false, 166 | "open_issues_count": 19, 167 | "license": null, 168 | "forks": 0, 169 | "open_issues": 19, 170 | "watchers": 0, 171 | "default_branch": "master" 172 | } 173 | }, 174 | "base": { 175 | "label": "JasonEtco:master", 176 | "ref": "master", 177 | "sha": "49b33294236494d3a16ee800aacd53632b82656d", 178 | "user": { 179 | "login": "JasonEtco", 180 | "id": 10660468, 181 | "avatar_url": "https://avatars1.githubusercontent.com/u/10660468?v=4", 182 | "gravatar_id": "", 183 | "url": "https://api.github.com/users/JasonEtco", 184 | "html_url": "https://github.com/JasonEtco", 185 | "followers_url": "https://api.github.com/users/JasonEtco/followers", 186 | "following_url": "https://api.github.com/users/JasonEtco/following{/other_user}", 187 | "gists_url": "https://api.github.com/users/JasonEtco/gists{/gist_id}", 188 | "starred_url": "https://api.github.com/users/JasonEtco/starred{/owner}{/repo}", 189 | "subscriptions_url": "https://api.github.com/users/JasonEtco/subscriptions", 190 | "organizations_url": "https://api.github.com/users/JasonEtco/orgs", 191 | "repos_url": "https://api.github.com/users/JasonEtco/repos", 192 | "events_url": "https://api.github.com/users/JasonEtco/events{/privacy}", 193 | "received_events_url": "https://api.github.com/users/JasonEtco/received_events", 194 | "type": "User", 195 | "site_admin": true 196 | }, 197 | "repo": { 198 | "id": 110556535, 199 | "name": "tests", 200 | "full_name": "JasonEtco/tests", 201 | "owner": { 202 | "login": "JasonEtco", 203 | "id": 10660468, 204 | "avatar_url": "https://avatars1.githubusercontent.com/u/10660468?v=4", 205 | "gravatar_id": "", 206 | "url": "https://api.github.com/users/JasonEtco", 207 | "html_url": "https://github.com/JasonEtco", 208 | "followers_url": "https://api.github.com/users/JasonEtco/followers", 209 | "following_url": "https://api.github.com/users/JasonEtco/following{/other_user}", 210 | "gists_url": "https://api.github.com/users/JasonEtco/gists{/gist_id}", 211 | "starred_url": "https://api.github.com/users/JasonEtco/starred{/owner}{/repo}", 212 | "subscriptions_url": "https://api.github.com/users/JasonEtco/subscriptions", 213 | "organizations_url": "https://api.github.com/users/JasonEtco/orgs", 214 | "repos_url": "https://api.github.com/users/JasonEtco/repos", 215 | "events_url": "https://api.github.com/users/JasonEtco/events{/privacy}", 216 | "received_events_url": "https://api.github.com/users/JasonEtco/received_events", 217 | "type": "User", 218 | "site_admin": true 219 | }, 220 | "private": true, 221 | "html_url": "https://github.com/JasonEtco/tests", 222 | "description": null, 223 | "fork": false, 224 | "url": "https://api.github.com/repos/JasonEtco/tests", 225 | "forks_url": "https://api.github.com/repos/JasonEtco/tests/forks", 226 | "keys_url": "https://api.github.com/repos/JasonEtco/tests/keys{/key_id}", 227 | "collaborators_url": "https://api.github.com/repos/JasonEtco/tests/collaborators{/collaborator}", 228 | "teams_url": "https://api.github.com/repos/JasonEtco/tests/teams", 229 | "hooks_url": "https://api.github.com/repos/JasonEtco/tests/hooks", 230 | "issue_events_url": "https://api.github.com/repos/JasonEtco/tests/issues/events{/number}", 231 | "events_url": "https://api.github.com/repos/JasonEtco/tests/events", 232 | "assignees_url": "https://api.github.com/repos/JasonEtco/tests/assignees{/user}", 233 | "branches_url": "https://api.github.com/repos/JasonEtco/tests/branches{/branch}", 234 | "tags_url": "https://api.github.com/repos/JasonEtco/tests/tags", 235 | "blobs_url": "https://api.github.com/repos/JasonEtco/tests/git/blobs{/sha}", 236 | "git_tags_url": "https://api.github.com/repos/JasonEtco/tests/git/tags{/sha}", 237 | "git_refs_url": "https://api.github.com/repos/JasonEtco/tests/git/refs{/sha}", 238 | "trees_url": "https://api.github.com/repos/JasonEtco/tests/git/trees{/sha}", 239 | "statuses_url": "https://api.github.com/repos/JasonEtco/tests/statuses/{sha}", 240 | "languages_url": "https://api.github.com/repos/JasonEtco/tests/languages", 241 | "stargazers_url": "https://api.github.com/repos/JasonEtco/tests/stargazers", 242 | "contributors_url": "https://api.github.com/repos/JasonEtco/tests/contributors", 243 | "subscribers_url": "https://api.github.com/repos/JasonEtco/tests/subscribers", 244 | "subscription_url": "https://api.github.com/repos/JasonEtco/tests/subscription", 245 | "commits_url": "https://api.github.com/repos/JasonEtco/tests/commits{/sha}", 246 | "git_commits_url": "https://api.github.com/repos/JasonEtco/tests/git/commits{/sha}", 247 | "comments_url": "https://api.github.com/repos/JasonEtco/tests/comments{/number}", 248 | "issue_comment_url": "https://api.github.com/repos/JasonEtco/tests/issues/comments{/number}", 249 | "contents_url": "https://api.github.com/repos/JasonEtco/tests/contents/{+path}", 250 | "compare_url": "https://api.github.com/repos/JasonEtco/tests/compare/{base}...{head}", 251 | "merges_url": "https://api.github.com/repos/JasonEtco/tests/merges", 252 | "archive_url": "https://api.github.com/repos/JasonEtco/tests/{archive_format}{/ref}", 253 | "downloads_url": "https://api.github.com/repos/JasonEtco/tests/downloads", 254 | "issues_url": "https://api.github.com/repos/JasonEtco/tests/issues{/number}", 255 | "pulls_url": "https://api.github.com/repos/JasonEtco/tests/pulls{/number}", 256 | "milestones_url": "https://api.github.com/repos/JasonEtco/tests/milestones{/number}", 257 | "notifications_url": "https://api.github.com/repos/JasonEtco/tests/notifications{?since,all,participating}", 258 | "labels_url": "https://api.github.com/repos/JasonEtco/tests/labels{/name}", 259 | "releases_url": "https://api.github.com/repos/JasonEtco/tests/releases{/id}", 260 | "deployments_url": "https://api.github.com/repos/JasonEtco/tests/deployments", 261 | "created_at": "2017-11-13T14:12:17Z", 262 | "updated_at": "2018-01-08T03:09:57Z", 263 | "pushed_at": "2018-02-17T23:18:17Z", 264 | "git_url": "git://github.com/JasonEtco/tests.git", 265 | "ssh_url": "git@github.com:JasonEtco/tests.git", 266 | "clone_url": "https://github.com/JasonEtco/tests.git", 267 | "svn_url": "https://github.com/JasonEtco/tests", 268 | "homepage": null, 269 | "size": 10, 270 | "stargazers_count": 0, 271 | "watchers_count": 0, 272 | "language": null, 273 | "has_issues": true, 274 | "has_projects": true, 275 | "has_downloads": true, 276 | "has_wiki": true, 277 | "has_pages": false, 278 | "forks_count": 0, 279 | "mirror_url": null, 280 | "archived": false, 281 | "open_issues_count": 19, 282 | "license": null, 283 | "forks": 0, 284 | "open_issues": 19, 285 | "watchers": 0, 286 | "default_branch": "master" 287 | } 288 | }, 289 | "_links": { 290 | "self": { 291 | "href": "https://api.github.com/repos/JasonEtco/tests/pulls/21" 292 | }, 293 | "html": { 294 | "href": "https://github.com/JasonEtco/tests/pull/21" 295 | }, 296 | "issue": { 297 | "href": "https://api.github.com/repos/JasonEtco/tests/issues/21" 298 | }, 299 | "comments": { 300 | "href": "https://api.github.com/repos/JasonEtco/tests/issues/21/comments" 301 | }, 302 | "review_comments": { 303 | "href": "https://api.github.com/repos/JasonEtco/tests/pulls/21/comments" 304 | }, 305 | "review_comment": { 306 | "href": "https://api.github.com/repos/JasonEtco/tests/pulls/comments{/number}" 307 | }, 308 | "commits": { 309 | "href": "https://api.github.com/repos/JasonEtco/tests/pulls/21/commits" 310 | }, 311 | "statuses": { 312 | "href": "https://api.github.com/repos/JasonEtco/tests/statuses/6c2373ad036d292f7e9467981ba8b467e8b8b0ff" 313 | } 314 | }, 315 | "author_association": "OWNER", 316 | "merged": true, 317 | "mergeable": null, 318 | "rebaseable": null, 319 | "mergeable_state": "unknown", 320 | "merged_by": { 321 | "login": "JasonEtco", 322 | "id": 10660468, 323 | "avatar_url": "https://avatars1.githubusercontent.com/u/10660468?v=4", 324 | "gravatar_id": "", 325 | "url": "https://api.github.com/users/JasonEtco", 326 | "html_url": "https://github.com/JasonEtco", 327 | "followers_url": "https://api.github.com/users/JasonEtco/followers", 328 | "following_url": "https://api.github.com/users/JasonEtco/following{/other_user}", 329 | "gists_url": "https://api.github.com/users/JasonEtco/gists{/gist_id}", 330 | "starred_url": "https://api.github.com/users/JasonEtco/starred{/owner}{/repo}", 331 | "subscriptions_url": "https://api.github.com/users/JasonEtco/subscriptions", 332 | "organizations_url": "https://api.github.com/users/JasonEtco/orgs", 333 | "repos_url": "https://api.github.com/users/JasonEtco/repos", 334 | "events_url": "https://api.github.com/users/JasonEtco/events{/privacy}", 335 | "received_events_url": "https://api.github.com/users/JasonEtco/received_events", 336 | "type": "User", 337 | "site_admin": true 338 | }, 339 | "comments": 0, 340 | "review_comments": 0, 341 | "maintainer_can_modify": false, 342 | "commits": 2, 343 | "additions": 2, 344 | "deletions": 0, 345 | "changed_files": 1 346 | }, 347 | "repository": { 348 | "id": 110556535, 349 | "name": "tests", 350 | "full_name": "JasonEtco/tests", 351 | "owner": { 352 | "login": "JasonEtco", 353 | "id": 10660468, 354 | "avatar_url": "https://avatars1.githubusercontent.com/u/10660468?v=4", 355 | "gravatar_id": "", 356 | "url": "https://api.github.com/users/JasonEtco", 357 | "html_url": "https://github.com/JasonEtco", 358 | "followers_url": "https://api.github.com/users/JasonEtco/followers", 359 | "following_url": "https://api.github.com/users/JasonEtco/following{/other_user}", 360 | "gists_url": "https://api.github.com/users/JasonEtco/gists{/gist_id}", 361 | "starred_url": "https://api.github.com/users/JasonEtco/starred{/owner}{/repo}", 362 | "subscriptions_url": "https://api.github.com/users/JasonEtco/subscriptions", 363 | "organizations_url": "https://api.github.com/users/JasonEtco/orgs", 364 | "repos_url": "https://api.github.com/users/JasonEtco/repos", 365 | "events_url": "https://api.github.com/users/JasonEtco/events{/privacy}", 366 | "received_events_url": "https://api.github.com/users/JasonEtco/received_events", 367 | "type": "User", 368 | "site_admin": true 369 | }, 370 | "private": true, 371 | "html_url": "https://github.com/JasonEtco/tests", 372 | "description": null, 373 | "fork": false, 374 | "url": "https://api.github.com/repos/JasonEtco/tests", 375 | "forks_url": "https://api.github.com/repos/JasonEtco/tests/forks", 376 | "keys_url": "https://api.github.com/repos/JasonEtco/tests/keys{/key_id}", 377 | "collaborators_url": "https://api.github.com/repos/JasonEtco/tests/collaborators{/collaborator}", 378 | "teams_url": "https://api.github.com/repos/JasonEtco/tests/teams", 379 | "hooks_url": "https://api.github.com/repos/JasonEtco/tests/hooks", 380 | "issue_events_url": "https://api.github.com/repos/JasonEtco/tests/issues/events{/number}", 381 | "events_url": "https://api.github.com/repos/JasonEtco/tests/events", 382 | "assignees_url": "https://api.github.com/repos/JasonEtco/tests/assignees{/user}", 383 | "branches_url": "https://api.github.com/repos/JasonEtco/tests/branches{/branch}", 384 | "tags_url": "https://api.github.com/repos/JasonEtco/tests/tags", 385 | "blobs_url": "https://api.github.com/repos/JasonEtco/tests/git/blobs{/sha}", 386 | "git_tags_url": "https://api.github.com/repos/JasonEtco/tests/git/tags{/sha}", 387 | "git_refs_url": "https://api.github.com/repos/JasonEtco/tests/git/refs{/sha}", 388 | "trees_url": "https://api.github.com/repos/JasonEtco/tests/git/trees{/sha}", 389 | "statuses_url": "https://api.github.com/repos/JasonEtco/tests/statuses/{sha}", 390 | "languages_url": "https://api.github.com/repos/JasonEtco/tests/languages", 391 | "stargazers_url": "https://api.github.com/repos/JasonEtco/tests/stargazers", 392 | "contributors_url": "https://api.github.com/repos/JasonEtco/tests/contributors", 393 | "subscribers_url": "https://api.github.com/repos/JasonEtco/tests/subscribers", 394 | "subscription_url": "https://api.github.com/repos/JasonEtco/tests/subscription", 395 | "commits_url": "https://api.github.com/repos/JasonEtco/tests/commits{/sha}", 396 | "git_commits_url": "https://api.github.com/repos/JasonEtco/tests/git/commits{/sha}", 397 | "comments_url": "https://api.github.com/repos/JasonEtco/tests/comments{/number}", 398 | "issue_comment_url": "https://api.github.com/repos/JasonEtco/tests/issues/comments{/number}", 399 | "contents_url": "https://api.github.com/repos/JasonEtco/tests/contents/{+path}", 400 | "compare_url": "https://api.github.com/repos/JasonEtco/tests/compare/{base}...{head}", 401 | "merges_url": "https://api.github.com/repos/JasonEtco/tests/merges", 402 | "archive_url": "https://api.github.com/repos/JasonEtco/tests/{archive_format}{/ref}", 403 | "downloads_url": "https://api.github.com/repos/JasonEtco/tests/downloads", 404 | "issues_url": "https://api.github.com/repos/JasonEtco/tests/issues{/number}", 405 | "pulls_url": "https://api.github.com/repos/JasonEtco/tests/pulls{/number}", 406 | "milestones_url": "https://api.github.com/repos/JasonEtco/tests/milestones{/number}", 407 | "notifications_url": "https://api.github.com/repos/JasonEtco/tests/notifications{?since,all,participating}", 408 | "labels_url": "https://api.github.com/repos/JasonEtco/tests/labels{/name}", 409 | "releases_url": "https://api.github.com/repos/JasonEtco/tests/releases{/id}", 410 | "deployments_url": "https://api.github.com/repos/JasonEtco/tests/deployments", 411 | "created_at": "2017-11-13T14:12:17Z", 412 | "updated_at": "2018-01-08T03:09:57Z", 413 | "pushed_at": "2018-02-17T23:18:17Z", 414 | "git_url": "git://github.com/JasonEtco/tests.git", 415 | "ssh_url": "git@github.com:JasonEtco/tests.git", 416 | "clone_url": "https://github.com/JasonEtco/tests.git", 417 | "svn_url": "https://github.com/JasonEtco/tests", 418 | "homepage": null, 419 | "size": 10, 420 | "stargazers_count": 0, 421 | "watchers_count": 0, 422 | "language": null, 423 | "has_issues": true, 424 | "has_projects": true, 425 | "has_downloads": true, 426 | "has_wiki": true, 427 | "has_pages": false, 428 | "forks_count": 0, 429 | "mirror_url": null, 430 | "archived": false, 431 | "open_issues_count": 19, 432 | "license": null, 433 | "forks": 0, 434 | "open_issues": 19, 435 | "watchers": 0, 436 | "default_branch": "master" 437 | }, 438 | "sender": { 439 | "login": "JasonEtco", 440 | "id": 10660468, 441 | "avatar_url": "https://avatars1.githubusercontent.com/u/10660468?v=4", 442 | "gravatar_id": "", 443 | "url": "https://api.github.com/users/JasonEtco", 444 | "html_url": "https://github.com/JasonEtco", 445 | "followers_url": "https://api.github.com/users/JasonEtco/followers", 446 | "following_url": "https://api.github.com/users/JasonEtco/following{/other_user}", 447 | "gists_url": "https://api.github.com/users/JasonEtco/gists{/gist_id}", 448 | "starred_url": "https://api.github.com/users/JasonEtco/starred{/owner}{/repo}", 449 | "subscriptions_url": "https://api.github.com/users/JasonEtco/subscriptions", 450 | "organizations_url": "https://api.github.com/users/JasonEtco/orgs", 451 | "repos_url": "https://api.github.com/users/JasonEtco/repos", 452 | "events_url": "https://api.github.com/users/JasonEtco/events{/privacy}", 453 | "received_events_url": "https://api.github.com/users/JasonEtco/received_events", 454 | "type": "User", 455 | "site_admin": true 456 | }, 457 | "installation": { 458 | "id": 1 459 | } 460 | } -------------------------------------------------------------------------------- /tests/fixtures/payloads/pull_request.opened.json: -------------------------------------------------------------------------------- 1 | { 2 | "action": "opened", 3 | "number": 32, 4 | "pull_request": { 5 | "url": "https://api.github.com/repos/JasonEtco/tests/pulls/32", 6 | "id": 169770975, 7 | "html_url": "https://github.com/JasonEtco/tests/pull/32", 8 | "diff_url": "https://github.com/JasonEtco/tests/pull/32.diff", 9 | "patch_url": "https://github.com/JasonEtco/tests/pull/32.patch", 10 | "issue_url": "https://api.github.com/repos/JasonEtco/tests/issues/32", 11 | "number": 32, 12 | "state": "open", 13 | "locked": false, 14 | "title": "fgsdfgd", 15 | "user": { 16 | "login": "JasonEtco", 17 | "id": 10660468, 18 | "avatar_url": "https://avatars1.githubusercontent.com/u/10660468?v=4", 19 | "gravatar_id": "", 20 | "url": "https://api.github.com/users/JasonEtco", 21 | "html_url": "https://github.com/JasonEtco", 22 | "followers_url": "https://api.github.com/users/JasonEtco/followers", 23 | "following_url": "https://api.github.com/users/JasonEtco/following{/other_user}", 24 | "gists_url": "https://api.github.com/users/JasonEtco/gists{/gist_id}", 25 | "starred_url": "https://api.github.com/users/JasonEtco/starred{/owner}{/repo}", 26 | "subscriptions_url": "https://api.github.com/users/JasonEtco/subscriptions", 27 | "organizations_url": "https://api.github.com/users/JasonEtco/orgs", 28 | "repos_url": "https://api.github.com/users/JasonEtco/repos", 29 | "events_url": "https://api.github.com/users/JasonEtco/events{/privacy}", 30 | "received_events_url": "https://api.github.com/users/JasonEtco/received_events", 31 | "type": "User", 32 | "site_admin": true 33 | }, 34 | "body": "", 35 | "created_at": "2018-02-17T06:22:15Z", 36 | "updated_at": "2018-02-17T06:22:15Z", 37 | "closed_at": null, 38 | "merged_at": null, 39 | "merge_commit_sha": null, 40 | "assignee": null, 41 | "assignees": [ 42 | 43 | ], 44 | "requested_reviewers": [ 45 | 46 | ], 47 | "requested_teams": [ 48 | 49 | ], 50 | "labels": [ 51 | 52 | ], 53 | "milestone": null, 54 | "commits_url": "https://api.github.com/repos/JasonEtco/tests/pulls/32/commits", 55 | "review_comments_url": "https://api.github.com/repos/JasonEtco/tests/pulls/32/comments", 56 | "review_comment_url": "https://api.github.com/repos/JasonEtco/tests/pulls/comments{/number}", 57 | "comments_url": "https://api.github.com/repos/JasonEtco/tests/issues/32/comments", 58 | "statuses_url": "https://api.github.com/repos/JasonEtco/tests/statuses/1409a3f61a27fe80a7170026fd73eaa2108a0108", 59 | "head": { 60 | "label": "JasonEtco:JasonEtco-patch-7", 61 | "ref": "JasonEtco-patch-7", 62 | "sha": "1409a3f61a27fe80a7170026fd73eaa2108a0108", 63 | "user": { 64 | "login": "JasonEtco", 65 | "id": 10660468, 66 | "avatar_url": "https://avatars1.githubusercontent.com/u/10660468?v=4", 67 | "gravatar_id": "", 68 | "url": "https://api.github.com/users/JasonEtco", 69 | "html_url": "https://github.com/JasonEtco", 70 | "followers_url": "https://api.github.com/users/JasonEtco/followers", 71 | "following_url": "https://api.github.com/users/JasonEtco/following{/other_user}", 72 | "gists_url": "https://api.github.com/users/JasonEtco/gists{/gist_id}", 73 | "starred_url": "https://api.github.com/users/JasonEtco/starred{/owner}{/repo}", 74 | "subscriptions_url": "https://api.github.com/users/JasonEtco/subscriptions", 75 | "organizations_url": "https://api.github.com/users/JasonEtco/orgs", 76 | "repos_url": "https://api.github.com/users/JasonEtco/repos", 77 | "events_url": "https://api.github.com/users/JasonEtco/events{/privacy}", 78 | "received_events_url": "https://api.github.com/users/JasonEtco/received_events", 79 | "type": "User", 80 | "site_admin": true 81 | }, 82 | "repo": { 83 | "id": 110556535, 84 | "name": "tests", 85 | "full_name": "JasonEtco/tests", 86 | "owner": { 87 | "login": "JasonEtco", 88 | "id": 10660468, 89 | "avatar_url": "https://avatars1.githubusercontent.com/u/10660468?v=4", 90 | "gravatar_id": "", 91 | "url": "https://api.github.com/users/JasonEtco", 92 | "html_url": "https://github.com/JasonEtco", 93 | "followers_url": "https://api.github.com/users/JasonEtco/followers", 94 | "following_url": "https://api.github.com/users/JasonEtco/following{/other_user}", 95 | "gists_url": "https://api.github.com/users/JasonEtco/gists{/gist_id}", 96 | "starred_url": "https://api.github.com/users/JasonEtco/starred{/owner}{/repo}", 97 | "subscriptions_url": "https://api.github.com/users/JasonEtco/subscriptions", 98 | "organizations_url": "https://api.github.com/users/JasonEtco/orgs", 99 | "repos_url": "https://api.github.com/users/JasonEtco/repos", 100 | "events_url": "https://api.github.com/users/JasonEtco/events{/privacy}", 101 | "received_events_url": "https://api.github.com/users/JasonEtco/received_events", 102 | "type": "User", 103 | "site_admin": true 104 | }, 105 | "private": true, 106 | "html_url": "https://github.com/JasonEtco/tests", 107 | "description": null, 108 | "fork": false, 109 | "url": "https://api.github.com/repos/JasonEtco/tests", 110 | "forks_url": "https://api.github.com/repos/JasonEtco/tests/forks", 111 | "keys_url": "https://api.github.com/repos/JasonEtco/tests/keys{/key_id}", 112 | "collaborators_url": "https://api.github.com/repos/JasonEtco/tests/collaborators{/collaborator}", 113 | "teams_url": "https://api.github.com/repos/JasonEtco/tests/teams", 114 | "hooks_url": "https://api.github.com/repos/JasonEtco/tests/hooks", 115 | "issue_events_url": "https://api.github.com/repos/JasonEtco/tests/issues/events{/number}", 116 | "events_url": "https://api.github.com/repos/JasonEtco/tests/events", 117 | "assignees_url": "https://api.github.com/repos/JasonEtco/tests/assignees{/user}", 118 | "branches_url": "https://api.github.com/repos/JasonEtco/tests/branches{/branch}", 119 | "tags_url": "https://api.github.com/repos/JasonEtco/tests/tags", 120 | "blobs_url": "https://api.github.com/repos/JasonEtco/tests/git/blobs{/sha}", 121 | "git_tags_url": "https://api.github.com/repos/JasonEtco/tests/git/tags{/sha}", 122 | "git_refs_url": "https://api.github.com/repos/JasonEtco/tests/git/refs{/sha}", 123 | "trees_url": "https://api.github.com/repos/JasonEtco/tests/git/trees{/sha}", 124 | "statuses_url": "https://api.github.com/repos/JasonEtco/tests/statuses/{sha}", 125 | "languages_url": "https://api.github.com/repos/JasonEtco/tests/languages", 126 | "stargazers_url": "https://api.github.com/repos/JasonEtco/tests/stargazers", 127 | "contributors_url": "https://api.github.com/repos/JasonEtco/tests/contributors", 128 | "subscribers_url": "https://api.github.com/repos/JasonEtco/tests/subscribers", 129 | "subscription_url": "https://api.github.com/repos/JasonEtco/tests/subscription", 130 | "commits_url": "https://api.github.com/repos/JasonEtco/tests/commits{/sha}", 131 | "git_commits_url": "https://api.github.com/repos/JasonEtco/tests/git/commits{/sha}", 132 | "comments_url": "https://api.github.com/repos/JasonEtco/tests/comments{/number}", 133 | "issue_comment_url": "https://api.github.com/repos/JasonEtco/tests/issues/comments{/number}", 134 | "contents_url": "https://api.github.com/repos/JasonEtco/tests/contents/{+path}", 135 | "compare_url": "https://api.github.com/repos/JasonEtco/tests/compare/{base}...{head}", 136 | "merges_url": "https://api.github.com/repos/JasonEtco/tests/merges", 137 | "archive_url": "https://api.github.com/repos/JasonEtco/tests/{archive_format}{/ref}", 138 | "downloads_url": "https://api.github.com/repos/JasonEtco/tests/downloads", 139 | "issues_url": "https://api.github.com/repos/JasonEtco/tests/issues{/number}", 140 | "pulls_url": "https://api.github.com/repos/JasonEtco/tests/pulls{/number}", 141 | "milestones_url": "https://api.github.com/repos/JasonEtco/tests/milestones{/number}", 142 | "notifications_url": "https://api.github.com/repos/JasonEtco/tests/notifications{?since,all,participating}", 143 | "labels_url": "https://api.github.com/repos/JasonEtco/tests/labels{/name}", 144 | "releases_url": "https://api.github.com/repos/JasonEtco/tests/releases{/id}", 145 | "deployments_url": "https://api.github.com/repos/JasonEtco/tests/deployments", 146 | "created_at": "2017-11-13T14:12:17Z", 147 | "updated_at": "2018-01-08T03:09:57Z", 148 | "pushed_at": "2018-02-17T06:22:12Z", 149 | "git_url": "git://github.com/JasonEtco/tests.git", 150 | "ssh_url": "git@github.com:JasonEtco/tests.git", 151 | "clone_url": "https://github.com/JasonEtco/tests.git", 152 | "svn_url": "https://github.com/JasonEtco/tests", 153 | "homepage": null, 154 | "size": 8, 155 | "stargazers_count": 0, 156 | "watchers_count": 0, 157 | "language": null, 158 | "has_issues": true, 159 | "has_projects": true, 160 | "has_downloads": true, 161 | "has_wiki": true, 162 | "has_pages": false, 163 | "forks_count": 0, 164 | "mirror_url": null, 165 | "archived": false, 166 | "open_issues_count": 19, 167 | "license": null, 168 | "forks": 0, 169 | "open_issues": 19, 170 | "watchers": 0, 171 | "default_branch": "master" 172 | } 173 | }, 174 | "base": { 175 | "label": "JasonEtco:master", 176 | "ref": "master", 177 | "sha": "8af6a939b34f5725b54946b5d7a9f77e14c2c002", 178 | "user": { 179 | "login": "JasonEtco", 180 | "id": 10660468, 181 | "avatar_url": "https://avatars1.githubusercontent.com/u/10660468?v=4", 182 | "gravatar_id": "", 183 | "url": "https://api.github.com/users/JasonEtco", 184 | "html_url": "https://github.com/JasonEtco", 185 | "followers_url": "https://api.github.com/users/JasonEtco/followers", 186 | "following_url": "https://api.github.com/users/JasonEtco/following{/other_user}", 187 | "gists_url": "https://api.github.com/users/JasonEtco/gists{/gist_id}", 188 | "starred_url": "https://api.github.com/users/JasonEtco/starred{/owner}{/repo}", 189 | "subscriptions_url": "https://api.github.com/users/JasonEtco/subscriptions", 190 | "organizations_url": "https://api.github.com/users/JasonEtco/orgs", 191 | "repos_url": "https://api.github.com/users/JasonEtco/repos", 192 | "events_url": "https://api.github.com/users/JasonEtco/events{/privacy}", 193 | "received_events_url": "https://api.github.com/users/JasonEtco/received_events", 194 | "type": "User", 195 | "site_admin": true 196 | }, 197 | "repo": { 198 | "id": 110556535, 199 | "name": "tests", 200 | "full_name": "JasonEtco/tests", 201 | "owner": { 202 | "login": "JasonEtco", 203 | "id": 10660468, 204 | "avatar_url": "https://avatars1.githubusercontent.com/u/10660468?v=4", 205 | "gravatar_id": "", 206 | "url": "https://api.github.com/users/JasonEtco", 207 | "html_url": "https://github.com/JasonEtco", 208 | "followers_url": "https://api.github.com/users/JasonEtco/followers", 209 | "following_url": "https://api.github.com/users/JasonEtco/following{/other_user}", 210 | "gists_url": "https://api.github.com/users/JasonEtco/gists{/gist_id}", 211 | "starred_url": "https://api.github.com/users/JasonEtco/starred{/owner}{/repo}", 212 | "subscriptions_url": "https://api.github.com/users/JasonEtco/subscriptions", 213 | "organizations_url": "https://api.github.com/users/JasonEtco/orgs", 214 | "repos_url": "https://api.github.com/users/JasonEtco/repos", 215 | "events_url": "https://api.github.com/users/JasonEtco/events{/privacy}", 216 | "received_events_url": "https://api.github.com/users/JasonEtco/received_events", 217 | "type": "User", 218 | "site_admin": true 219 | }, 220 | "private": true, 221 | "html_url": "https://github.com/JasonEtco/tests", 222 | "description": null, 223 | "fork": false, 224 | "url": "https://api.github.com/repos/JasonEtco/tests", 225 | "forks_url": "https://api.github.com/repos/JasonEtco/tests/forks", 226 | "keys_url": "https://api.github.com/repos/JasonEtco/tests/keys{/key_id}", 227 | "collaborators_url": "https://api.github.com/repos/JasonEtco/tests/collaborators{/collaborator}", 228 | "teams_url": "https://api.github.com/repos/JasonEtco/tests/teams", 229 | "hooks_url": "https://api.github.com/repos/JasonEtco/tests/hooks", 230 | "issue_events_url": "https://api.github.com/repos/JasonEtco/tests/issues/events{/number}", 231 | "events_url": "https://api.github.com/repos/JasonEtco/tests/events", 232 | "assignees_url": "https://api.github.com/repos/JasonEtco/tests/assignees{/user}", 233 | "branches_url": "https://api.github.com/repos/JasonEtco/tests/branches{/branch}", 234 | "tags_url": "https://api.github.com/repos/JasonEtco/tests/tags", 235 | "blobs_url": "https://api.github.com/repos/JasonEtco/tests/git/blobs{/sha}", 236 | "git_tags_url": "https://api.github.com/repos/JasonEtco/tests/git/tags{/sha}", 237 | "git_refs_url": "https://api.github.com/repos/JasonEtco/tests/git/refs{/sha}", 238 | "trees_url": "https://api.github.com/repos/JasonEtco/tests/git/trees{/sha}", 239 | "statuses_url": "https://api.github.com/repos/JasonEtco/tests/statuses/{sha}", 240 | "languages_url": "https://api.github.com/repos/JasonEtco/tests/languages", 241 | "stargazers_url": "https://api.github.com/repos/JasonEtco/tests/stargazers", 242 | "contributors_url": "https://api.github.com/repos/JasonEtco/tests/contributors", 243 | "subscribers_url": "https://api.github.com/repos/JasonEtco/tests/subscribers", 244 | "subscription_url": "https://api.github.com/repos/JasonEtco/tests/subscription", 245 | "commits_url": "https://api.github.com/repos/JasonEtco/tests/commits{/sha}", 246 | "git_commits_url": "https://api.github.com/repos/JasonEtco/tests/git/commits{/sha}", 247 | "comments_url": "https://api.github.com/repos/JasonEtco/tests/comments{/number}", 248 | "issue_comment_url": "https://api.github.com/repos/JasonEtco/tests/issues/comments{/number}", 249 | "contents_url": "https://api.github.com/repos/JasonEtco/tests/contents/{+path}", 250 | "compare_url": "https://api.github.com/repos/JasonEtco/tests/compare/{base}...{head}", 251 | "merges_url": "https://api.github.com/repos/JasonEtco/tests/merges", 252 | "archive_url": "https://api.github.com/repos/JasonEtco/tests/{archive_format}{/ref}", 253 | "downloads_url": "https://api.github.com/repos/JasonEtco/tests/downloads", 254 | "issues_url": "https://api.github.com/repos/JasonEtco/tests/issues{/number}", 255 | "pulls_url": "https://api.github.com/repos/JasonEtco/tests/pulls{/number}", 256 | "milestones_url": "https://api.github.com/repos/JasonEtco/tests/milestones{/number}", 257 | "notifications_url": "https://api.github.com/repos/JasonEtco/tests/notifications{?since,all,participating}", 258 | "labels_url": "https://api.github.com/repos/JasonEtco/tests/labels{/name}", 259 | "releases_url": "https://api.github.com/repos/JasonEtco/tests/releases{/id}", 260 | "deployments_url": "https://api.github.com/repos/JasonEtco/tests/deployments", 261 | "created_at": "2017-11-13T14:12:17Z", 262 | "updated_at": "2018-01-08T03:09:57Z", 263 | "pushed_at": "2018-02-17T06:22:12Z", 264 | "git_url": "git://github.com/JasonEtco/tests.git", 265 | "ssh_url": "git@github.com:JasonEtco/tests.git", 266 | "clone_url": "https://github.com/JasonEtco/tests.git", 267 | "svn_url": "https://github.com/JasonEtco/tests", 268 | "homepage": null, 269 | "size": 8, 270 | "stargazers_count": 0, 271 | "watchers_count": 0, 272 | "language": null, 273 | "has_issues": true, 274 | "has_projects": true, 275 | "has_downloads": true, 276 | "has_wiki": true, 277 | "has_pages": false, 278 | "forks_count": 0, 279 | "mirror_url": null, 280 | "archived": false, 281 | "open_issues_count": 19, 282 | "license": null, 283 | "forks": 0, 284 | "open_issues": 19, 285 | "watchers": 0, 286 | "default_branch": "master" 287 | } 288 | }, 289 | "_links": { 290 | "self": { 291 | "href": "https://api.github.com/repos/JasonEtco/tests/pulls/32" 292 | }, 293 | "html": { 294 | "href": "https://github.com/JasonEtco/tests/pull/32" 295 | }, 296 | "issue": { 297 | "href": "https://api.github.com/repos/JasonEtco/tests/issues/32" 298 | }, 299 | "comments": { 300 | "href": "https://api.github.com/repos/JasonEtco/tests/issues/32/comments" 301 | }, 302 | "review_comments": { 303 | "href": "https://api.github.com/repos/JasonEtco/tests/pulls/32/comments" 304 | }, 305 | "review_comment": { 306 | "href": "https://api.github.com/repos/JasonEtco/tests/pulls/comments{/number}" 307 | }, 308 | "commits": { 309 | "href": "https://api.github.com/repos/JasonEtco/tests/pulls/32/commits" 310 | }, 311 | "statuses": { 312 | "href": "https://api.github.com/repos/JasonEtco/tests/statuses/1409a3f61a27fe80a7170026fd73eaa2108a0108" 313 | } 314 | }, 315 | "author_association": "OWNER", 316 | "merged": false, 317 | "mergeable": null, 318 | "rebaseable": null, 319 | "mergeable_state": "unknown", 320 | "merged_by": null, 321 | "comments": 0, 322 | "review_comments": 0, 323 | "maintainer_can_modify": false, 324 | "commits": 1, 325 | "additions": 1, 326 | "deletions": 0, 327 | "changed_files": 1 328 | }, 329 | "repository": { 330 | "id": 110556535, 331 | "name": "tests", 332 | "full_name": "JasonEtco/tests", 333 | "owner": { 334 | "login": "JasonEtco", 335 | "id": 10660468, 336 | "avatar_url": "https://avatars1.githubusercontent.com/u/10660468?v=4", 337 | "gravatar_id": "", 338 | "url": "https://api.github.com/users/JasonEtco", 339 | "html_url": "https://github.com/JasonEtco", 340 | "followers_url": "https://api.github.com/users/JasonEtco/followers", 341 | "following_url": "https://api.github.com/users/JasonEtco/following{/other_user}", 342 | "gists_url": "https://api.github.com/users/JasonEtco/gists{/gist_id}", 343 | "starred_url": "https://api.github.com/users/JasonEtco/starred{/owner}{/repo}", 344 | "subscriptions_url": "https://api.github.com/users/JasonEtco/subscriptions", 345 | "organizations_url": "https://api.github.com/users/JasonEtco/orgs", 346 | "repos_url": "https://api.github.com/users/JasonEtco/repos", 347 | "events_url": "https://api.github.com/users/JasonEtco/events{/privacy}", 348 | "received_events_url": "https://api.github.com/users/JasonEtco/received_events", 349 | "type": "User", 350 | "site_admin": true 351 | }, 352 | "private": true, 353 | "html_url": "https://github.com/JasonEtco/tests", 354 | "description": null, 355 | "fork": false, 356 | "url": "https://api.github.com/repos/JasonEtco/tests", 357 | "forks_url": "https://api.github.com/repos/JasonEtco/tests/forks", 358 | "keys_url": "https://api.github.com/repos/JasonEtco/tests/keys{/key_id}", 359 | "collaborators_url": "https://api.github.com/repos/JasonEtco/tests/collaborators{/collaborator}", 360 | "teams_url": "https://api.github.com/repos/JasonEtco/tests/teams", 361 | "hooks_url": "https://api.github.com/repos/JasonEtco/tests/hooks", 362 | "issue_events_url": "https://api.github.com/repos/JasonEtco/tests/issues/events{/number}", 363 | "events_url": "https://api.github.com/repos/JasonEtco/tests/events", 364 | "assignees_url": "https://api.github.com/repos/JasonEtco/tests/assignees{/user}", 365 | "branches_url": "https://api.github.com/repos/JasonEtco/tests/branches{/branch}", 366 | "tags_url": "https://api.github.com/repos/JasonEtco/tests/tags", 367 | "blobs_url": "https://api.github.com/repos/JasonEtco/tests/git/blobs{/sha}", 368 | "git_tags_url": "https://api.github.com/repos/JasonEtco/tests/git/tags{/sha}", 369 | "git_refs_url": "https://api.github.com/repos/JasonEtco/tests/git/refs{/sha}", 370 | "trees_url": "https://api.github.com/repos/JasonEtco/tests/git/trees{/sha}", 371 | "statuses_url": "https://api.github.com/repos/JasonEtco/tests/statuses/{sha}", 372 | "languages_url": "https://api.github.com/repos/JasonEtco/tests/languages", 373 | "stargazers_url": "https://api.github.com/repos/JasonEtco/tests/stargazers", 374 | "contributors_url": "https://api.github.com/repos/JasonEtco/tests/contributors", 375 | "subscribers_url": "https://api.github.com/repos/JasonEtco/tests/subscribers", 376 | "subscription_url": "https://api.github.com/repos/JasonEtco/tests/subscription", 377 | "commits_url": "https://api.github.com/repos/JasonEtco/tests/commits{/sha}", 378 | "git_commits_url": "https://api.github.com/repos/JasonEtco/tests/git/commits{/sha}", 379 | "comments_url": "https://api.github.com/repos/JasonEtco/tests/comments{/number}", 380 | "issue_comment_url": "https://api.github.com/repos/JasonEtco/tests/issues/comments{/number}", 381 | "contents_url": "https://api.github.com/repos/JasonEtco/tests/contents/{+path}", 382 | "compare_url": "https://api.github.com/repos/JasonEtco/tests/compare/{base}...{head}", 383 | "merges_url": "https://api.github.com/repos/JasonEtco/tests/merges", 384 | "archive_url": "https://api.github.com/repos/JasonEtco/tests/{archive_format}{/ref}", 385 | "downloads_url": "https://api.github.com/repos/JasonEtco/tests/downloads", 386 | "issues_url": "https://api.github.com/repos/JasonEtco/tests/issues{/number}", 387 | "pulls_url": "https://api.github.com/repos/JasonEtco/tests/pulls{/number}", 388 | "milestones_url": "https://api.github.com/repos/JasonEtco/tests/milestones{/number}", 389 | "notifications_url": "https://api.github.com/repos/JasonEtco/tests/notifications{?since,all,participating}", 390 | "labels_url": "https://api.github.com/repos/JasonEtco/tests/labels{/name}", 391 | "releases_url": "https://api.github.com/repos/JasonEtco/tests/releases{/id}", 392 | "deployments_url": "https://api.github.com/repos/JasonEtco/tests/deployments", 393 | "created_at": "2017-11-13T14:12:17Z", 394 | "updated_at": "2018-01-08T03:09:57Z", 395 | "pushed_at": "2018-02-17T06:22:12Z", 396 | "git_url": "git://github.com/JasonEtco/tests.git", 397 | "ssh_url": "git@github.com:JasonEtco/tests.git", 398 | "clone_url": "https://github.com/JasonEtco/tests.git", 399 | "svn_url": "https://github.com/JasonEtco/tests", 400 | "homepage": null, 401 | "size": 8, 402 | "stargazers_count": 0, 403 | "watchers_count": 0, 404 | "language": null, 405 | "has_issues": true, 406 | "has_projects": true, 407 | "has_downloads": true, 408 | "has_wiki": true, 409 | "has_pages": false, 410 | "forks_count": 0, 411 | "mirror_url": null, 412 | "archived": false, 413 | "open_issues_count": 19, 414 | "license": null, 415 | "forks": 0, 416 | "open_issues": 19, 417 | "watchers": 0, 418 | "default_branch": "master" 419 | }, 420 | "sender": { 421 | "login": "JasonEtco", 422 | "id": 10660468, 423 | "avatar_url": "https://avatars1.githubusercontent.com/u/10660468?v=4", 424 | "gravatar_id": "", 425 | "url": "https://api.github.com/users/JasonEtco", 426 | "html_url": "https://github.com/JasonEtco", 427 | "followers_url": "https://api.github.com/users/JasonEtco/followers", 428 | "following_url": "https://api.github.com/users/JasonEtco/following{/other_user}", 429 | "gists_url": "https://api.github.com/users/JasonEtco/gists{/gist_id}", 430 | "starred_url": "https://api.github.com/users/JasonEtco/starred{/owner}{/repo}", 431 | "subscriptions_url": "https://api.github.com/users/JasonEtco/subscriptions", 432 | "organizations_url": "https://api.github.com/users/JasonEtco/orgs", 433 | "repos_url": "https://api.github.com/users/JasonEtco/repos", 434 | "events_url": "https://api.github.com/users/JasonEtco/events{/privacy}", 435 | "received_events_url": "https://api.github.com/users/JasonEtco/received_events", 436 | "type": "User", 437 | "site_admin": true 438 | }, 439 | "installation": { 440 | "id": 1 441 | } 442 | } -------------------------------------------------------------------------------- /tests/fixtures/payloads/pull_request.synchronize.json: -------------------------------------------------------------------------------- 1 | { 2 | "action": "synchronize", 3 | "number": 32, 4 | "pull_request": { 5 | "url": "https://api.github.com/repos/JasonEtco/tests/pulls/32", 6 | "id": 169770975, 7 | "html_url": "https://github.com/JasonEtco/tests/pull/32", 8 | "diff_url": "https://github.com/JasonEtco/tests/pull/32.diff", 9 | "patch_url": "https://github.com/JasonEtco/tests/pull/32.patch", 10 | "issue_url": "https://api.github.com/repos/JasonEtco/tests/issues/32", 11 | "number": 32, 12 | "state": "open", 13 | "locked": false, 14 | "title": "fgsdfgd", 15 | "user": { 16 | "login": "JasonEtco", 17 | "id": 10660468, 18 | "avatar_url": "https://avatars1.githubusercontent.com/u/10660468?v=4", 19 | "gravatar_id": "", 20 | "url": "https://api.github.com/users/JasonEtco", 21 | "html_url": "https://github.com/JasonEtco", 22 | "followers_url": "https://api.github.com/users/JasonEtco/followers", 23 | "following_url": "https://api.github.com/users/JasonEtco/following{/other_user}", 24 | "gists_url": "https://api.github.com/users/JasonEtco/gists{/gist_id}", 25 | "starred_url": "https://api.github.com/users/JasonEtco/starred{/owner}{/repo}", 26 | "subscriptions_url": "https://api.github.com/users/JasonEtco/subscriptions", 27 | "organizations_url": "https://api.github.com/users/JasonEtco/orgs", 28 | "repos_url": "https://api.github.com/users/JasonEtco/repos", 29 | "events_url": "https://api.github.com/users/JasonEtco/events{/privacy}", 30 | "received_events_url": "https://api.github.com/users/JasonEtco/received_events", 31 | "type": "User", 32 | "site_admin": true 33 | }, 34 | "body": "", 35 | "created_at": "2018-02-17T06:22:15Z", 36 | "updated_at": "2018-02-17T06:38:38Z", 37 | "closed_at": null, 38 | "merged_at": null, 39 | "merge_commit_sha": "5258421ec786f7093cc3af7548ec8422d97ceb29", 40 | "assignee": null, 41 | "assignees": [ 42 | 43 | ], 44 | "requested_reviewers": [ 45 | 46 | ], 47 | "requested_teams": [ 48 | 49 | ], 50 | "labels": [ 51 | 52 | ], 53 | "milestone": null, 54 | "commits_url": "https://api.github.com/repos/JasonEtco/tests/pulls/32/commits", 55 | "review_comments_url": "https://api.github.com/repos/JasonEtco/tests/pulls/32/comments", 56 | "review_comment_url": "https://api.github.com/repos/JasonEtco/tests/pulls/comments{/number}", 57 | "comments_url": "https://api.github.com/repos/JasonEtco/tests/issues/32/comments", 58 | "statuses_url": "https://api.github.com/repos/JasonEtco/tests/statuses/fe5fe7386cc940a4d848718ea9b40ed0790861e1", 59 | "head": { 60 | "label": "JasonEtco:JasonEtco-patch-7", 61 | "ref": "JasonEtco-patch-7", 62 | "sha": "fe5fe7386cc940a4d848718ea9b40ed0790861e1", 63 | "user": { 64 | "login": "JasonEtco", 65 | "id": 10660468, 66 | "avatar_url": "https://avatars1.githubusercontent.com/u/10660468?v=4", 67 | "gravatar_id": "", 68 | "url": "https://api.github.com/users/JasonEtco", 69 | "html_url": "https://github.com/JasonEtco", 70 | "followers_url": "https://api.github.com/users/JasonEtco/followers", 71 | "following_url": "https://api.github.com/users/JasonEtco/following{/other_user}", 72 | "gists_url": "https://api.github.com/users/JasonEtco/gists{/gist_id}", 73 | "starred_url": "https://api.github.com/users/JasonEtco/starred{/owner}{/repo}", 74 | "subscriptions_url": "https://api.github.com/users/JasonEtco/subscriptions", 75 | "organizations_url": "https://api.github.com/users/JasonEtco/orgs", 76 | "repos_url": "https://api.github.com/users/JasonEtco/repos", 77 | "events_url": "https://api.github.com/users/JasonEtco/events{/privacy}", 78 | "received_events_url": "https://api.github.com/users/JasonEtco/received_events", 79 | "type": "User", 80 | "site_admin": true 81 | }, 82 | "repo": { 83 | "id": 110556535, 84 | "name": "tests", 85 | "full_name": "JasonEtco/tests", 86 | "owner": { 87 | "login": "JasonEtco", 88 | "id": 10660468, 89 | "avatar_url": "https://avatars1.githubusercontent.com/u/10660468?v=4", 90 | "gravatar_id": "", 91 | "url": "https://api.github.com/users/JasonEtco", 92 | "html_url": "https://github.com/JasonEtco", 93 | "followers_url": "https://api.github.com/users/JasonEtco/followers", 94 | "following_url": "https://api.github.com/users/JasonEtco/following{/other_user}", 95 | "gists_url": "https://api.github.com/users/JasonEtco/gists{/gist_id}", 96 | "starred_url": "https://api.github.com/users/JasonEtco/starred{/owner}{/repo}", 97 | "subscriptions_url": "https://api.github.com/users/JasonEtco/subscriptions", 98 | "organizations_url": "https://api.github.com/users/JasonEtco/orgs", 99 | "repos_url": "https://api.github.com/users/JasonEtco/repos", 100 | "events_url": "https://api.github.com/users/JasonEtco/events{/privacy}", 101 | "received_events_url": "https://api.github.com/users/JasonEtco/received_events", 102 | "type": "User", 103 | "site_admin": true 104 | }, 105 | "private": true, 106 | "html_url": "https://github.com/JasonEtco/tests", 107 | "description": null, 108 | "fork": false, 109 | "url": "https://api.github.com/repos/JasonEtco/tests", 110 | "forks_url": "https://api.github.com/repos/JasonEtco/tests/forks", 111 | "keys_url": "https://api.github.com/repos/JasonEtco/tests/keys{/key_id}", 112 | "collaborators_url": "https://api.github.com/repos/JasonEtco/tests/collaborators{/collaborator}", 113 | "teams_url": "https://api.github.com/repos/JasonEtco/tests/teams", 114 | "hooks_url": "https://api.github.com/repos/JasonEtco/tests/hooks", 115 | "issue_events_url": "https://api.github.com/repos/JasonEtco/tests/issues/events{/number}", 116 | "events_url": "https://api.github.com/repos/JasonEtco/tests/events", 117 | "assignees_url": "https://api.github.com/repos/JasonEtco/tests/assignees{/user}", 118 | "branches_url": "https://api.github.com/repos/JasonEtco/tests/branches{/branch}", 119 | "tags_url": "https://api.github.com/repos/JasonEtco/tests/tags", 120 | "blobs_url": "https://api.github.com/repos/JasonEtco/tests/git/blobs{/sha}", 121 | "git_tags_url": "https://api.github.com/repos/JasonEtco/tests/git/tags{/sha}", 122 | "git_refs_url": "https://api.github.com/repos/JasonEtco/tests/git/refs{/sha}", 123 | "trees_url": "https://api.github.com/repos/JasonEtco/tests/git/trees{/sha}", 124 | "statuses_url": "https://api.github.com/repos/JasonEtco/tests/statuses/{sha}", 125 | "languages_url": "https://api.github.com/repos/JasonEtco/tests/languages", 126 | "stargazers_url": "https://api.github.com/repos/JasonEtco/tests/stargazers", 127 | "contributors_url": "https://api.github.com/repos/JasonEtco/tests/contributors", 128 | "subscribers_url": "https://api.github.com/repos/JasonEtco/tests/subscribers", 129 | "subscription_url": "https://api.github.com/repos/JasonEtco/tests/subscription", 130 | "commits_url": "https://api.github.com/repos/JasonEtco/tests/commits{/sha}", 131 | "git_commits_url": "https://api.github.com/repos/JasonEtco/tests/git/commits{/sha}", 132 | "comments_url": "https://api.github.com/repos/JasonEtco/tests/comments{/number}", 133 | "issue_comment_url": "https://api.github.com/repos/JasonEtco/tests/issues/comments{/number}", 134 | "contents_url": "https://api.github.com/repos/JasonEtco/tests/contents/{+path}", 135 | "compare_url": "https://api.github.com/repos/JasonEtco/tests/compare/{base}...{head}", 136 | "merges_url": "https://api.github.com/repos/JasonEtco/tests/merges", 137 | "archive_url": "https://api.github.com/repos/JasonEtco/tests/{archive_format}{/ref}", 138 | "downloads_url": "https://api.github.com/repos/JasonEtco/tests/downloads", 139 | "issues_url": "https://api.github.com/repos/JasonEtco/tests/issues{/number}", 140 | "pulls_url": "https://api.github.com/repos/JasonEtco/tests/pulls{/number}", 141 | "milestones_url": "https://api.github.com/repos/JasonEtco/tests/milestones{/number}", 142 | "notifications_url": "https://api.github.com/repos/JasonEtco/tests/notifications{?since,all,participating}", 143 | "labels_url": "https://api.github.com/repos/JasonEtco/tests/labels{/name}", 144 | "releases_url": "https://api.github.com/repos/JasonEtco/tests/releases{/id}", 145 | "deployments_url": "https://api.github.com/repos/JasonEtco/tests/deployments", 146 | "created_at": "2017-11-13T14:12:17Z", 147 | "updated_at": "2018-01-08T03:09:57Z", 148 | "pushed_at": "2018-02-17T06:38:38Z", 149 | "git_url": "git://github.com/JasonEtco/tests.git", 150 | "ssh_url": "git@github.com:JasonEtco/tests.git", 151 | "clone_url": "https://github.com/JasonEtco/tests.git", 152 | "svn_url": "https://github.com/JasonEtco/tests", 153 | "homepage": null, 154 | "size": 8, 155 | "stargazers_count": 0, 156 | "watchers_count": 0, 157 | "language": null, 158 | "has_issues": true, 159 | "has_projects": true, 160 | "has_downloads": true, 161 | "has_wiki": true, 162 | "has_pages": false, 163 | "forks_count": 0, 164 | "mirror_url": null, 165 | "archived": false, 166 | "open_issues_count": 19, 167 | "license": null, 168 | "forks": 0, 169 | "open_issues": 19, 170 | "watchers": 0, 171 | "default_branch": "master" 172 | } 173 | }, 174 | "base": { 175 | "label": "JasonEtco:master", 176 | "ref": "master", 177 | "sha": "8af6a939b34f5725b54946b5d7a9f77e14c2c002", 178 | "user": { 179 | "login": "JasonEtco", 180 | "id": 10660468, 181 | "avatar_url": "https://avatars1.githubusercontent.com/u/10660468?v=4", 182 | "gravatar_id": "", 183 | "url": "https://api.github.com/users/JasonEtco", 184 | "html_url": "https://github.com/JasonEtco", 185 | "followers_url": "https://api.github.com/users/JasonEtco/followers", 186 | "following_url": "https://api.github.com/users/JasonEtco/following{/other_user}", 187 | "gists_url": "https://api.github.com/users/JasonEtco/gists{/gist_id}", 188 | "starred_url": "https://api.github.com/users/JasonEtco/starred{/owner}{/repo}", 189 | "subscriptions_url": "https://api.github.com/users/JasonEtco/subscriptions", 190 | "organizations_url": "https://api.github.com/users/JasonEtco/orgs", 191 | "repos_url": "https://api.github.com/users/JasonEtco/repos", 192 | "events_url": "https://api.github.com/users/JasonEtco/events{/privacy}", 193 | "received_events_url": "https://api.github.com/users/JasonEtco/received_events", 194 | "type": "User", 195 | "site_admin": true 196 | }, 197 | "repo": { 198 | "id": 110556535, 199 | "name": "tests", 200 | "full_name": "JasonEtco/tests", 201 | "owner": { 202 | "login": "JasonEtco", 203 | "id": 10660468, 204 | "avatar_url": "https://avatars1.githubusercontent.com/u/10660468?v=4", 205 | "gravatar_id": "", 206 | "url": "https://api.github.com/users/JasonEtco", 207 | "html_url": "https://github.com/JasonEtco", 208 | "followers_url": "https://api.github.com/users/JasonEtco/followers", 209 | "following_url": "https://api.github.com/users/JasonEtco/following{/other_user}", 210 | "gists_url": "https://api.github.com/users/JasonEtco/gists{/gist_id}", 211 | "starred_url": "https://api.github.com/users/JasonEtco/starred{/owner}{/repo}", 212 | "subscriptions_url": "https://api.github.com/users/JasonEtco/subscriptions", 213 | "organizations_url": "https://api.github.com/users/JasonEtco/orgs", 214 | "repos_url": "https://api.github.com/users/JasonEtco/repos", 215 | "events_url": "https://api.github.com/users/JasonEtco/events{/privacy}", 216 | "received_events_url": "https://api.github.com/users/JasonEtco/received_events", 217 | "type": "User", 218 | "site_admin": true 219 | }, 220 | "private": true, 221 | "html_url": "https://github.com/JasonEtco/tests", 222 | "description": null, 223 | "fork": false, 224 | "url": "https://api.github.com/repos/JasonEtco/tests", 225 | "forks_url": "https://api.github.com/repos/JasonEtco/tests/forks", 226 | "keys_url": "https://api.github.com/repos/JasonEtco/tests/keys{/key_id}", 227 | "collaborators_url": "https://api.github.com/repos/JasonEtco/tests/collaborators{/collaborator}", 228 | "teams_url": "https://api.github.com/repos/JasonEtco/tests/teams", 229 | "hooks_url": "https://api.github.com/repos/JasonEtco/tests/hooks", 230 | "issue_events_url": "https://api.github.com/repos/JasonEtco/tests/issues/events{/number}", 231 | "events_url": "https://api.github.com/repos/JasonEtco/tests/events", 232 | "assignees_url": "https://api.github.com/repos/JasonEtco/tests/assignees{/user}", 233 | "branches_url": "https://api.github.com/repos/JasonEtco/tests/branches{/branch}", 234 | "tags_url": "https://api.github.com/repos/JasonEtco/tests/tags", 235 | "blobs_url": "https://api.github.com/repos/JasonEtco/tests/git/blobs{/sha}", 236 | "git_tags_url": "https://api.github.com/repos/JasonEtco/tests/git/tags{/sha}", 237 | "git_refs_url": "https://api.github.com/repos/JasonEtco/tests/git/refs{/sha}", 238 | "trees_url": "https://api.github.com/repos/JasonEtco/tests/git/trees{/sha}", 239 | "statuses_url": "https://api.github.com/repos/JasonEtco/tests/statuses/{sha}", 240 | "languages_url": "https://api.github.com/repos/JasonEtco/tests/languages", 241 | "stargazers_url": "https://api.github.com/repos/JasonEtco/tests/stargazers", 242 | "contributors_url": "https://api.github.com/repos/JasonEtco/tests/contributors", 243 | "subscribers_url": "https://api.github.com/repos/JasonEtco/tests/subscribers", 244 | "subscription_url": "https://api.github.com/repos/JasonEtco/tests/subscription", 245 | "commits_url": "https://api.github.com/repos/JasonEtco/tests/commits{/sha}", 246 | "git_commits_url": "https://api.github.com/repos/JasonEtco/tests/git/commits{/sha}", 247 | "comments_url": "https://api.github.com/repos/JasonEtco/tests/comments{/number}", 248 | "issue_comment_url": "https://api.github.com/repos/JasonEtco/tests/issues/comments{/number}", 249 | "contents_url": "https://api.github.com/repos/JasonEtco/tests/contents/{+path}", 250 | "compare_url": "https://api.github.com/repos/JasonEtco/tests/compare/{base}...{head}", 251 | "merges_url": "https://api.github.com/repos/JasonEtco/tests/merges", 252 | "archive_url": "https://api.github.com/repos/JasonEtco/tests/{archive_format}{/ref}", 253 | "downloads_url": "https://api.github.com/repos/JasonEtco/tests/downloads", 254 | "issues_url": "https://api.github.com/repos/JasonEtco/tests/issues{/number}", 255 | "pulls_url": "https://api.github.com/repos/JasonEtco/tests/pulls{/number}", 256 | "milestones_url": "https://api.github.com/repos/JasonEtco/tests/milestones{/number}", 257 | "notifications_url": "https://api.github.com/repos/JasonEtco/tests/notifications{?since,all,participating}", 258 | "labels_url": "https://api.github.com/repos/JasonEtco/tests/labels{/name}", 259 | "releases_url": "https://api.github.com/repos/JasonEtco/tests/releases{/id}", 260 | "deployments_url": "https://api.github.com/repos/JasonEtco/tests/deployments", 261 | "created_at": "2017-11-13T14:12:17Z", 262 | "updated_at": "2018-01-08T03:09:57Z", 263 | "pushed_at": "2018-02-17T06:38:38Z", 264 | "git_url": "git://github.com/JasonEtco/tests.git", 265 | "ssh_url": "git@github.com:JasonEtco/tests.git", 266 | "clone_url": "https://github.com/JasonEtco/tests.git", 267 | "svn_url": "https://github.com/JasonEtco/tests", 268 | "homepage": null, 269 | "size": 8, 270 | "stargazers_count": 0, 271 | "watchers_count": 0, 272 | "language": null, 273 | "has_issues": true, 274 | "has_projects": true, 275 | "has_downloads": true, 276 | "has_wiki": true, 277 | "has_pages": false, 278 | "forks_count": 0, 279 | "mirror_url": null, 280 | "archived": false, 281 | "open_issues_count": 19, 282 | "license": null, 283 | "forks": 0, 284 | "open_issues": 19, 285 | "watchers": 0, 286 | "default_branch": "master" 287 | } 288 | }, 289 | "_links": { 290 | "self": { 291 | "href": "https://api.github.com/repos/JasonEtco/tests/pulls/32" 292 | }, 293 | "html": { 294 | "href": "https://github.com/JasonEtco/tests/pull/32" 295 | }, 296 | "issue": { 297 | "href": "https://api.github.com/repos/JasonEtco/tests/issues/32" 298 | }, 299 | "comments": { 300 | "href": "https://api.github.com/repos/JasonEtco/tests/issues/32/comments" 301 | }, 302 | "review_comments": { 303 | "href": "https://api.github.com/repos/JasonEtco/tests/pulls/32/comments" 304 | }, 305 | "review_comment": { 306 | "href": "https://api.github.com/repos/JasonEtco/tests/pulls/comments{/number}" 307 | }, 308 | "commits": { 309 | "href": "https://api.github.com/repos/JasonEtco/tests/pulls/32/commits" 310 | }, 311 | "statuses": { 312 | "href": "https://api.github.com/repos/JasonEtco/tests/statuses/fe5fe7386cc940a4d848718ea9b40ed0790861e1" 313 | } 314 | }, 315 | "author_association": "OWNER", 316 | "merged": false, 317 | "mergeable": null, 318 | "rebaseable": null, 319 | "mergeable_state": "unknown", 320 | "merged_by": null, 321 | "comments": 0, 322 | "review_comments": 0, 323 | "maintainer_can_modify": false, 324 | "commits": 2, 325 | "additions": 9, 326 | "deletions": 0, 327 | "changed_files": 1 328 | }, 329 | "before": "1409a3f61a27fe80a7170026fd73eaa2108a0108", 330 | "after": "fe5fe7386cc940a4d848718ea9b40ed0790861e1", 331 | "repository": { 332 | "id": 110556535, 333 | "name": "tests", 334 | "full_name": "JasonEtco/tests", 335 | "owner": { 336 | "login": "JasonEtco", 337 | "id": 10660468, 338 | "avatar_url": "https://avatars1.githubusercontent.com/u/10660468?v=4", 339 | "gravatar_id": "", 340 | "url": "https://api.github.com/users/JasonEtco", 341 | "html_url": "https://github.com/JasonEtco", 342 | "followers_url": "https://api.github.com/users/JasonEtco/followers", 343 | "following_url": "https://api.github.com/users/JasonEtco/following{/other_user}", 344 | "gists_url": "https://api.github.com/users/JasonEtco/gists{/gist_id}", 345 | "starred_url": "https://api.github.com/users/JasonEtco/starred{/owner}{/repo}", 346 | "subscriptions_url": "https://api.github.com/users/JasonEtco/subscriptions", 347 | "organizations_url": "https://api.github.com/users/JasonEtco/orgs", 348 | "repos_url": "https://api.github.com/users/JasonEtco/repos", 349 | "events_url": "https://api.github.com/users/JasonEtco/events{/privacy}", 350 | "received_events_url": "https://api.github.com/users/JasonEtco/received_events", 351 | "type": "User", 352 | "site_admin": true 353 | }, 354 | "private": true, 355 | "html_url": "https://github.com/JasonEtco/tests", 356 | "description": null, 357 | "fork": false, 358 | "url": "https://api.github.com/repos/JasonEtco/tests", 359 | "forks_url": "https://api.github.com/repos/JasonEtco/tests/forks", 360 | "keys_url": "https://api.github.com/repos/JasonEtco/tests/keys{/key_id}", 361 | "collaborators_url": "https://api.github.com/repos/JasonEtco/tests/collaborators{/collaborator}", 362 | "teams_url": "https://api.github.com/repos/JasonEtco/tests/teams", 363 | "hooks_url": "https://api.github.com/repos/JasonEtco/tests/hooks", 364 | "issue_events_url": "https://api.github.com/repos/JasonEtco/tests/issues/events{/number}", 365 | "events_url": "https://api.github.com/repos/JasonEtco/tests/events", 366 | "assignees_url": "https://api.github.com/repos/JasonEtco/tests/assignees{/user}", 367 | "branches_url": "https://api.github.com/repos/JasonEtco/tests/branches{/branch}", 368 | "tags_url": "https://api.github.com/repos/JasonEtco/tests/tags", 369 | "blobs_url": "https://api.github.com/repos/JasonEtco/tests/git/blobs{/sha}", 370 | "git_tags_url": "https://api.github.com/repos/JasonEtco/tests/git/tags{/sha}", 371 | "git_refs_url": "https://api.github.com/repos/JasonEtco/tests/git/refs{/sha}", 372 | "trees_url": "https://api.github.com/repos/JasonEtco/tests/git/trees{/sha}", 373 | "statuses_url": "https://api.github.com/repos/JasonEtco/tests/statuses/{sha}", 374 | "languages_url": "https://api.github.com/repos/JasonEtco/tests/languages", 375 | "stargazers_url": "https://api.github.com/repos/JasonEtco/tests/stargazers", 376 | "contributors_url": "https://api.github.com/repos/JasonEtco/tests/contributors", 377 | "subscribers_url": "https://api.github.com/repos/JasonEtco/tests/subscribers", 378 | "subscription_url": "https://api.github.com/repos/JasonEtco/tests/subscription", 379 | "commits_url": "https://api.github.com/repos/JasonEtco/tests/commits{/sha}", 380 | "git_commits_url": "https://api.github.com/repos/JasonEtco/tests/git/commits{/sha}", 381 | "comments_url": "https://api.github.com/repos/JasonEtco/tests/comments{/number}", 382 | "issue_comment_url": "https://api.github.com/repos/JasonEtco/tests/issues/comments{/number}", 383 | "contents_url": "https://api.github.com/repos/JasonEtco/tests/contents/{+path}", 384 | "compare_url": "https://api.github.com/repos/JasonEtco/tests/compare/{base}...{head}", 385 | "merges_url": "https://api.github.com/repos/JasonEtco/tests/merges", 386 | "archive_url": "https://api.github.com/repos/JasonEtco/tests/{archive_format}{/ref}", 387 | "downloads_url": "https://api.github.com/repos/JasonEtco/tests/downloads", 388 | "issues_url": "https://api.github.com/repos/JasonEtco/tests/issues{/number}", 389 | "pulls_url": "https://api.github.com/repos/JasonEtco/tests/pulls{/number}", 390 | "milestones_url": "https://api.github.com/repos/JasonEtco/tests/milestones{/number}", 391 | "notifications_url": "https://api.github.com/repos/JasonEtco/tests/notifications{?since,all,participating}", 392 | "labels_url": "https://api.github.com/repos/JasonEtco/tests/labels{/name}", 393 | "releases_url": "https://api.github.com/repos/JasonEtco/tests/releases{/id}", 394 | "deployments_url": "https://api.github.com/repos/JasonEtco/tests/deployments", 395 | "created_at": "2017-11-13T14:12:17Z", 396 | "updated_at": "2018-01-08T03:09:57Z", 397 | "pushed_at": "2018-02-17T06:38:38Z", 398 | "git_url": "git://github.com/JasonEtco/tests.git", 399 | "ssh_url": "git@github.com:JasonEtco/tests.git", 400 | "clone_url": "https://github.com/JasonEtco/tests.git", 401 | "svn_url": "https://github.com/JasonEtco/tests", 402 | "homepage": null, 403 | "size": 8, 404 | "stargazers_count": 0, 405 | "watchers_count": 0, 406 | "language": null, 407 | "has_issues": true, 408 | "has_projects": true, 409 | "has_downloads": true, 410 | "has_wiki": true, 411 | "has_pages": false, 412 | "forks_count": 0, 413 | "mirror_url": null, 414 | "archived": false, 415 | "open_issues_count": 19, 416 | "license": null, 417 | "forks": 0, 418 | "open_issues": 19, 419 | "watchers": 0, 420 | "default_branch": "master" 421 | }, 422 | "sender": { 423 | "login": "JasonEtco", 424 | "id": 10660468, 425 | "avatar_url": "https://avatars1.githubusercontent.com/u/10660468?v=4", 426 | "gravatar_id": "", 427 | "url": "https://api.github.com/users/JasonEtco", 428 | "html_url": "https://github.com/JasonEtco", 429 | "followers_url": "https://api.github.com/users/JasonEtco/followers", 430 | "following_url": "https://api.github.com/users/JasonEtco/following{/other_user}", 431 | "gists_url": "https://api.github.com/users/JasonEtco/gists{/gist_id}", 432 | "starred_url": "https://api.github.com/users/JasonEtco/starred{/owner}{/repo}", 433 | "subscriptions_url": "https://api.github.com/users/JasonEtco/subscriptions", 434 | "organizations_url": "https://api.github.com/users/JasonEtco/orgs", 435 | "repos_url": "https://api.github.com/users/JasonEtco/repos", 436 | "events_url": "https://api.github.com/users/JasonEtco/events{/privacy}", 437 | "received_events_url": "https://api.github.com/users/JasonEtco/received_events", 438 | "type": "User", 439 | "site_admin": true 440 | }, 441 | "installation": { 442 | "id": 1 443 | } 444 | } -------------------------------------------------------------------------------- /tests/fixtures/payloads/push.json: -------------------------------------------------------------------------------- 1 | { 2 | "ref": "refs/heads/master", 3 | "before": "46e3daa3218850cf3262eed1ff0f1eecb0769d2f", 4 | "after": "e06c237a0c041f5a0a61f1c361f7a1d6f3d669af", 5 | "created": false, 6 | "deleted": false, 7 | "forced": false, 8 | "base_ref": null, 9 | "compare": "https://github.com/JasonEtco/tests/compare/46e3daa32188...e06c237a0c04", 10 | "commits": [ 11 | { 12 | "id": "e06c237a0c041f5a0a61f1c361f7a1d6f3d669af", 13 | "tree_id": "6447cfbafd313fbf6a0d15c36c411d988d176cc3", 14 | "distinct": true, 15 | "message": "Create sadfsadf", 16 | "timestamp": "2018-02-17T18:47:11-05:00", 17 | "url": "https://github.com/JasonEtco/tests/commit/e06c237a0c041f5a0a61f1c361f7a1d6f3d669af", 18 | "author": { 19 | "name": "Jason Etcovitch", 20 | "email": "jasonetco@github.com", 21 | "username": "JasonEtco" 22 | }, 23 | "committer": { 24 | "name": "GitHub", 25 | "email": "noreply@github.com", 26 | "username": "web-flow" 27 | }, 28 | "added": [ 29 | "sadfsadf" 30 | ], 31 | "removed": [ 32 | 33 | ], 34 | "modified": [ 35 | 36 | ] 37 | } 38 | ], 39 | "head_commit": { 40 | "id": "e06c237a0c041f5a0a61f1c361f7a1d6f3d669af", 41 | "tree_id": "6447cfbafd313fbf6a0d15c36c411d988d176cc3", 42 | "distinct": true, 43 | "message": "Create sadfsadf", 44 | "timestamp": "2018-02-17T18:47:11-05:00", 45 | "url": "https://github.com/JasonEtco/tests/commit/e06c237a0c041f5a0a61f1c361f7a1d6f3d669af", 46 | "author": { 47 | "name": "Jason Etcovitch", 48 | "email": "jasonetco@github.com", 49 | "username": "JasonEtco" 50 | }, 51 | "committer": { 52 | "name": "GitHub", 53 | "email": "noreply@github.com", 54 | "username": "web-flow" 55 | }, 56 | "added": [ 57 | "sadfsadf" 58 | ], 59 | "removed": [ 60 | 61 | ], 62 | "modified": [ 63 | 64 | ] 65 | }, 66 | "repository": { 67 | "id": 110556535, 68 | "name": "tests", 69 | "full_name": "JasonEtco/tests", 70 | "owner": { 71 | "name": "JasonEtco", 72 | "email": "jasonetco@github.com", 73 | "login": "JasonEtco", 74 | "id": 10660468, 75 | "avatar_url": "https://avatars1.githubusercontent.com/u/10660468?v=4", 76 | "gravatar_id": "", 77 | "url": "https://api.github.com/users/JasonEtco", 78 | "html_url": "https://github.com/JasonEtco", 79 | "followers_url": "https://api.github.com/users/JasonEtco/followers", 80 | "following_url": "https://api.github.com/users/JasonEtco/following{/other_user}", 81 | "gists_url": "https://api.github.com/users/JasonEtco/gists{/gist_id}", 82 | "starred_url": "https://api.github.com/users/JasonEtco/starred{/owner}{/repo}", 83 | "subscriptions_url": "https://api.github.com/users/JasonEtco/subscriptions", 84 | "organizations_url": "https://api.github.com/users/JasonEtco/orgs", 85 | "repos_url": "https://api.github.com/users/JasonEtco/repos", 86 | "events_url": "https://api.github.com/users/JasonEtco/events{/privacy}", 87 | "received_events_url": "https://api.github.com/users/JasonEtco/received_events", 88 | "type": "User", 89 | "site_admin": true 90 | }, 91 | "private": true, 92 | "html_url": "https://github.com/JasonEtco/tests", 93 | "description": null, 94 | "fork": false, 95 | "url": "https://github.com/JasonEtco/tests", 96 | "forks_url": "https://api.github.com/repos/JasonEtco/tests/forks", 97 | "keys_url": "https://api.github.com/repos/JasonEtco/tests/keys{/key_id}", 98 | "collaborators_url": "https://api.github.com/repos/JasonEtco/tests/collaborators{/collaborator}", 99 | "teams_url": "https://api.github.com/repos/JasonEtco/tests/teams", 100 | "hooks_url": "https://api.github.com/repos/JasonEtco/tests/hooks", 101 | "issue_events_url": "https://api.github.com/repos/JasonEtco/tests/issues/events{/number}", 102 | "events_url": "https://api.github.com/repos/JasonEtco/tests/events", 103 | "assignees_url": "https://api.github.com/repos/JasonEtco/tests/assignees{/user}", 104 | "branches_url": "https://api.github.com/repos/JasonEtco/tests/branches{/branch}", 105 | "tags_url": "https://api.github.com/repos/JasonEtco/tests/tags", 106 | "blobs_url": "https://api.github.com/repos/JasonEtco/tests/git/blobs{/sha}", 107 | "git_tags_url": "https://api.github.com/repos/JasonEtco/tests/git/tags{/sha}", 108 | "git_refs_url": "https://api.github.com/repos/JasonEtco/tests/git/refs{/sha}", 109 | "trees_url": "https://api.github.com/repos/JasonEtco/tests/git/trees{/sha}", 110 | "statuses_url": "https://api.github.com/repos/JasonEtco/tests/statuses/{sha}", 111 | "languages_url": "https://api.github.com/repos/JasonEtco/tests/languages", 112 | "stargazers_url": "https://api.github.com/repos/JasonEtco/tests/stargazers", 113 | "contributors_url": "https://api.github.com/repos/JasonEtco/tests/contributors", 114 | "subscribers_url": "https://api.github.com/repos/JasonEtco/tests/subscribers", 115 | "subscription_url": "https://api.github.com/repos/JasonEtco/tests/subscription", 116 | "commits_url": "https://api.github.com/repos/JasonEtco/tests/commits{/sha}", 117 | "git_commits_url": "https://api.github.com/repos/JasonEtco/tests/git/commits{/sha}", 118 | "comments_url": "https://api.github.com/repos/JasonEtco/tests/comments{/number}", 119 | "issue_comment_url": "https://api.github.com/repos/JasonEtco/tests/issues/comments{/number}", 120 | "contents_url": "https://api.github.com/repos/JasonEtco/tests/contents/{+path}", 121 | "compare_url": "https://api.github.com/repos/JasonEtco/tests/compare/{base}...{head}", 122 | "merges_url": "https://api.github.com/repos/JasonEtco/tests/merges", 123 | "archive_url": "https://api.github.com/repos/JasonEtco/tests/{archive_format}{/ref}", 124 | "downloads_url": "https://api.github.com/repos/JasonEtco/tests/downloads", 125 | "issues_url": "https://api.github.com/repos/JasonEtco/tests/issues{/number}", 126 | "pulls_url": "https://api.github.com/repos/JasonEtco/tests/pulls{/number}", 127 | "milestones_url": "https://api.github.com/repos/JasonEtco/tests/milestones{/number}", 128 | "notifications_url": "https://api.github.com/repos/JasonEtco/tests/notifications{?since,all,participating}", 129 | "labels_url": "https://api.github.com/repos/JasonEtco/tests/labels{/name}", 130 | "releases_url": "https://api.github.com/repos/JasonEtco/tests/releases{/id}", 131 | "deployments_url": "https://api.github.com/repos/JasonEtco/tests/deployments", 132 | "created_at": 1510582337, 133 | "updated_at": "2018-01-08T03:09:57Z", 134 | "pushed_at": 1518911232, 135 | "git_url": "git://github.com/JasonEtco/tests.git", 136 | "ssh_url": "git@github.com:JasonEtco/tests.git", 137 | "clone_url": "https://github.com/JasonEtco/tests.git", 138 | "svn_url": "https://github.com/JasonEtco/tests", 139 | "homepage": null, 140 | "size": 10, 141 | "stargazers_count": 0, 142 | "watchers_count": 0, 143 | "language": null, 144 | "has_issues": true, 145 | "has_projects": true, 146 | "has_downloads": true, 147 | "has_wiki": true, 148 | "has_pages": false, 149 | "forks_count": 0, 150 | "mirror_url": null, 151 | "archived": false, 152 | "open_issues_count": 19, 153 | "license": null, 154 | "forks": 0, 155 | "open_issues": 19, 156 | "watchers": 0, 157 | "default_branch": "master", 158 | "stargazers": 0, 159 | "master_branch": "master" 160 | }, 161 | "pusher": { 162 | "name": "JasonEtco", 163 | "email": "jasonetco@github.com" 164 | }, 165 | "sender": { 166 | "login": "JasonEtco", 167 | "id": 10660468, 168 | "avatar_url": "https://avatars1.githubusercontent.com/u/10660468?v=4", 169 | "gravatar_id": "", 170 | "url": "https://api.github.com/users/JasonEtco", 171 | "html_url": "https://github.com/JasonEtco", 172 | "followers_url": "https://api.github.com/users/JasonEtco/followers", 173 | "following_url": "https://api.github.com/users/JasonEtco/following{/other_user}", 174 | "gists_url": "https://api.github.com/users/JasonEtco/gists{/gist_id}", 175 | "starred_url": "https://api.github.com/users/JasonEtco/starred{/owner}{/repo}", 176 | "subscriptions_url": "https://api.github.com/users/JasonEtco/subscriptions", 177 | "organizations_url": "https://api.github.com/users/JasonEtco/orgs", 178 | "repos_url": "https://api.github.com/users/JasonEtco/repos", 179 | "events_url": "https://api.github.com/users/JasonEtco/events{/privacy}", 180 | "received_events_url": "https://api.github.com/users/JasonEtco/received_events", 181 | "type": "User", 182 | "site_admin": true 183 | }, 184 | "installation": { 185 | "id": 1 186 | } 187 | } -------------------------------------------------------------------------------- /tests/helpers.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs') 2 | const path = require('path') 3 | const { Application } = require('probot') 4 | const plugin = require('..') 5 | 6 | const loadDiff = exports.loadDiff = filename => { 7 | return Promise.resolve({ 8 | data: fs.readFileSync(path.join(__dirname, 'fixtures', 'diffs', filename + '.txt'), 'utf8'), 9 | headers: { 'content-length': 1 } 10 | }) 11 | } 12 | 13 | exports.loadConfig = filename => { 14 | return Promise.resolve({ 15 | data: { 16 | content: fs.readFileSync(path.join(__dirname, 'fixtures', 'configs', filename + '.yml'), 'base64') 17 | } 18 | }) 19 | } 20 | 21 | exports.gimmeApp = () => { 22 | const logger = { 23 | trace: jest.fn(), 24 | debug: jest.fn(), 25 | info: jest.fn(), 26 | warn: jest.fn(), 27 | error: jest.fn(), 28 | fatal: jest.fn() 29 | } 30 | 31 | const app = new Application({ 32 | logger, 33 | app: { 34 | getInstallationAccessToken: jest.fn().mockResolvedValue('test'), 35 | getSignedJsonWebToken: jest.fn().mockReturnValue('test') 36 | } 37 | }) 38 | app.load(plugin) 39 | 40 | const github = { 41 | issues: { 42 | create: jest.fn(data => Promise.resolve({ data })).mockName('issues.create'), 43 | createLabel: jest.fn().mockName('issues.createLabel'), 44 | update: jest.fn().mockName('issues.update'), 45 | createComment: jest.fn().mockName('issues.createComment'), 46 | listComments: jest.fn(() => Promise.resolve({ data: [] })).mockName('issues.listComments') 47 | }, 48 | search: { 49 | issuesAndPullRequests: jest.fn(() => Promise.resolve({ data: { total_count: 0, items: [] } })).mockName('search.issuesAndPullRequests') 50 | }, 51 | git: { 52 | getCommit: jest.fn(() => Promise.resolve({ data: { parents: [1] } })).mockName('git.getCommit') 53 | }, 54 | repos: { 55 | // Response for getting content from '.github/todo.yml' 56 | getContents: jest.fn(() => { 57 | throw { status: 404 } // eslint-disable-line 58 | }).mockName('repos.getContents'), 59 | getCommit: jest.fn(() => loadDiff('basic')).mockName('repos.getCommit') 60 | }, 61 | pulls: { 62 | get: jest.fn(() => loadDiff('basic')).mockName('pulls.get') 63 | }, 64 | hook: { 65 | before: jest.fn() 66 | } 67 | } 68 | // Passes the mocked out GitHub API into out app instance 69 | app.auth = () => Promise.resolve(github) 70 | return { app, github } 71 | } 72 | -------------------------------------------------------------------------------- /tests/issue-rename-handler.test.js: -------------------------------------------------------------------------------- 1 | const { Application } = require('probot') 2 | const issueEdited = require('./fixtures/payloads/issues.edited.json') 3 | const plugin = require('..') 4 | 5 | describe('issue-rename-handler', () => { 6 | let app, github, event 7 | 8 | beforeEach(() => { 9 | app = new Application() 10 | event = { name: 'issues', payload: issueEdited } 11 | 12 | github = { 13 | issues: { 14 | update: jest.fn(), 15 | createComment: jest.fn() 16 | } 17 | } 18 | 19 | app.auth = jest.fn(() => Promise.resolve(github)) 20 | app.load(plugin) 21 | }) 22 | 23 | it('un-edits the issue title', async () => { 24 | await app.receive(event) 25 | expect(github.issues.update.mock.calls[0][0]).toMatchSnapshot() 26 | expect(github.issues.createComment.mock.calls[0][0]).toMatchSnapshot() 27 | }) 28 | 29 | it('only acts if the title is edited', async () => { 30 | event.payload.changes = {} 31 | await app.receive(event) 32 | expect(github.issues.update).not.toHaveBeenCalled() 33 | expect(github.issues.createComment).not.toHaveBeenCalled() 34 | }) 35 | }) 36 | -------------------------------------------------------------------------------- /tests/lib/__snapshots__/main-loop.test.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`main-loop throws on an invalid config 1`] = `"\\"pizza\\" is not allowed"`; 4 | -------------------------------------------------------------------------------- /tests/lib/check-for-body.test.js: -------------------------------------------------------------------------------- 1 | const checkForBody = require('../../lib/utils/check-for-body') 2 | 3 | describe('check-for-body', () => { 4 | let changes, config 5 | 6 | beforeEach(() => { 7 | changes = [{ 8 | type: 'add', 9 | ln: 257, 10 | content: '+ // TODO: A title' 11 | }, { 12 | type: 'add', 13 | ln: 258, 14 | content: '+ // BODY: A body' 15 | }] 16 | 17 | config = { 18 | bodyKeyword: 'BODY' 19 | } 20 | }) 21 | 22 | it('returns the body if its present', () => { 23 | const actual = checkForBody(changes, 0, config) 24 | expect(actual).toBe('A body') 25 | }) 26 | 27 | it('returns false if there is no next line', () => { 28 | const actual = checkForBody([changes[0]], 0, config) 29 | expect(actual).toBe(false) 30 | }) 31 | 32 | it('returns false if the next line does not have a body', () => { 33 | changes = [ 34 | changes[0], 35 | { content: '+ // PIZZA' } 36 | ] 37 | 38 | const actual = checkForBody(changes, 0, config) 39 | expect(actual).toBe(false) 40 | }) 41 | 42 | it('allows for multiple keywords', () => { 43 | config.bodyKeyword = ['PIZZA', 'text'] 44 | 45 | changes[1].content = '// PIZZA This is a pizza' 46 | const pizza = checkForBody(changes, 0, config) 47 | expect(pizza).toBe('This is a pizza') 48 | 49 | changes[1].content = '// text This is a test' 50 | const text = checkForBody(changes, 0, config) 51 | expect(text).toBe('This is a test') 52 | }) 53 | 54 | it('Allows multiple consecutive BODY lines', () => { 55 | changes.push({ 56 | type: 'add', 57 | ln: 259, 58 | content: '+ // BODY: Another body line' 59 | }) 60 | const body = checkForBody(changes, 0, config) 61 | expect(body).toBe('A body Another body line') 62 | }) 63 | 64 | it('Properly splits empty lines in multi-line BODY', () => { 65 | changes.push({ 66 | type: 'add', 67 | ln: 259, 68 | content: '+ // BODY:' 69 | }) 70 | changes.push({ 71 | type: 'add', 72 | ln: 260, 73 | content: '+ // BODY: Test' 74 | }) 75 | const body = checkForBody(changes, 0, config) 76 | expect(body).toBe('A body\nTest') 77 | }) 78 | }) 79 | -------------------------------------------------------------------------------- /tests/lib/check-for-duplicate-issue.test.js: -------------------------------------------------------------------------------- 1 | const checkForDuplicateIssue = require('../../lib/utils/check-for-duplicate-issue') 2 | const payload = require('../fixtures/payloads/push.json') 3 | 4 | describe('check-for-duplicate-issue', () => { 5 | let context 6 | 7 | beforeEach(() => { 8 | context = { 9 | payload, 10 | github: { 11 | search: { 12 | issuesAndPullRequests: jest.fn(() => Promise.resolve({ data: { items: [] } })) 13 | } 14 | }, 15 | todos: [] 16 | } 17 | }) 18 | 19 | it('returns undefined if no duplicate issue is found', async () => { 20 | const actual = await checkForDuplicateIssue(context, 'hello') 21 | expect(actual).toBe(undefined) 22 | }) 23 | 24 | it('returns the issue if a duplicate issue is found in context.todos', async () => { 25 | context.todos.push('hello') 26 | const actual = await checkForDuplicateIssue(context, 'hello') 27 | expect(actual).toBe('hello') 28 | }) 29 | 30 | it('returns the issue if a duplicate issue is found by searching', async () => { 31 | const mock = { data: { total_count: 1, items: [{ title: 'hello' }] } } 32 | context.github.search.issuesAndPullRequests.mockReturnValueOnce(mock) 33 | const actual = await checkForDuplicateIssue(context, 'hello') 34 | expect(actual).toEqual(mock.data.items[0]) 35 | }) 36 | }) 37 | -------------------------------------------------------------------------------- /tests/lib/generate-label.test.js: -------------------------------------------------------------------------------- 1 | const generateLabel = require('../../lib/utils/generate-label') 2 | 3 | describe('generate-label', () => { 4 | let context 5 | 6 | beforeEach(() => { 7 | context = { 8 | repo: (obj) => ({ 9 | owner: 'JasonEtco', 10 | repo: 'todo', 11 | ...obj 12 | }), 13 | github: { 14 | issues: { 15 | createLabel: jest.fn() 16 | } 17 | } 18 | } 19 | }) 20 | 21 | it('calls github.createLabel()', async () => { 22 | const labels = await generateLabel(context, { label: true }) 23 | expect(Array.isArray(labels)).toBe(true) 24 | expect(context.github.issues.createLabel).toHaveBeenCalledTimes(1) 25 | }) 26 | 27 | it('returns an array of the default label name', async () => { 28 | const labels = await generateLabel(context, { label: true }) 29 | expect(Array.isArray(labels)).toBe(true) 30 | expect(labels).toEqual(['todo :spiral_notepad:']) 31 | }) 32 | 33 | it('returns an empty array with label: false', async () => { 34 | const labels = await generateLabel(context, { label: false }) 35 | expect(Array.isArray(labels)).toBe(true) 36 | expect(labels.length).toEqual(0) 37 | expect(labels).toEqual([]) 38 | }) 39 | 40 | it('returns an array with the provided string', async () => { 41 | const label = 'I am a label' 42 | const labels = await generateLabel(context, { label }) 43 | expect(Array.isArray(labels)).toBe(true) 44 | expect(labels.length).toEqual(1) 45 | expect(labels).toEqual([label]) 46 | }) 47 | 48 | it('returns an array with the provided array of strings', async () => { 49 | const labels = await generateLabel(context, { label: ['pizza', 'dog'] }) 50 | expect(Array.isArray(labels)).toBe(true) 51 | expect(labels.length).toEqual(2) 52 | expect(labels).toEqual(['pizza', 'dog']) 53 | }) 54 | }) 55 | -------------------------------------------------------------------------------- /tests/lib/ignore-repos.test.js: -------------------------------------------------------------------------------- 1 | const ignoreRepos = require('../../lib/ignore-repos') 2 | 3 | describe('ignoreRepos', () => { 4 | let spy, func, context 5 | 6 | beforeEach(() => { 7 | spy = jest.fn() 8 | func = ignoreRepos(spy) 9 | context = { 10 | repo: () => ({ owner: 'JasonEtco', repo: 'todo' }), 11 | log: jest.fn() 12 | } 13 | }) 14 | 15 | it('does call the handler if the repo is not being ignored', () => { 16 | process.env.IGNORED_REPOS = 'JasonEtco/pizza' 17 | func(context) 18 | expect(spy).toHaveBeenCalled() 19 | }) 20 | 21 | it('does not call the handler if the repo is being ignored', () => { 22 | process.env.IGNORED_REPOS = 'JasonEtco/todo' 23 | func(context) 24 | expect(spy).not.toHaveBeenCalled() 25 | }) 26 | 27 | afterEach(() => { 28 | delete process.env.IGNORED_REPOS 29 | }) 30 | }) 31 | -------------------------------------------------------------------------------- /tests/lib/main-loop.test.js: -------------------------------------------------------------------------------- 1 | const mainLoop = require('../../lib/utils/main-loop') 2 | const { loadDiff } = require('../helpers') 3 | 4 | describe('main-loop', () => { 5 | let context, handler 6 | 7 | beforeEach(() => { 8 | context = { 9 | event: 'push', 10 | payload: require('../fixtures/payloads/push.json'), 11 | config: jest.fn(), 12 | log: f => f, 13 | repo: (obj) => ({ 14 | owner: 'JasonEtco', 15 | repo: 'todo', 16 | ...obj 17 | }), 18 | github: { 19 | issues: { 20 | createLabel: jest.fn() 21 | }, 22 | repos: { 23 | getCommit: jest.fn(() => loadDiff('basic')).mockName('repos.getCommit') 24 | } 25 | } 26 | } 27 | 28 | handler = jest.fn() 29 | }) 30 | 31 | it('uses the default config if context.config return value does not have todo', async () => { 32 | context.config = jest.fn(() => Promise.resolve({ 33 | pizza: true 34 | })) 35 | 36 | await mainLoop(context, handler) 37 | expect(handler).toHaveBeenCalled() 38 | }) 39 | 40 | it('throws on an invalid config', async () => { 41 | context.config = jest.fn(() => Promise.resolve({ 42 | todo: { pizza: true } 43 | })) 44 | 45 | await expect(mainLoop(context, handler)) 46 | .rejects 47 | .toThrowErrorMatchingSnapshot() 48 | expect(handler).not.toHaveBeenCalled() 49 | }) 50 | 51 | it('does nothing if a title is only whitespace', async () => { 52 | context.github.repos.getCommit.mockReturnValue(Promise.resolve(loadDiff('title-with-whitespace'))) 53 | await mainLoop(context, handler) 54 | expect(handler).not.toHaveBeenCalled() 55 | }) 56 | 57 | it('does nothing if a keyword is in the middle of a sentence', async () => { 58 | context.github.repos.getCommit.mockReturnValue(Promise.resolve(loadDiff('middle-of-sentence'))) 59 | await mainLoop(context, handler) 60 | expect(handler).not.toHaveBeenCalled() 61 | }) 62 | }) 63 | -------------------------------------------------------------------------------- /tests/lib/should-exclude-file.test.js: -------------------------------------------------------------------------------- 1 | const shouldExcludeFile = require('../../lib/utils/should-exclude-file') 2 | 3 | describe('shouldExcludeFile', () => { 4 | let logger 5 | 6 | beforeEach(() => { 7 | logger = { debug: jest.fn() } 8 | }) 9 | 10 | it('returns true for the .github/config.yml file', () => { 11 | const actual = shouldExcludeFile(logger, '.github/config.yml') 12 | expect(actual).toBe(true) 13 | }) 14 | 15 | it('returns true for any files that match the alwaysExclude RegEx', () => { 16 | const actual = shouldExcludeFile(logger, 'example.min.js') 17 | expect(actual).toBe(true) 18 | }) 19 | 20 | it('returns true for any files that match the provded pattern', () => { 21 | const actual = shouldExcludeFile(logger, 'pizza.js', 'pi') 22 | expect(actual).toBe(true) 23 | }) 24 | }) 25 | -------------------------------------------------------------------------------- /tests/pull-request-handler.test.js: -------------------------------------------------------------------------------- 1 | const { gimmeApp, loadConfig, loadDiff } = require('./helpers') 2 | const pullRequestOpened = require('./fixtures/payloads/pull_request.opened.json') 3 | 4 | describe('pull-request-handler', () => { 5 | let app, github 6 | const event = { name: 'pull_request', payload: pullRequestOpened } 7 | 8 | beforeEach(() => { 9 | const gimme = gimmeApp() 10 | app = gimme.app 11 | github = gimme.github 12 | }) 13 | 14 | it('comments on a pull request', async () => { 15 | await app.receive(event) 16 | expect(github.issues.createComment).toHaveBeenCalledTimes(1) 17 | expect(github.issues.createComment.mock.calls[0]).toMatchSnapshot() 18 | }) 19 | 20 | it('comments on a pull request and mentions the assigned user', async () => { 21 | github.repos.getContents.mockReturnValueOnce(loadConfig('autoAssignString')) 22 | await app.receive(event) 23 | expect(github.issues.createComment.mock.calls[0]).toMatchSnapshot() 24 | }) 25 | 26 | it('comments on a pull request and mentions the assigned users', async () => { 27 | github.repos.getContents.mockReturnValueOnce(loadConfig('autoAssignArr')) 28 | await app.receive(event) 29 | expect(github.issues.createComment.mock.calls[0]).toMatchSnapshot() 30 | }) 31 | 32 | it('does not create duplicate comments', async () => { 33 | github.issues.listComments.mockReturnValueOnce(Promise.resolve({ 34 | data: [{ 35 | body: '## I am an example title' 36 | }] 37 | })) 38 | 39 | await app.receive(event) 40 | expect(github.issues.createComment).not.toHaveBeenCalled() 41 | }) 42 | 43 | it('creates many (5) comments', async () => { 44 | github.pulls.get.mockReturnValue(loadDiff('many')) 45 | await app.receive(event) 46 | expect(github.issues.createComment).toHaveBeenCalledTimes(5) 47 | }) 48 | 49 | it('ignores changes to the config file', async () => { 50 | github.pulls.get.mockReturnValue(loadDiff('config')) 51 | await app.receive(event) 52 | expect(github.issues.createComment).not.toHaveBeenCalled() 53 | }) 54 | 55 | it('ignores changes to the bin directory', async () => { 56 | github.pulls.get.mockReturnValue(loadDiff('bin')) 57 | github.repos.getContents.mockReturnValueOnce(loadConfig('excludeBin')) 58 | await app.receive(event) 59 | expect(github.issues.createComment).not.toHaveBeenCalled() 60 | }) 61 | 62 | it('works with a string as the keyword config', async () => { 63 | github.repos.getContents.mockReturnValueOnce(loadConfig('keywordsString')) 64 | github.pulls.get.mockReturnValue(loadDiff('custom-keyword')) 65 | await app.receive(event) 66 | expect(github.issues.createComment.mock.calls[0]).toMatchSnapshot() 67 | }) 68 | 69 | it('creates a comment with a body line', async () => { 70 | github.pulls.get.mockReturnValue(loadDiff('body')) 71 | await app.receive(event) 72 | expect(github.issues.createComment.mock.calls[0]).toMatchSnapshot() 73 | }) 74 | }) 75 | -------------------------------------------------------------------------------- /tests/pull-request-merge-handler.test.js: -------------------------------------------------------------------------------- 1 | const pullRequestClosed = require('./fixtures/payloads/pull_request.closed.json') 2 | const { gimmeApp, loadConfig, loadDiff } = require('./helpers') 3 | 4 | describe('pull-request-merged-handler', () => { 5 | let app, github 6 | const event = { name: 'pull_request', payload: pullRequestClosed } 7 | 8 | beforeEach(() => { 9 | const gimme = gimmeApp() 10 | app = gimme.app 11 | github = gimme.github 12 | }) 13 | 14 | it('does nothing on an unmerged, closed PR', async () => { 15 | await app.receive({ 16 | ...event, 17 | payload: { 18 | ...event.payload, 19 | pull_request: { merged: false } 20 | } 21 | }) 22 | 23 | expect(github.issues.create).not.toHaveBeenCalled() 24 | }) 25 | 26 | it('creates an issue', async () => { 27 | await app.receive(event) 28 | expect(github.issues.create).toHaveBeenCalledTimes(1) 29 | expect(github.issues.create.mock.calls[0]).toMatchSnapshot() 30 | }) 31 | 32 | it('creates an issue with a truncated title', async () => { 33 | github.pulls.get.mockReturnValue(loadDiff('long-title')) 34 | await app.receive(event) 35 | expect(github.issues.create).toHaveBeenCalledTimes(1) 36 | expect(github.issues.create.mock.calls[0]).toMatchSnapshot() 37 | }) 38 | 39 | it('creates an issue without assigning anyone', async () => { 40 | github.repos.getContents.mockReturnValueOnce(loadConfig('autoAssignFalse')) 41 | await app.receive(event) 42 | expect(github.issues.create.mock.calls[0]).toMatchSnapshot() 43 | }) 44 | 45 | it('creates an issue and assigns the configured user', async () => { 46 | github.repos.getContents.mockReturnValueOnce(loadConfig('autoAssignString')) 47 | await app.receive(event) 48 | expect(github.issues.create.mock.calls[0]).toMatchSnapshot() 49 | }) 50 | 51 | it('creates an issue and assigns the configured users', async () => { 52 | github.repos.getContents.mockReturnValueOnce(loadConfig('autoAssignArr')) 53 | await app.receive(event) 54 | expect(github.issues.create.mock.calls[0]).toMatchSnapshot() 55 | }) 56 | 57 | it('does not create any issues if no todos are found', async () => { 58 | github.pulls.get.mockReturnValue(loadDiff('none')) 59 | await app.receive(event) 60 | expect(github.issues.create).not.toHaveBeenCalled() 61 | }) 62 | 63 | it('does not create an issue that already exists', async () => { 64 | github.search.issuesAndPullRequests.mockReturnValueOnce(Promise.resolve({ 65 | data: { total_count: 1, items: [{ title: 'I am an example title', state: 'open' }] } 66 | })) 67 | await app.receive(event) 68 | expect(github.issues.create).not.toHaveBeenCalled() 69 | }) 70 | 71 | it('creates many (5) issues', async () => { 72 | github.pulls.get.mockReturnValue(loadDiff('many')) 73 | await app.receive(event) 74 | expect(github.issues.create).toHaveBeenCalledTimes(5) 75 | }) 76 | 77 | it('ignores changes to the config file', async () => { 78 | github.pulls.get.mockReturnValue(loadDiff('config')) 79 | await app.receive(event) 80 | expect(github.issues.create).not.toHaveBeenCalled() 81 | }) 82 | 83 | it('ignores changes to the bin directory', async () => { 84 | github.pulls.get.mockReturnValue(loadDiff('bin')) 85 | github.repos.getContents.mockReturnValueOnce(loadConfig('excludeBin')) 86 | await app.receive(event) 87 | expect(github.issues.createComment).not.toHaveBeenCalled() 88 | }) 89 | 90 | it('creates an issue with a body line', async () => { 91 | github.pulls.get.mockReturnValue(loadDiff('body')) 92 | await app.receive(event) 93 | expect(github.issues.create.mock.calls[0]).toMatchSnapshot() 94 | }) 95 | 96 | it('reopens a closed issue', async () => { 97 | github.search.issuesAndPullRequests.mockReturnValueOnce(Promise.resolve({ 98 | data: { total_count: 1, items: [{ number: 1, title: 'I am an example title', state: 'closed' }] } 99 | })) 100 | await app.receive(event) 101 | expect(github.issues.update).toHaveBeenCalledTimes(1) 102 | expect(github.issues.createComment).toHaveBeenCalledTimes(1) 103 | expect(github.issues.createComment.mock.calls[0]).toMatchSnapshot() 104 | expect(github.issues.create).not.toHaveBeenCalled() 105 | }) 106 | 107 | it('respects the reopenClosed config', async () => { 108 | github.repos.getContents.mockReturnValueOnce(loadConfig('reopenClosedFalse')) 109 | github.search.issuesAndPullRequests.mockReturnValueOnce(Promise.resolve({ 110 | data: { total_count: 1, items: [{ title: 'I am an example title', state: 'closed' }] } 111 | })) 112 | await app.receive(event) 113 | expect(github.issues.update).not.toHaveBeenCalled() 114 | expect(github.issues.createComment).not.toHaveBeenCalled() 115 | expect(github.issues.create).not.toHaveBeenCalled() 116 | }) 117 | }) 118 | -------------------------------------------------------------------------------- /tests/push-handler.test.js: -------------------------------------------------------------------------------- 1 | const pushEvent = require('./fixtures/payloads/push.json') 2 | const { gimmeApp, loadConfig, loadDiff } = require('./helpers') 3 | 4 | describe('push-handler', () => { 5 | let app, github 6 | const event = { name: 'push', payload: pushEvent } 7 | 8 | beforeEach(() => { 9 | const gimme = gimmeApp() 10 | app = gimme.app 11 | github = gimme.github 12 | }) 13 | 14 | it('creates an issue', async () => { 15 | await app.receive(event) 16 | expect(github.issues.create).toHaveBeenCalledTimes(1) 17 | expect(github.issues.create.mock.calls[0]).toMatchSnapshot() 18 | }) 19 | 20 | it('creates an issue with an @todo comment', async () => { 21 | github.repos.getCommit.mockReturnValue(loadDiff('at-todo')) 22 | await app.receive(event) 23 | expect(github.issues.create).toHaveBeenCalledTimes(1) 24 | expect(github.issues.create.mock.calls[0]).toMatchSnapshot() 25 | }) 26 | 27 | it('creates an issue with a truncated title', async () => { 28 | github.repos.getCommit.mockReturnValue(loadDiff('long-title')) 29 | await app.receive(event) 30 | expect(github.issues.create).toHaveBeenCalledTimes(1) 31 | expect(github.issues.create.mock.calls[0]).toMatchSnapshot() 32 | }) 33 | 34 | it('creates an issue without assigning anyone', async () => { 35 | github.repos.getContents.mockReturnValueOnce(loadConfig('autoAssignFalse')) 36 | await app.receive(event) 37 | expect(github.issues.create.mock.calls[0]).toMatchSnapshot() 38 | }) 39 | 40 | it('creates an issue and assigns the configured user', async () => { 41 | github.repos.getContents.mockReturnValueOnce(loadConfig('autoAssignString')) 42 | await app.receive(event) 43 | expect(github.issues.create.mock.calls[0]).toMatchSnapshot() 44 | }) 45 | 46 | it('creates an issue and assigns the configured users', async () => { 47 | github.repos.getContents.mockReturnValueOnce(loadConfig('autoAssignArr')) 48 | await app.receive(event) 49 | expect(github.issues.create.mock.calls[0]).toMatchSnapshot() 50 | }) 51 | 52 | it('does not create any issues if no todos are found', async () => { 53 | github.repos.getCommit.mockReturnValue(loadDiff('none')) 54 | await app.receive(event) 55 | expect(github.issues.create).not.toHaveBeenCalled() 56 | }) 57 | 58 | it('does not create any issues if the push is not on the default branch', async () => { 59 | await app.receive({ name: 'push', payload: { ...pushEvent, ref: 'not-master' } }) 60 | expect(github.issues.create).not.toHaveBeenCalled() 61 | }) 62 | 63 | it('does not create an issue that already exists', async () => { 64 | github.search.issuesAndPullRequests.mockReturnValueOnce(Promise.resolve({ 65 | data: { total_count: 1, items: [{ title: 'I am an example title', state: 'open' }] } 66 | })) 67 | await app.receive(event) 68 | expect(github.issues.create).not.toHaveBeenCalled() 69 | }) 70 | 71 | it('does not create the same issue twice in the same run', async () => { 72 | github.repos.getCommit.mockReturnValue(loadDiff('duplicate')) 73 | await app.receive(event) 74 | expect(github.issues.create).toHaveBeenCalledTimes(1) 75 | }) 76 | 77 | it('creates an issue if the search does not have an issue with the correct title', async () => { 78 | github.search.issuesAndPullRequests.mockReturnValueOnce(Promise.resolve({ 79 | data: { total_count: 1, items: [{ title: 'Not found', state: 'open' }] } 80 | })) 81 | await app.receive(event) 82 | expect(github.issues.create).toHaveBeenCalledTimes(1) 83 | }) 84 | 85 | it('creates many (5) issues', async () => { 86 | github.repos.getCommit.mockReturnValue(loadDiff('many')) 87 | await app.receive(event) 88 | expect(github.issues.create).toHaveBeenCalledTimes(5) 89 | expect(github.issues.create.mock.calls).toMatchSnapshot() 90 | }) 91 | 92 | it('ignores changes to the config file', async () => { 93 | github.repos.getCommit.mockReturnValue(loadDiff('config')) 94 | await app.receive(event) 95 | expect(github.issues.create).not.toHaveBeenCalled() 96 | }) 97 | 98 | it('ignores changes to the bin directory', async () => { 99 | github.repos.getCommit.mockReturnValue(loadDiff('bin')) 100 | github.repos.getContents.mockReturnValueOnce(loadConfig('excludeBin')) 101 | await app.receive(event) 102 | expect(github.issues.create).not.toHaveBeenCalled() 103 | }) 104 | 105 | it('ignores pushes not to master', async () => { 106 | const e = { event: event.event, payload: { ...event.payload, ref: 'not/the/master/branch' } } 107 | await app.receive(e) 108 | expect(github.issues.create).not.toHaveBeenCalled() 109 | }) 110 | 111 | it('ignores merge commits', async () => { 112 | github.git.getCommit.mockReturnValueOnce(Promise.resolve({ 113 | data: { parents: [1, 2] } 114 | })) 115 | await app.receive(event) 116 | expect(github.issues.create).not.toHaveBeenCalled() 117 | }) 118 | 119 | it('creates an issue with a body line', async () => { 120 | github.repos.getCommit.mockReturnValue(loadDiff('body')) 121 | await app.receive(event) 122 | expect(github.issues.create.mock.calls[0]).toMatchSnapshot() 123 | }) 124 | 125 | it('creates an issue with a custom keyword config', async () => { 126 | github.repos.getCommit.mockReturnValue(loadDiff('custom-keyword')) 127 | github.repos.getContents.mockReturnValueOnce(loadConfig('keywordsString')) 128 | await app.receive(event) 129 | expect(github.issues.create.mock.calls[0]).toMatchSnapshot() 130 | }) 131 | 132 | it('creates an issue with a body line with one body keyword', async () => { 133 | github.repos.getCommit.mockReturnValue(loadDiff('body')) 134 | github.repos.getContents.mockReturnValueOnce(loadConfig('bodyString')) 135 | await app.receive(event) 136 | expect(github.issues.create.mock.calls[0]).toMatchSnapshot() 137 | }) 138 | 139 | it('reopens a closed issue', async () => { 140 | github.search.issuesAndPullRequests.mockReturnValueOnce(Promise.resolve({ 141 | data: { total_count: 1, items: [{ number: 1, title: 'I am an example title', state: 'closed' }] } 142 | })) 143 | await app.receive(event) 144 | expect(github.issues.update).toHaveBeenCalledTimes(1) 145 | expect(github.issues.createComment).toHaveBeenCalledTimes(1) 146 | expect(github.issues.createComment.mock.calls[0]).toMatchSnapshot() 147 | expect(github.issues.create).not.toHaveBeenCalled() 148 | }) 149 | 150 | it('respects the reopenClosed config', async () => { 151 | github.repos.getContents.mockReturnValueOnce(loadConfig('reopenClosedFalse')) 152 | github.search.issuesAndPullRequests.mockReturnValueOnce(Promise.resolve({ 153 | data: { total_count: 1, items: [{ title: 'I am an example title', state: 'closed' }] } 154 | })) 155 | await app.receive(event) 156 | expect(github.issues.update).not.toHaveBeenCalled() 157 | expect(github.issues.createComment).not.toHaveBeenCalled() 158 | expect(github.issues.create).not.toHaveBeenCalled() 159 | }) 160 | 161 | it('does not show the blob if blobLines is false', async () => { 162 | github.repos.getContents.mockReturnValueOnce(loadConfig('blobLinesFalse')) 163 | await app.receive(event) 164 | expect(github.issues.create.mock.calls[0]).toMatchSnapshot() 165 | }) 166 | 167 | it('cuts the blobLines', async () => { 168 | github.repos.getCommit.mockReturnValue(loadDiff('blob-past-end')) 169 | await app.receive(event) 170 | expect(github.issues.create.mock.calls[0]).toMatchSnapshot() 171 | }) 172 | 173 | it('does nothing with a diff over the max size', async () => { 174 | github.repos.getCommit.mockReturnValue(Promise.resolve({ headers: { 'content-length': 2000001 } })) 175 | await app.receive(event) 176 | expect(github.issues.create).not.toHaveBeenCalled() 177 | }) 178 | 179 | it('creates an issue and respects GHE_HOST', async () => { 180 | process.env.GHE_HOST = 'fakegittillyoumakegit.com' 181 | await app.receive(event) 182 | expect(github.issues.create).toHaveBeenCalledTimes(1) 183 | expect(github.issues.create.mock.calls[0]).toMatchSnapshot() 184 | delete process.env.GHE_HOST 185 | }) 186 | }) 187 | -------------------------------------------------------------------------------- /tests/setup.js: -------------------------------------------------------------------------------- 1 | process.env.APP_NAME = 'todo-dev' 2 | process.env.LOG_LEVEL = 'fatal' 3 | --------------------------------------------------------------------------------