From 56f6696abb6c8d24ab5c229cc8f74f16f5e90b15 Mon Sep 17 00:00:00 2001 From: Jon Bretman <jon.bretman@gmail.com> Date: Sat, 11 Feb 2017 19:18:54 +0000 Subject: [PATCH] Add more tests (#98) --- lib/api/github.js | 13 +- test/_fixtures/config/file.js | 2 +- test/api/__snapshots__/github.spec.js.snap | 634 +++++++++++++++++++++ test/api/github.spec.js | 421 ++++++++++++++ test/api/npm.spec.js | 34 ++ test/helpers/package-json.spec.js | 5 + test/helpers/versions.spec.js | 26 + 7 files changed, 1123 insertions(+), 12 deletions(-) create mode 100644 test/api/__snapshots__/github.spec.js.snap create mode 100644 test/api/github.spec.js create mode 100644 test/api/npm.spec.js diff --git a/lib/api/github.js b/lib/api/github.js index 13ea7d96a3..7dffbb9c69 100644 --- a/lib/api/github.js +++ b/lib/api/github.js @@ -51,6 +51,7 @@ async function initRepo(repoName, token, endpoint) { logger.error(`GitHub init error: ${JSON.stringify(err)}`); throw err; } + return config; } // Search @@ -223,17 +224,7 @@ async function getFileContent(filePath, branchName = config.baseBranch) { } async function getFileJson(filePath, branchName = config.baseBranch) { - try { - const file = await getFile(filePath, branchName); - return JSON.parse(new Buffer(file, 'base64').toString()); - } catch (error) { - if (error.statusCode === 404) { - // If file not found, then return null JSON - return null; - } - // Propagate if it's any other error - throw error; - } + return JSON.parse(await getFileContent(filePath, branchName)); } // Add a new commit, create branch if not existing diff --git a/test/_fixtures/config/file.js b/test/_fixtures/config/file.js index 54eda96d35..7ccccf04de 100644 --- a/test/_fixtures/config/file.js +++ b/test/_fixtures/config/file.js @@ -1,6 +1,6 @@ module.exports = { token: 'abcdefg', - logLevel: 'verbose', + logLevel: 'error', repositories: [ 'singapore/lint-condo', { diff --git a/test/api/__snapshots__/github.spec.js.snap b/test/api/__snapshots__/github.spec.js.snap new file mode 100644 index 0000000000..c454a51244 --- /dev/null +++ b/test/api/__snapshots__/github.spec.js.snap @@ -0,0 +1,634 @@ +exports[`api/github addAssignees(issueNo, assignees) should add the given assignees to the issue 1`] = ` +Array [ + Array [ + "repos/some/repo/issues/42/assignees", + Object { + "body": Object { + "assignees": Array [ + "someuser", + "someotheruser", + ], + }, + }, + ], +] +`; + +exports[`api/github addLabels(issueNo, labels) should add the given labels to the issue 1`] = ` +Array [ + Array [ + "repos/some/repo/issues/42/labels", + Object { + "body": "[\"foo\",\"bar\"]", + }, + ], +] +`; + +exports[`api/github addReviewers(issueNo, reviewers) should add the given reviewers to the PR 1`] = ` +Array [ + Array [ + "repos/some/repo/pulls/42/requested_reviewers", + Object { + "body": Object { + "reviewers": Array [ + "someuser", + "someotheruser", + ], + }, + "headers": Object { + "accept": "application/vnd.github.black-cat-preview+json", + }, + }, + ], +] +`; + +exports[`api/github branchExists(branchName) should propagate unknown errors 1`] = ` +Array [ + Array [ + "repos/some/repo", + ], + Array [ + "repos/some/repo/git/refs/heads/master", + ], + Array [ + "repos/some/repo/git/commits/1234", + ], + Array [ + "repos/some/repo/git/refs/heads/thebranchname", + ], +] +`; + +exports[`api/github branchExists(branchName) should return false if a 404 is returned 1`] = ` +Array [ + Array [ + "repos/some/repo", + ], + Array [ + "repos/some/repo/git/refs/heads/master", + ], + Array [ + "repos/some/repo/git/commits/1234", + ], + Array [ + "repos/some/repo/git/refs/heads/thebranchname", + ], +] +`; + +exports[`api/github branchExists(branchName) should return false if a non-200 response is returned 1`] = ` +Array [ + Array [ + "repos/some/repo", + ], + Array [ + "repos/some/repo/git/refs/heads/master", + ], + Array [ + "repos/some/repo/git/commits/1234", + ], + Array [ + "repos/some/repo/git/refs/heads/thebranchname", + ], +] +`; + +exports[`api/github branchExists(branchName) should return true if the branch exists 1`] = ` +Array [ + Array [ + "repos/some/repo", + ], + Array [ + "repos/some/repo/git/refs/heads/master", + ], + Array [ + "repos/some/repo/git/commits/1234", + ], + Array [ + "repos/some/repo/git/refs/heads/thebranchname", + ], +] +`; + +exports[`api/github commitFilesToBranch(branchName, files, message, parentBranch) should add a commit to a new branch if the branch does not already exist 1`] = ` +Array [ + Array [ + "repos/some/repo", + ], + Array [ + "repos/some/repo/git/refs/heads/master", + ], + Array [ + "repos/some/repo/git/commits/1234", + ], + Array [ + "repos/some/repo/git/refs/heads/master", + ], + Array [ + "repos/some/repo/git/commits/1111", + ], + Array [ + "repos/some/repo/git/refs/heads/package.json", + ], +] +`; + +exports[`api/github commitFilesToBranch(branchName, files, message, parentBranch) should add a commit to a new branch if the branch does not already exist 2`] = ` +Array [ + Array [ + "repos/some/repo/git/blobs", + Object { + "body": Object { + "content": "aGVsbG8gd29ybGQ=", + "encoding": "base64", + }, + }, + ], + Array [ + "repos/some/repo/git/trees", + Object { + "body": Object { + "base_tree": "2222", + "tree": Array [ + Object { + "mode": "100644", + "path": "package.json", + "sha": "3333", + "type": "blob", + }, + ], + }, + }, + ], + Array [ + "repos/some/repo/git/commits", + Object { + "body": Object { + "message": "my other commit message", + "parents": Array [ + "1111", + ], + "tree": "4444", + }, + }, + ], + Array [ + "repos/some/repo/git/refs", + Object { + "body": Object { + "ref": "refs/heads/package.json", + "sha": "5555", + }, + }, + ], +] +`; + +exports[`api/github commitFilesToBranch(branchName, files, message, parentBranch) should add a commit to a new branch if the branch does not already exist 3`] = `Array []`; + +exports[`api/github commitFilesToBranch(branchName, files, message, parentBranch) should add a new commit to the branch 1`] = ` +Array [ + Array [ + "repos/some/repo", + ], + Array [ + "repos/some/repo/git/refs/heads/master", + ], + Array [ + "repos/some/repo/git/commits/1234", + ], + Array [ + "repos/some/repo/git/refs/heads/master", + ], + Array [ + "repos/some/repo/git/commits/1111", + ], + Array [ + "repos/some/repo/git/refs/heads/package.json", + ], +] +`; + +exports[`api/github commitFilesToBranch(branchName, files, message, parentBranch) should add a new commit to the branch 2`] = ` +Array [ + Array [ + "repos/some/repo/git/blobs", + Object { + "body": Object { + "content": "aGVsbG8gd29ybGQ=", + "encoding": "base64", + }, + }, + ], + Array [ + "repos/some/repo/git/trees", + Object { + "body": Object { + "base_tree": "2222", + "tree": Array [ + Object { + "mode": "100644", + "path": "package.json", + "sha": "3333", + "type": "blob", + }, + ], + }, + }, + ], + Array [ + "repos/some/repo/git/commits", + Object { + "body": Object { + "message": "my commit message", + "parents": Array [ + "1111", + ], + "tree": "4444", + }, + }, + ], +] +`; + +exports[`api/github commitFilesToBranch(branchName, files, message, parentBranch) should add a new commit to the branch 3`] = ` +Array [ + Array [ + "repos/some/repo/git/refs/heads/package.json", + Object { + "body": Object { + "force": true, + "sha": "5555", + }, + }, + ], +] +`; + +exports[`api/github createPr(branchName, title, body) should create and return a PR object 1`] = ` +Object { + "displayNumber": "Pull Request #123", + "number": 123, +} +`; + +exports[`api/github createPr(branchName, title, body) should create and return a PR object 2`] = ` +Array [ + Array [ + "repos/some/repo/pulls", + Object { + "body": Object { + "base": "master", + "body": "Hello world", + "head": "some-branch", + "title": "The Title", + }, + }, + ], +] +`; + +exports[`api/github findFilePaths(fileName) should return the files matching the fileName 1`] = ` +Array [ + Array [ + "repos/some/repo", + ], + Array [ + "repos/some/repo/git/refs/heads/master", + ], + Array [ + "repos/some/repo/git/commits/1234", + ], + Array [ + "search/code?q=repo:some/repo+filename:package.json", + ], +] +`; + +exports[`api/github findFilePaths(fileName) should return the files matching the fileName 2`] = ` +Array [ + "package.json", + "src/app/package.json", + "src/otherapp/package.json", +] +`; + +exports[`api/github findPr(branchName, prTitle, state) should return a PR object 1`] = ` +Array [ + Array [ + "repos/some/repo", + ], + Array [ + "repos/some/repo/git/refs/heads/master", + ], + Array [ + "repos/some/repo/git/commits/1234", + ], + Array [ + "repos/some/repo/pulls?head=theowner:master&state=all", + ], +] +`; + +exports[`api/github findPr(branchName, prTitle, state) should return a PR object 2`] = ` +Object { + "displayNumber": "Pull Request #42", + "number": 42, + "state": "open", + "title": "PR Title", +} +`; + +exports[`api/github findPr(branchName, prTitle, state) should return null if no PR's are found 1`] = ` +Array [ + Array [ + "repos/some/repo", + ], + Array [ + "repos/some/repo/git/refs/heads/master", + ], + Array [ + "repos/some/repo/git/commits/1234", + ], + Array [ + "repos/some/repo/pulls?head=theowner:master&state=all", + ], +] +`; + +exports[`api/github findPr(branchName, prTitle, state) should set the isClosed attribute of the PR to true if the PR is closed 1`] = ` +Array [ + Array [ + "repos/some/repo", + ], + Array [ + "repos/some/repo/git/refs/heads/master", + ], + Array [ + "repos/some/repo/git/commits/1234", + ], + Array [ + "repos/some/repo/pulls?head=theowner:master&state=all", + ], +] +`; + +exports[`api/github findPr(branchName, prTitle, state) should set the isClosed attribute of the PR to true if the PR is closed 2`] = ` +Object { + "displayNumber": "Pull Request #42", + "isClosed": true, + "number": 42, + "state": "closed", + "title": "PR Title", +} +`; + +exports[`api/github getBranchPr(branchName) should return null if no PR exists 1`] = ` +Array [ + Array [ + "repos/some/repo", + ], + Array [ + "repos/some/repo/git/refs/heads/master", + ], + Array [ + "repos/some/repo/git/commits/1234", + ], + Array [ + "repos/some/repo/pulls?state=open&base=master&head=theowner:somebranch", + ], +] +`; + +exports[`api/github getBranchPr(branchName) should return the PR object 1`] = ` +Array [ + Array [ + "repos/some/repo", + ], + Array [ + "repos/some/repo/git/refs/heads/master", + ], + Array [ + "repos/some/repo/git/commits/1234", + ], + Array [ + "repos/some/repo/pulls?state=open&base=master&head=theowner:somebranch", + ], + Array [ + "repos/some/repo/pulls/91", + ], +] +`; + +exports[`api/github getBranchPr(branchName) should return the PR object 2`] = ` +Object { + "additions": 1, + "base": Object { + "sha": "1234", + }, + "canRebase": true, + "commits": 1, + "deletions": 1, + "displayNumber": "Pull Request #91", + "number": 91, +} +`; + +exports[`api/github getFile(filePatch, branchName) should return the encoded file content 1`] = ` +Array [ + Array [ + "repos/some/repo", + ], + Array [ + "repos/some/repo/git/refs/heads/master", + ], + Array [ + "repos/some/repo/git/commits/1234", + ], + Array [ + "repos/some/repo/contents/package.json?ref=master", + ], +] +`; + +exports[`api/github getFileContent(filePatch, branchName) should return null if GitHub returns a 404 1`] = ` +Array [ + Array [ + "repos/some/repo", + ], + Array [ + "repos/some/repo/git/refs/heads/master", + ], + Array [ + "repos/some/repo/git/commits/1234", + ], + Array [ + "repos/some/repo/contents/package.json?ref=master", + ], +] +`; + +exports[`api/github getFileContent(filePatch, branchName) should return the encoded file content 1`] = ` +Array [ + Array [ + "repos/some/repo", + ], + Array [ + "repos/some/repo/git/refs/heads/master", + ], + Array [ + "repos/some/repo/git/commits/1234", + ], + Array [ + "repos/some/repo/contents/package.json?ref=master", + ], +] +`; + +exports[`api/github getFileJson(filePatch, branchName) should return the file contents parsed as JSON 1`] = ` +Array [ + Array [ + "repos/some/repo", + ], + Array [ + "repos/some/repo/git/refs/heads/master", + ], + Array [ + "repos/some/repo/git/commits/1234", + ], + Array [ + "repos/some/repo/contents/package.json?ref=master", + ], +] +`; + +exports[`api/github getFileJson(filePatch, branchName) should return the file contents parsed as JSON 2`] = ` +Object { + "hello": "world", +} +`; + +exports[`api/github getPr(prNo) should return a PR object - 0 1`] = ` +Object { + "base": Object { + "sha": "1234", + }, + "displayNumber": "Pull Request #1", + "isClosed": true, + "number": 1, + "state": "closed", +} +`; + +exports[`api/github getPr(prNo) should return a PR object - 1 1`] = ` +Object { + "base": Object { + "sha": "1234", + }, + "displayNumber": "Pull Request #1", + "isUnmergeable": true, + "mergeable_state": "dirty", + "number": 1, + "state": "open", +} +`; + +exports[`api/github getPr(prNo) should return a PR object - 2 1`] = ` +Object { + "base": Object { + "sha": "5678", + }, + "displayNumber": "Pull Request #1", + "isStale": true, + "number": 1, + "state": "open", +} +`; + +exports[`api/github initRepo should initialise the config for the repo - 0 1`] = ` +Array [ + Array [ + "repos/some/repo", + ], + Array [ + "repos/some/repo/git/refs/heads/master", + ], + Array [ + "repos/some/repo/git/commits/1234", + ], +] +`; + +exports[`api/github initRepo should initialise the config for the repo - 0 2`] = ` +Object { + "baseCommitSHA": "1234", + "baseTreeSHA": "5678", + "defaultBranch": "master", + "owner": "theowner", + "repoName": "some/repo", +} +`; + +exports[`api/github initRepo should initialise the config for the repo - 1 1`] = ` +Array [ + Array [ + "repos/some/repo", + ], + Array [ + "repos/some/repo/git/refs/heads/master", + ], + Array [ + "repos/some/repo/git/commits/1234", + ], +] +`; + +exports[`api/github initRepo should initialise the config for the repo - 1 2`] = ` +Object { + "baseCommitSHA": "1234", + "baseTreeSHA": "5678", + "defaultBranch": "master", + "owner": "theowner", + "repoName": "some/repo", +} +`; + +exports[`api/github initRepo should initialise the config for the repo - 2 1`] = ` +Array [ + Array [ + "repos/some/repo", + ], + Array [ + "repos/some/repo/git/refs/heads/master", + ], + Array [ + "repos/some/repo/git/commits/1234", + ], +] +`; + +exports[`api/github initRepo should initialise the config for the repo - 2 2`] = ` +Object { + "baseCommitSHA": "1234", + "baseTreeSHA": "5678", + "defaultBranch": "master", + "owner": "theowner", + "repoName": "some/repo", +} +`; + +exports[`api/github updatePr(prNo, title, body) should update the PR 1`] = ` +Array [ + Array [ + "repos/some/repo/pulls/1234", + Object { + "body": Object { + "body": "Hello world again", + "title": "The New Title", + }, + }, + ], +] +`; diff --git a/test/api/github.spec.js b/test/api/github.spec.js new file mode 100644 index 0000000000..8008f8b9e1 --- /dev/null +++ b/test/api/github.spec.js @@ -0,0 +1,421 @@ +describe('api/github', () => { + let github; + let ghGot; + beforeEach(() => { + // clean up env + delete process.env.GITHUB_TOKEN; + delete process.env.GITHUB_ENDPOINT; + + // reset module + jest.resetModules(); + jest.mock('gh-got'); + github = require('../../lib/api/github'); + ghGot = require('gh-got'); + }); + + async function initRepo(...args) { + // repo info + ghGot.mockImplementationOnce(() => ({ + body: { + owner: { + login: 'theowner', + }, + default_branch: 'master', + }, + })); + // getBranchCommit + ghGot.mockImplementationOnce(() => ({ + body: { + object: { + sha: '1234', + }, + }, + })); + // getCommitTree + ghGot.mockImplementationOnce(() => ({ + body: { + tree: { + sha: '5678', + }, + }, + })); + return github.initRepo(...args); + } + + describe('initRepo', () => { + [ + [undefined, ['mytoken'], 'mytoken', undefined], + [undefined, ['mytoken', 'https://my.custom.endpoint/'], 'mytoken', 'https://my.custom.endpoint/'], + ['myenvtoken', [], 'myenvtoken', undefined], + ].forEach(([envToken, args, token, endpoint], i) => { + it(`should initialise the config for the repo - ${i}`, async () => { + if (envToken !== undefined) { + process.env.GITHUB_TOKEN = envToken; + } + const config = await initRepo('some/repo', ...args); + expect(ghGot.mock.calls).toMatchSnapshot(); + expect(config).toMatchSnapshot(); + expect(process.env.GITHUB_TOKEN).toBe(token); + expect(process.env.GITHUB_ENDPOINT).toBe(endpoint); + }); + }); + it('should throw an error if no token is provided', async () => { + let err; + try { + await github.initRepo('some/repo'); + } catch (e) { + err = e; + } + expect(err.message).toBe('No token found for GitHub repository some/repo'); + }); + }); + describe('findFilePaths(fileName)', () => { + it('should return the files matching the fileName', async () => { + await initRepo('some/repo', 'token'); + ghGot.mockImplementationOnce(() => ({ + body: { + items: [ + { name: 'package.json', path: '/package.json' }, + { name: 'package.json.something-else', path: 'some-dir/package.json.some-thing-else' }, + { name: 'package.json', path: 'src/app/package.json' }, + { name: 'package.json', path: 'src/otherapp/package.json' }, + ], + }, + })); + const files = await github.findFilePaths('package.json'); + expect(ghGot.mock.calls).toMatchSnapshot(); + expect(files).toMatchSnapshot(); + }); + }); + describe('branchExists(branchName)', () => { + it('should return true if the branch exists', async () => { + await initRepo('some/repo', 'token'); + ghGot.mockImplementationOnce(() => ({ + statusCode: 200, + })); + const exists = await github.branchExists('thebranchname'); + expect(ghGot.mock.calls).toMatchSnapshot(); + expect(exists).toBe(true); + }); + it('should return false if a non-200 response is returned', async () => { + await initRepo('some/repo', 'token'); + ghGot.mockImplementationOnce(() => ({ + statusCode: 123, + })); + const exists = await github.branchExists('thebranchname'); + expect(ghGot.mock.calls).toMatchSnapshot(); + expect(exists).toBe(false); + }); + it('should return false if a 404 is returned', async () => { + await initRepo('some/repo', 'token'); + ghGot.mockImplementationOnce(() => Promise.reject({ + statusCode: 404, + })); + const exists = await github.branchExists('thebranchname'); + expect(ghGot.mock.calls).toMatchSnapshot(); + expect(exists).toBe(false); + }); + it('should propagate unknown errors', async () => { + await initRepo('some/repo', 'token'); + ghGot.mockImplementationOnce(() => Promise.reject(new Error('Something went wrong'))); + let err; + try { + await github.branchExists('thebranchname'); + } catch (e) { + err = e; + } + expect(ghGot.mock.calls).toMatchSnapshot(); + expect(err.message).toBe('Something went wrong'); + }); + }); + describe('getBranchPr(branchName)', () => { + it('should return null if no PR exists', async () => { + await initRepo('some/repo', 'token'); + ghGot.mockImplementationOnce(() => ({ + body: [], + })); + const pr = await github.getBranchPr('somebranch'); + expect(ghGot.mock.calls).toMatchSnapshot(); + expect(pr).toBe(null); + }); + it('should return the PR object', async () => { + await initRepo('some/repo', 'token'); + ghGot.mockImplementationOnce(() => ({ + body: [ + { number: 91 }, + ], + })); + ghGot.mockImplementationOnce(() => ({ + body: { + number: 91, + additions: 1, + deletions: 1, + commits: 1, + base: { + sha: '1234', + }, + }, + })); + const pr = await github.getBranchPr('somebranch'); + expect(ghGot.mock.calls).toMatchSnapshot(); + expect(pr).toMatchSnapshot(); + }); + }); + describe('addAssignees(issueNo, assignees)', () => { + it('should add the given assignees to the issue', async () => { + await initRepo('some/repo', 'token'); + await github.addAssignees(42, ['someuser', 'someotheruser']); + expect(ghGot.post.mock.calls).toMatchSnapshot(); + }); + }); + describe('addReviewers(issueNo, reviewers)', () => { + it('should add the given reviewers to the PR', async () => { + await initRepo('some/repo', 'token'); + await github.addReviewers(42, ['someuser', 'someotheruser']); + expect(ghGot.post.mock.calls).toMatchSnapshot(); + }); + }); + describe('addLabels(issueNo, labels)', () => { + it('should add the given labels to the issue', async () => { + await initRepo('some/repo', 'token'); + await github.addLabels(42, ['foo', 'bar']); + expect(ghGot.post.mock.calls).toMatchSnapshot(); + }); + }); + describe('findPr(branchName, prTitle, state)', () => { + it('should return a PR object', async () => { + await initRepo('some/repo', 'token'); + ghGot.mockImplementationOnce(() => ({ + body: [ + { title: 'PR Title', state: 'open', number: 42 }, + ], + })); + const pr = await github.findPr('master', 'PR Title'); + expect(ghGot.mock.calls).toMatchSnapshot(); + expect(pr).toMatchSnapshot(); + }); + it('should return null if no PR\'s are found', async () => { + await initRepo('some/repo', 'token'); + ghGot.mockImplementationOnce(() => ({ + body: [], + })); + const pr = await github.findPr('master', 'PR Title'); + expect(ghGot.mock.calls).toMatchSnapshot(); + expect(pr).toBe(null); + }); + it('should set the isClosed attribute of the PR to true if the PR is closed', async () => { + await initRepo('some/repo', 'token'); + ghGot.mockImplementationOnce(() => ({ + body: [ + { title: 'PR Title', state: 'closed', number: 42 }, + ], + })); + const pr = await github.findPr('master'); + expect(ghGot.mock.calls).toMatchSnapshot(); + expect(pr).toMatchSnapshot(); + }); + }); + describe('checkForClosedPr(branchName, prTitle)', () => { + [ + ['some-branch', 'foo', true], + ['some-branch', 'bar', false], + ['some-branch', 'bop', false], + ].forEach(([branch, title, expected], i) => { + it(`should return true if a closed PR is found - ${i}`, async () => { + await initRepo('some/repo', 'token'); + ghGot.mockImplementationOnce(() => ({ + body: [ + { title: 'foo', head: { label: 'theowner:some-branch' } }, + { title: 'bar', head: { label: 'theowner:some-other-branch' } }, + { title: 'baz', head: { label: 'theowner:some-branch' } }, + ], + })); + const res = await github.checkForClosedPr(branch, title); + expect(res).toBe(expected); + }); + }); + }); + describe('createPr(branchName, title, body)', () => { + it('should create and return a PR object', async () => { + await initRepo('some/repo', 'token'); + ghGot.post.mockImplementationOnce(() => ({ + body: { + number: 123, + }, + })); + const pr = await github.createPr('some-branch', 'The Title', 'Hello world'); + expect(pr).toMatchSnapshot(); + expect(ghGot.post.mock.calls).toMatchSnapshot(); + }); + }); + describe('getPr(prNo)', () => { + it('should return null if no prNo is passed', async () => { + const pr = await github.getPr(null); + expect(pr).toBe(null); + }); + it('should return null if no PR is returned from GitHub', async () => { + await initRepo('some/repo', 'token'); + ghGot.mockImplementationOnce(() => ({ + body: null, + })); + const pr = await github.getPr(1234); + expect(pr).toBe(null); + }); + [ + { number: 1, state: 'closed', base: { sha: '1234' } }, + { number: 1, state: 'open', mergeable_state: 'dirty', base: { sha: '1234' } }, + { number: 1, state: 'open', base: { sha: '5678' } }, + ].forEach((body, i) => { + it(`should return a PR object - ${i}`, async () => { + await initRepo('some/repo', 'token'); + ghGot.mockImplementationOnce(() => ({ + body, + })); + const pr = await github.getPr(1234); + expect(pr).toMatchSnapshot(); + }); + }); + }); + describe('updatePr(prNo, title, body)', () => { + it('should update the PR', async () => { + await initRepo('some/repo', 'token'); + await github.updatePr(1234, 'The New Title', 'Hello world again'); + expect(ghGot.patch.mock.calls).toMatchSnapshot(); + }); + }); + describe('getFile(filePatch, branchName)', () => { + it('should return the encoded file content', async () => { + await initRepo('some/repo', 'token'); + ghGot.mockImplementationOnce(() => ({ + body: { + content: 'hello', + }, + })); + const content = await github.getFile('package.json'); + expect(ghGot.mock.calls).toMatchSnapshot(); + expect(content).toBe('hello'); + }); + }); + describe('getFileContent(filePatch, branchName)', () => { + it('should return the encoded file content', async () => { + await initRepo('some/repo', 'token'); + ghGot.mockImplementationOnce(() => ({ + body: { + content: Buffer.from('hello world').toString('base64'), + }, + })); + const content = await github.getFileContent('package.json'); + expect(ghGot.mock.calls).toMatchSnapshot(); + expect(content).toBe('hello world'); + }); + it('should return null if GitHub returns a 404', async () => { + await initRepo('some/repo', 'token'); + ghGot.mockImplementationOnce(() => { + const error = new Error(); + error.statusCode = 404; + throw error; + }); + const content = await github.getFileContent('package.json'); + expect(ghGot.mock.calls).toMatchSnapshot(); + expect(content).toBe(null); + }); + it('should return propagate unknown errors', async () => { + await initRepo('some/repo', 'token'); + ghGot.mockImplementationOnce(() => { + throw new Error('Something went wrong'); + }); + let err; + try { + await github.getFileContent('package.json'); + } catch (e) { + err = e; + } + expect(err.message).toBe('Something went wrong'); + }); + }); + describe('getFileJson(filePatch, branchName)', () => { + it('should return the file contents parsed as JSON', async () => { + await initRepo('some/repo', 'token'); + ghGot.mockImplementationOnce(() => ({ + body: { + content: Buffer.from('{"hello": "world"}').toString('base64'), + }, + })); + const content = await github.getFileJson('package.json'); + expect(ghGot.mock.calls).toMatchSnapshot(); + expect(content).toMatchSnapshot(); + }); + }); + describe('commitFilesToBranch(branchName, files, message, parentBranch)', () => { + beforeEach(async () => { + await initRepo('some/repo', 'token'); + + // getBranchCommit + ghGot.mockImplementationOnce(() => ({ + body: { + object: { + sha: '1111', + }, + }, + })); + + // getCommitTree + ghGot.mockImplementationOnce(() => ({ + body: { + tree: { + sha: '2222', + }, + }, + })); + + // createBlob + ghGot.post.mockImplementationOnce(() => ({ + body: { + sha: '3333', + }, + })); + + // createTree + ghGot.post.mockImplementationOnce(() => ({ + body: { + sha: '4444', + }, + })); + + // createCommit + ghGot.post.mockImplementationOnce(() => ({ + body: { + sha: '5555', + }, + })); + }); + it('should add a new commit to the branch', async () => { + // branchExists + ghGot.mockImplementationOnce(() => ({ + statusCode: 200, + })); + const files = [{ + name: 'package.json', + contents: 'hello world', + }]; + await github.commitFilesToBranch('package.json', files, 'my commit message'); + expect(ghGot.mock.calls).toMatchSnapshot(); + expect(ghGot.post.mock.calls).toMatchSnapshot(); + expect(ghGot.patch.mock.calls).toMatchSnapshot(); + }); + it('should add a commit to a new branch if the branch does not already exist', async () => { + // branchExists + ghGot.mockImplementationOnce(() => ({ + statusCode: 404, + })); + const files = [{ + name: 'package.json', + contents: 'hello world', + }]; + await github.commitFilesToBranch('package.json', files, 'my other commit message'); + expect(ghGot.mock.calls).toMatchSnapshot(); + expect(ghGot.post.mock.calls).toMatchSnapshot(); + expect(ghGot.patch.mock.calls).toMatchSnapshot(); + }); + }); +}); diff --git a/test/api/npm.spec.js b/test/api/npm.spec.js new file mode 100644 index 0000000000..fe9b5ac4e7 --- /dev/null +++ b/test/api/npm.spec.js @@ -0,0 +1,34 @@ +const npm = require('../../lib/api/npm'); +const got = require('got'); +const registryUrl = require('registry-url'); +const registryAuthToken = require('registry-auth-token'); + +jest.mock('registry-url'); +jest.mock('registry-auth-token'); +jest.mock('got'); + +describe('api/npm', () => { + beforeEach(() => { + jest.resetAllMocks(); + }); + it('should fetch package info from npm', async () => { + registryUrl.mockImplementation(() => 'https://npm.mycustomregistry.com/'); + got.mockImplementation(() => Promise.resolve({ body: { some: 'data' } })); + const res = await npm.getDependency('foobar'); + expect(res).toMatchObject({ some: 'data' }); + const call = got.mock.calls[0]; + expect(call).toMatchObject(['https://npm.mycustomregistry.com/foobar', { json: true, headers: {} }]); + }); + it('should send an authorization header if provided', async () => { + registryUrl.mockImplementation(() => 'https://npm.mycustomregistry.com/'); + registryAuthToken.mockImplementation(() => ({ type: 'Basic', token: '1234' })); + got.mockImplementation(() => Promise.resolve({ body: { some: 'data' } })); + const res = await npm.getDependency('foobar'); + expect(res).toMatchObject({ some: 'data' }); + const call = got.mock.calls[0]; + expect(call).toMatchObject(['https://npm.mycustomregistry.com/foobar', { json: true, + headers: { + authorization: 'Basic 1234', + } }]); + }); +}); diff --git a/test/helpers/package-json.spec.js b/test/helpers/package-json.spec.js index da891e3888..3d00eac92d 100644 --- a/test/helpers/package-json.spec.js +++ b/test/helpers/package-json.spec.js @@ -51,5 +51,10 @@ describe('helpers/package-json', () => { packageJson.setNewValue(input01Content, 'devDependencies', 'angular-sanitize', '1.6.1'); testContent.should.equal(outputContent); }); + it('handles the case where the desired version is already supported', () => { + const testContent = + packageJson.setNewValue(input01Content, 'devDependencies', 'angular-touch', '1.5.8'); + testContent.should.equal(input01Content); + }); }); }); diff --git a/test/helpers/versions.spec.js b/test/helpers/versions.spec.js index 318c27fb03..c68aa2c942 100644 --- a/test/helpers/versions.spec.js +++ b/test/helpers/versions.spec.js @@ -91,6 +91,32 @@ describe('helpers/versions', () => { ]; versionsHelper.determineUpgrades(qJson, '^2.0.0', defaultConfig).should.eql(upgradeVersions); }); + it('should ignore unstable versions if the current version is stable', () => { + versionsHelper.determineUpgrades({ + name: 'amazing-package', + versions: { + '1.0.0': {}, + '1.1.0-beta': {}, + }, + }, '1.0.0', defaultConfig).should.eql([]); + }); + it('should allow unstable versions if the current version is unstable', () => { + const upgradeVersions = [ + { + newVersion: '1.1.0-beta', + newVersionMajor: 1, + upgradeType: 'minor', + workingVersion: '1.0.0-beta', + }, + ]; + versionsHelper.determineUpgrades({ + name: 'amazing-package', + versions: { + '1.0.0-beta': {}, + '1.1.0-beta': {}, + }, + }, '1.0.0-beta', defaultConfig).should.eql(upgradeVersions); + }); }); describe('.isRange(input)', () => { it('rejects simple semver', () => { -- GitLab