From f8d348e4cdb71414c96efb7e9bebf92f3a8a904a Mon Sep 17 00:00:00 2001 From: Clay Reimann Date: Sun, 6 Dec 2015 19:27:36 -0600 Subject: [PATCH 1/5] Restructure package.json for humans * add new dependencies --- package.json | 64 ++++++++++++++++++++++++++-------------------------- 1 file changed, 32 insertions(+), 32 deletions(-) diff --git a/package.json b/package.json index 01a8ec3f..9f61b682 100644 --- a/package.json +++ b/package.json @@ -2,8 +2,36 @@ "name": "github-api", "version": "0.10.7", "description": "A higher-level wrapper around the Github API.", + "license": "BSD-3-Clause-Clear", + "readmeFilename": "README.md", + "scripts": { + "test": "gulp test && gulp lint", + "lint": "gulp lint", + "codecov": "cat coverage/*/lcov.info | codecov" + }, + "bugs": { + "url": "https://github.com/michael/github/issues" + }, + "repository": { + "type": "git", + "url": "git://github.com/michael/github.git" + }, + "keywords": [ + "github", + "api" + ], + "contributors": [ + "Ændrew Rininsland (http://www.aendrew.com)", + "Aurelio De Rosa (http://www.audero.it/)", + "Michael Aufreiter (http://substance.io)", + "Clay Reimann (http://clayreimann.me)" + ], "main": "github.js", "dependencies": { + "axios": "^0.7.0", + "debug": "^2.2.0", + "es6-promise": "^3.0.2", + "inherits": "^2.0.1", "js-base64": "^2.1.8", "xmlhttprequest": "~1.7.0" }, @@ -14,47 +42,19 @@ "gulp-jscs": "^3.0.1", "gulp-jscs-stylish": "^1.2.1", "gulp-jshint": "^1.11.2", - "gulp-rename": "^1.2.2", - "gulp-uglify": "^1.4.2", "istanbul": "^0.3.13", "jshint": "^2.5.8", "jshint-stylish": "^2.0.1", "karma": "^0.13.14", "karma-chai": "^0.1.0", + "karma-chrome-launcher": "^0.2.2", "karma-coverage": "^0.5.3", "karma-json-fixtures-preprocessor": "0.0.5", "karma-mocha": "^0.2.0", "karma-mocha-reporter": "^1.1.1", - "karma-phantomjs-launcher": "^0.2.1", "karma-sauce-launcher": "^0.3.0", - "mocha": "^2.3.3" - }, - "scripts": { - "test": "gulp test && gulp lint", - "lint": "gulp lint", - "codecov": "cat coverage/*/lcov.info | codecov" - }, - "repository": { - "type": "git", - "url": "git://github.com/michael/github.git" - }, - "keywords": [ - "github", - "api" - ], - "contributors": [ - "Ændrew Rininsland (http://www.aendrew.com)", - "Aurelio De Rosa (http://www.audero.it/)", - "Michael Aufreiter (http://substance.io)" - ], - "license": "BSD-3-Clause-Clear", - "readmeFilename": "README.md", - "gitHead": "aa8aa3c8cd5ce5240373d4fd1d06a7ab4af41a36", - "bugs": { - "url": "https://github.com/michael/github/issues" + "mocha": "^2.3.3", + "webpack-stream": "^2.3.0" }, - "browser": { - "xmlhttprequest": false, - "base64": false - } + "gitHead": "aa8aa3c8cd5ce5240373d4fd1d06a7ab4af41a36" } From cb9b76bff280549dec336eca6db4be1cd9ddc93a Mon Sep 17 00:00:00 2001 From: Clay Reimann Date: Sun, 6 Dec 2015 19:29:23 -0600 Subject: [PATCH 2/5] Update build and modularize code * Build with webpack * Bundle dependencies * Split the library into separate files based on API --- github.js | 1108 +++--------------------------------- gulpfile.js | 86 ++- lib/Gist.js | 66 +++ lib/Issue.js | 40 ++ lib/Repository.js | 493 ++++++++++++++++ lib/Search.js | 41 ++ lib/User.js | 143 +++++ lib/helpers/Requestable.js | 150 +++++ 8 files changed, 1064 insertions(+), 1063 deletions(-) create mode 100644 lib/Gist.js create mode 100644 lib/Issue.js create mode 100644 lib/Repository.js create mode 100644 lib/Search.js create mode 100644 lib/User.js create mode 100644 lib/helpers/Requestable.js diff --git a/github.js b/github.js index 2e0d3591..ab95c628 100644 --- a/github.js +++ b/github.js @@ -3,1046 +3,82 @@ * * @copyright (c) 2013 Michael Aufreiter, Development Seed * Github.js is freely distributable. - *de * @license Licensed under BSD-3-Clause-Clear * * For all details and documentation: * http://substance.io/michael/github */ - -(function (root, factory) { - // UMD boilerplate from https://github.com/umdjs/umd/blob/master/returnExportsGlobal.js - 'use strict'; - - /* istanbul ignore next */ - if (typeof define === 'function' && define.amd) { - // AMD. Register as an anonymous module. - define(['xmlhttprequest', 'js-base64'], function (XMLHttpRequest, b64encode) { - return (root.Github = factory(XMLHttpRequest.XMLHttpRequest, b64encode.Base64.encode)); - }); - } else if (typeof module === 'object' && module.exports) { - if (typeof window !== 'undefined') { // jscs:ignore - module.exports = factory(window.XMLHttpRequest, window.btoa); - } else { // jscs:ignore - module.exports = factory(require('xmlhttprequest').XMLHttpRequest, require('js-base64').Base64.encode); - } - } else { - // Browser globals - var b64encode = function(str) { - return root.btoa(encodeURIComponent(str).replace(/%([0-9A-F]{2})/g, function(match, p1) { - return String.fromCharCode('0x' + p1); - })); - }; - - root.Github = factory(root.XMLHttpRequest, b64encode); +'use strict'; + +require('es6-promise').polyfill(); + +var User = require('./lib/User'); +var Gist = require('./lib/Gist'); +var Issue = require('./lib/Issue'); +var Search = require('./lib/Search'); +var Repository = require('./lib/Repository'); + +function Github(options) { + this.apiUrl = options.apiUrl || 'https://api.github.com'; + this.username = options.username; + this.password = options.password; + this.token = options.token; +} + +Github.prototype.getIssues = function(user, repo) { + return new Issue({ + apiUrl: this.apiUrl, + username: this.username, + password: this.password, + token: this.token, + user: user, + repo: repo + }); +}; + +Github.prototype.getRepo = function(user, repo) { + var fullname = user; + + if (repo) { + fullname = user + '/' + repo; } -}(this, function (XMLHttpRequest, b64encode) { - 'use strict'; - - // Initial Setup - // ------------- - - var Github = function(options) { - var API_URL = options.apiUrl || 'https://api.github.com'; - - // HTTP Request Abstraction - // ======= - // - // I'm not proud of this and neither should you be if you were responsible for the XMLHttpRequest spec. - - var _request = Github._request = function _request(method, path, data, cb, raw, sync) { - function getURL() { - var url = path.indexOf('//') >= 0 ? path : API_URL + path; - - url += ((/\?/).test(url) ? '&' : '?'); - - if (data && typeof data === 'object' && ['GET', 'HEAD', 'DELETE'].indexOf(method) > -1) { - for(var param in data) { - if (data.hasOwnProperty(param)) - url += '&' + encodeURIComponent(param) + '=' + encodeURIComponent(data[param]); - } - } - - return url.replace(/(×tamp=\d+)/, '') + - (typeof window !== 'undefined' ? '×tamp=' + new Date().getTime() : ''); - } - - var xhr = new XMLHttpRequest(); - - xhr.open(method, getURL(), !sync); - - if (!sync) { - xhr.onreadystatechange = function () { - if (this.readyState === 4) { - if (this.status >= 200 && this.status < 300 || this.status === 304) { - cb(null, raw ? this.responseText : this.responseText ? JSON.parse(this.responseText) : true, this); - } else { - cb({ - path: path, request: this, error: this.status - }); - } - } - }; - } - - if (!raw) { - xhr.dataType = 'json'; - xhr.setRequestHeader('Accept', 'application/vnd.github.v3+json'); - } else { - xhr.setRequestHeader('Accept', 'application/vnd.github.v3.raw+json'); - } - - xhr.setRequestHeader('Content-Type', 'application/json;charset=UTF-8'); - - if ((options.token) || (options.username && options.password)) { - var authorization = options.token ? 'token ' + options.token : - 'Basic ' + b64encode(options.username + ':' + options.password); - - xhr.setRequestHeader('Authorization', authorization); - } - - if (data) { - xhr.send(JSON.stringify(data)); - } else { - xhr.send(); - } - - if (sync) { - return xhr.response; - } - }; - - var _requestAllPages = Github._requestAllPages = function _requestAllPages(path, cb) { - var results = []; - - (function iterate() { - _request('GET', path, null, function(err, res, xhr) { - if (err) { - return cb(err); - } - - results.push.apply(results, res); - - var links = (xhr.getResponseHeader('link') || '').split(/\s*,\s*/g); - var next = null; - - links.forEach(function(link) { - next = /rel="next"/.test(link) ? link : next; - }); - - if (next) { - next = (/<(.*)>/.exec(next) || [])[1]; - } - - if (!next) { - cb(err, results); - } else { - path = next; - iterate(); - } - }); - })(); - }; - - // User API - // ======= - - Github.User = function() { - this.repos = function(options, cb) { - if (arguments.length === 1 && typeof arguments[0] === 'function') { - cb = options; - options = {}; - } - - options = options || {}; - - var url = '/user/repos'; - var params = []; - - params.push('type=' + encodeURIComponent(options.type || 'all')); - params.push('sort=' + encodeURIComponent(options.sort || 'updated')); - params.push('per_page=' + encodeURIComponent(options.per_page || '1000')); // jscs:ignore - - if (options.page) { - params.push('page=' + encodeURIComponent(options.page)); - } - - url += '?' + params.join('&'); - - _request('GET', url, null, cb); - }; - - // List user organizations - // ------- - - this.orgs = function(cb) { - _request('GET', '/user/orgs', null, cb); - }; - - // List authenticated user's gists - // ------- - - this.gists = function(cb) { - _request('GET', '/gists', null, cb); - }; - - // List authenticated user's unread notifications - // ------- - - this.notifications = function(options, cb) { - if (arguments.length === 1 && typeof arguments[0] === 'function') { - cb = options; - options = {}; - } - - options = options || {}; - var url = '/notifications'; - var params = []; - - if (options.all) { - params.push('all=true'); - } - - if (options.participating) { - params.push('participating=true'); - } - - if (options.since) { - var since = options.since; - - if (since.constructor === Date) { - since = since.toISOString(); - } - - params.push('since=' + encodeURIComponent(since)); - } - - if (options.before) { - var before = options.before; - - if (before.constructor === Date) { - before = before.toISOString(); - } - - params.push('before=' + encodeURIComponent(before)); - } - - if (options.page) { - params.push('page=' + encodeURIComponent(options.page)); - } - - if (params.length > 0) { - url += '?' + params.join('&'); - } - - _request('GET', url, null, cb); - }; - - // Show user information - // ------- - - this.show = function(username, cb) { - var command = username ? '/users/' + username : '/user'; - - _request('GET', command, null, cb); - }; - - // List user repositories - // ------- - - this.userRepos = function(username, cb) { - // Github does not always honor the 1000 limit so we want to iterate over the data set. - _requestAllPages('/users/' + username + '/repos?type=all&per_page=1000&sort=updated', cb); - }; - - // List user starred repositories - // ------- - - this.userStarred = function(username, cb) { - // Github does not always honor the 1000 limit so we want to iterate over the data set. - _requestAllPages('/users/' + username + '/starred?type=all&per_page=1000', function(err, res) { - cb(err, res); - }); - }; - - // List a user's gists - // ------- - - this.userGists = function(username, cb) { - _request('GET', '/users/' + username + '/gists', null, cb); - }; - - // List organization repositories - // ------- - - this.orgRepos = function(orgname, cb) { - // Github does not always honor the 1000 limit so we want to iterate over the data set. - _requestAllPages('/orgs/' + orgname + '/repos?type=all&&page_num=1000&sort=updated&direction=desc', cb); - }; - - // Follow user - // ------- - - this.follow = function(username, cb) { - _request('PUT', '/user/following/' + username, null, cb); - }; - - // Unfollow user - // ------- - - this.unfollow = function(username, cb) { - _request('DELETE', '/user/following/' + username, null, cb); - }; - - // Create a repo - // ------- - this.createRepo = function(options, cb) { - _request('POST', '/user/repos', options, cb); - }; - }; - - // Repository API - // ======= - - Github.Repository = function(options) { - var repo = options.name; - var user = options.user; - var fullname = options.fullname; - - var that = this; - var repoPath; - - if (fullname) { - repoPath = '/repos/' + fullname; - } else { - repoPath = '/repos/' + user + '/' + repo; - } - - var currentTree = { - branch: null, - sha: null - }; - - // Uses the cache if branch has not been changed - // ------- - - function updateTree(branch, cb) { - if (branch === currentTree.branch && currentTree.sha) { - return cb(null, currentTree.sha); - } - - that.getRef('heads/' + branch, function(err, sha) { - currentTree.branch = branch; - currentTree.sha = sha; - cb(err, sha); - }); - } - - // Get a particular reference - // ------- - - this.getRef = function(ref, cb) { - _request('GET', repoPath + '/git/refs/' + ref, null, function(err, res, xhr) { - if (err) { - return cb(err); - } - - cb(null, res.object.sha, xhr); - }); - }; - - // Create a new reference - // -------- - // - // { - // "ref": "refs/heads/my-new-branch-name", - // "sha": "827efc6d56897b048c772eb4087f854f46256132" - // } - - this.createRef = function(options, cb) { - _request('POST', repoPath + '/git/refs', options, cb); - }; - - // Delete a reference - // -------- - // - // Repo.deleteRef('heads/gh-pages') - // repo.deleteRef('tags/v1.0') - - this.deleteRef = function(ref, cb) { - _request('DELETE', repoPath + '/git/refs/' + ref, options, function(err, res, xhr) { - cb(err, res, xhr); - }); - }; - - // Create a repo - // ------- - - this.createRepo = function(options, cb) { - _request('POST', '/user/repos', options, cb); - }; - - // Delete a repo - // -------- - - this.deleteRepo = function(cb) { - _request('DELETE', repoPath, options, cb); - }; - - // List all tags of a repository - // ------- - - this.listTags = function(cb) { - _request('GET', repoPath + '/tags', null, function(err, tags, xhr) { - if (err) { - return cb(err); - } - - cb(null, tags, xhr); - }); - }; - - // List all pull requests of a respository - // ------- - - this.listPulls = function(options, cb) { - options = options || {}; - var url = repoPath + '/pulls'; - var params = []; - - if (typeof options === 'string') { - // Backward compatibility - params.push('state=' + options); - } else { - if (options.state) { - params.push('state=' + encodeURIComponent(options.state)); - } - - if (options.head) { - params.push('head=' + encodeURIComponent(options.head)); - } - - if (options.base) { - params.push('base=' + encodeURIComponent(options.base)); - } - - if (options.sort) { - params.push('sort=' + encodeURIComponent(options.sort)); - } - - if (options.direction) { - params.push('direction=' + encodeURIComponent(options.direction)); - } - - if (options.page) { - params.push('page=' + options.page); - } - - if (options.per_page) { - params.push('per_page=' + options.per_page); - } - } - - if (params.length > 0) { - url += '?' + params.join('&'); - } - - _request('GET', url, null, function(err, pulls, xhr) { - if (err) return cb(err); - cb(null, pulls, xhr); - }); - }; - - // Gets details for a specific pull request - // ------- - - this.getPull = function(number, cb) { - _request('GET', repoPath + '/pulls/' + number, null, function(err, pull, xhr) { - if (err) return cb(err); - cb(null, pull, xhr); - }); - }; - - // Retrieve the changes made between base and head - // ------- - - this.compare = function(base, head, cb) { - _request('GET', repoPath + '/compare/' + base + '...' + head, null, function(err, diff, xhr) { - if (err) return cb(err); - cb(null, diff, xhr); - }); - }; - - // List all branches of a repository - // ------- - - this.listBranches = function(cb) { - _request('GET', repoPath + '/git/refs/heads', null, function(err, heads, xhr) { - if (err) return cb(err); - cb(null, heads.map(function(head) { - return head.ref.replace(/^refs\/heads\//, ''); - }), xhr); - }); - }; - - // Retrieve the contents of a blob - // ------- - - this.getBlob = function(sha, cb) { - _request('GET', repoPath + '/git/blobs/' + sha, null, cb, 'raw'); - }; - - // For a given file path, get the corresponding sha (blob for files, tree for dirs) - // ------- - - this.getCommit = function(branch, sha, cb) { - _request('GET', repoPath + '/git/commits/' + sha, null, function(err, commit, xhr) { - if (err) return cb(err); - cb(null, commit, xhr); - }); - }; - - // For a given file path, get the corresponding sha (blob for files, tree for dirs) - // ------- - - this.getSha = function(branch, path, cb) { - if (!path || path === '') return that.getRef('heads/' + branch, cb); - _request('GET', repoPath + '/contents/' + path + (branch ? '?ref=' + branch : ''), - null, function(err, pathContent, xhr) { - if (err) return cb(err); - cb(null, pathContent.sha, xhr); - }); - }; - - // Get the statuses for a particular SHA - // ------- - - this.getStatuses = function(sha, cb) { - _request('GET', repoPath + '/statuses/' + sha, null, cb); - }; - - // Retrieve the tree a commit points to - // ------- - - this.getTree = function(tree, cb) { - _request('GET', repoPath + '/git/trees/' + tree, null, function(err, res, xhr) { - if (err) return cb(err); - cb(null, res.tree, xhr); - }); - }; - - // Post a new blob object, getting a blob SHA back - // ------- - - this.postBlob = function(content, cb) { - if (typeof (content) === 'string') { - content = { - content: content, - encoding: 'utf-8' - }; - } else { - content = { - content: b64encode(content), - encoding: 'base64' - }; - } - - _request('POST', repoPath + '/git/blobs', content, function(err, res) { - if (err) return cb(err); - cb(null, res.sha); - }); - }; - - // Update an existing tree adding a new blob object getting a tree SHA back - // ------- - - this.updateTree = function(baseTree, path, blob, cb) { - var data = { - base_tree: baseTree, - tree: [ - { - path: path, - mode: '100644', - type: 'blob', - sha: blob - } - ] - }; - - _request('POST', repoPath + '/git/trees', data, function(err, res) { - if (err) return cb(err); - cb(null, res.sha); - }); - }; - - // Post a new tree object having a file path pointer replaced - // with a new blob SHA getting a tree SHA back - // ------- - - this.postTree = function(tree, cb) { - _request('POST', repoPath + '/git/trees', { - tree: tree - }, function(err, res) { - if (err) return cb(err); - cb(null, res.sha); - }); - }; - - // Create a new commit object with the current commit SHA as the parent - // and the new tree SHA, getting a commit SHA back - // ------- - - this.commit = function(parent, tree, message, cb) { - var user = new Github.User(); - - user.show(null, function(err, userData) { - if (err) return cb(err); - var data = { - message: message, - author: { - name: options.user, - email: userData.email - }, - parents: [ - parent - ], - tree: tree - }; - - _request('POST', repoPath + '/git/commits', data, function(err, res) { - if (err) return cb(err); - currentTree.sha = res.sha; // Update latest commit - cb(null, res.sha); - }); - }); - }; - - // Update the reference of your head to point to the new commit SHA - // ------- - - this.updateHead = function(head, commit, cb) { - _request('PATCH', repoPath + '/git/refs/heads/' + head, { - sha: commit - }, function(err) { - cb(err); - }); - }; - - // Show repository information - // ------- - - this.show = function(cb) { - _request('GET', repoPath, null, cb); - }; - - // Show repository contributors - // ------- - - this.contributors = function (cb, retry) { - retry = retry || 1000; - var that = this; - - _request('GET', repoPath + '/stats/contributors', null, function (err, data, xhr) { - if (err) return cb(err); - - if (xhr.status === 202) { - setTimeout( - function () { - that.contributors(cb, retry); - }, - retry - ); - } else { - cb(err, data, xhr); - } - }); - }; - - // Get contents - // -------- - - this.contents = function(ref, path, cb) { - path = encodeURI(path); - _request('GET', repoPath + '/contents' + (path ? '/' + path : ''), { - ref: ref - }, cb); - }; - - // Fork repository - // ------- - - this.fork = function(cb) { - _request('POST', repoPath + '/forks', null, cb); - }; - - // List forks - // -------- - - this.listForks = function(cb) { - _request('GET', repoPath + '/forks', null, cb); - }; - - // Branch repository - // -------- - - this.branch = function(oldBranch, newBranch, cb) { - if (arguments.length === 2 && typeof arguments[1] === 'function') { - cb = newBranch; - newBranch = oldBranch; - oldBranch = 'master'; - } - - this.getRef('heads/' + oldBranch, function(err, ref) { - if (err && cb) return cb(err); - that.createRef({ - ref: 'refs/heads/' + newBranch, - sha: ref - }, cb); - }); - }; - - // Create pull request - // -------- - - this.createPullRequest = function(options, cb) { - _request('POST', repoPath + '/pulls', options, cb); - }; - - // List hooks - // -------- - - this.listHooks = function(cb) { - _request('GET', repoPath + '/hooks', null, cb); - }; - - // Get a hook - // -------- - - this.getHook = function(id, cb) { - _request('GET', repoPath + '/hooks/' + id, null, cb); - }; - - // Create a hook - // -------- - - this.createHook = function(options, cb) { - _request('POST', repoPath + '/hooks', options, cb); - }; - - // Edit a hook - // -------- - - this.editHook = function(id, options, cb) { - _request('PATCH', repoPath + '/hooks/' + id, options, cb); - }; - - // Delete a hook - // -------- - - this.deleteHook = function(id, cb) { - _request('DELETE', repoPath + '/hooks/' + id, null, cb); - }; - - // Read file at given path - // ------- - - this.read = function(branch, path, cb) { - _request('GET', repoPath + '/contents/' + encodeURI(path) + (branch ? '?ref=' + branch : ''), - null, function(err, obj, xhr) { - if (err && err.error === 404) return cb('not found', null, null); - - if (err) return cb(err); - cb(null, obj, xhr); - }, true); - }; - - // Remove a file - // ------- - - this.remove = function(branch, path, cb) { - that.getSha(branch, path, function(err, sha) { - if (err) return cb(err); - _request('DELETE', repoPath + '/contents/' + path, { - message: path + ' is removed', - sha: sha, - branch: branch - }, cb); - }); - }; - - // Alias for repo.remove for backwards comapt. - // ------- - this.delete = this.remove; - - // Move a file to a new location - // ------- - - this.move = function(branch, path, newPath, cb) { - updateTree(branch, function(err, latestCommit) { - that.getTree(latestCommit + '?recursive=true', function(err, tree) { - // Update Tree - tree.forEach(function(ref) { - if (ref.path === path) ref.path = newPath; - - if (ref.type === 'tree') delete ref.sha; - }); - - that.postTree(tree, function(err, rootTree) { - that.commit(latestCommit, rootTree, 'Deleted ' + path , function(err, commit) { - that.updateHead(branch, commit, function(err) { - cb(err); - }); - }); - }); - }); - }); - }; - - // Write file contents to a given branch and path - // ------- - - this.write = function(branch, path, content, message, options, cb) { - if (typeof cb === 'undefined') { - cb = options; - options = {}; - } - - that.getSha(branch, encodeURI(path), function(err, sha) { - var writeOptions = { - message: message, - content: typeof options.encode === 'undefined' || options.encode ? b64encode(content) : content, - branch: branch, - committer: options && options.committer ? options.committer : undefined, - author: options && options.author ? options.author : undefined - }; - - // If no error, we set the sha to overwrite an existing file - if (!(err && err.error !== 404)) writeOptions.sha = sha; - _request('PUT', repoPath + '/contents/' + encodeURI(path), writeOptions, cb); - }); - }; - - // List commits on a repository. Takes an object of optional paramaters: - // sha: SHA or branch to start listing commits from - // path: Only commits containing this file path will be returned - // since: ISO 8601 date - only commits after this date will be returned - // until: ISO 8601 date - only commits before this date will be returned - // ------- - - this.getCommits = function(options, cb) { - options = options || {}; - var url = repoPath + '/commits'; - var params = []; - - if (options.sha) { - params.push('sha=' + encodeURIComponent(options.sha)); - } - - if (options.path) { - params.push('path=' + encodeURIComponent(options.path)); - } - - if (options.since) { - var since = options.since; - - if (since.constructor === Date) { - since = since.toISOString(); - } - - params.push('since=' + encodeURIComponent(since)); - } - - if (options.until) { - var until = options.until; - - if (until.constructor === Date) { - until = until.toISOString(); - } - - params.push('until=' + encodeURIComponent(until)); - } - - if (options.page) { - params.push('page=' + options.page); - } - - if (options.perpage) { - params.push('per_page=' + options.perpage); - } - - if (params.length > 0) { - url += '?' + params.join('&'); - } - - _request('GET', url, null, cb); - }; - }; - - // Gists API - // ======= - - Github.Gist = function(options) { - var id = options.id; - var gistPath = '/gists/' + id; - - // Read the gist - // -------- - - this.read = function(cb) { - _request('GET', gistPath, null, cb); - }; - - // Create the gist - // -------- - // { - // "description": "the description for this gist", - // "public": true, - // "files": { - // "file1.txt": { - // "content": "String file contents" - // } - // } - // } - - this.create = function(options, cb) { - _request('POST', '/gists', options, cb); - }; - - // Delete the gist - // -------- - - this.delete = function(cb) { - _request('DELETE', gistPath, null, cb); - }; - - // Fork a gist - // -------- - - this.fork = function(cb) { - _request('POST', gistPath + '/fork', null, cb); - }; - - // Update a gist with the new stuff - // -------- - - this.update = function(options, cb) { - _request('PATCH', gistPath, options, cb); - }; - - // Star a gist - // -------- - - this.star = function(cb) { - _request('PUT', gistPath + '/star', null, cb); - }; - - // Untar a gist - // -------- - - this.unstar = function(cb) { - _request('DELETE', gistPath + '/star', null, cb); - }; - - // Check if a gist is starred - // -------- - - this.isStarred = function(cb) { - _request('GET', gistPath + '/star', null, cb); - }; - }; - - // Issues API - // ========== - - Github.Issue = function(options) { - var path = '/repos/' + options.user + '/' + options.repo + '/issues'; - - this.list = function(options, cb) { - var query = []; - - for(var key in options) { - if (options.hasOwnProperty(key)) { - query.push(encodeURIComponent(key) + '=' + encodeURIComponent(options[key])); - } - } - - _requestAllPages(path + '?' + query.join('&'), cb); - }; - - this.comment = function(issue, comment, cb) { - _request('POST', issue.comments_url, { - body: comment - }, function(err, res) { - cb(err, res); - }); - }; - }; - - // Search API - // ========== - - Github.Search = function(options) { - var path = '/search/'; - var query = '?q=' + options.query; - - this.repositories = function(options, cb) { - _request('GET', path + 'repositories' + query, options, cb); - }; - - this.code = function(options, cb) { - _request('GET', path + 'code' + query, options, cb); - }; - - this.issues = function(options, cb) { - _request('GET', path + 'issues' + query, options, cb); - }; - - this.users = function(options, cb) { - _request('GET', path + 'users' + query, options, cb); - }; - }; - - return Github; - }; - - // Top Level API - // ------- - - Github.getIssues = function(user, repo) { - return new Github.Issue({ - user: user, repo: repo - }); - }; - - Github.getRepo = function(user, repo) { - if (!repo) { - var fullname = user; - - return new Github.Repository({ - fullname: fullname - }); - } else { - return new Github.Repository({ - user: user, name: repo - }); - } - }; - - Github.getUser = function() { - return new Github.User(); - }; - - Github.getGist = function(id) { - return new Github.Gist({ - id: id - }); - }; - - Github.getSearch = function(query) { - return new Github.Search({ - query: query - }); - }; - return Github; -})); + return new Repository({ + apiUrl: this.apiUrl, + username: this.username, + password: this.password, + token: this.token, + fullname: fullname + }); +}; + +Github.prototype.getUser = function() { + return new User({ + apiUrl: this.apiUrl, + username: this.username, + password: this.password, + token: this.token + }); +}; + +Github.prototype.getGist = function(id) { + return new Gist({ + apiUrl: this.apiUrl, + username: this.username, + password: this.password, + token: this.token, + id: id + }); +}; + +Github.prototype.getSearch = function(query) { + return new Search({ + apiUrl: this.apiUrl, + username: this.username, + password: this.password, + token: this.token, + query: query + }); +}; + +module.exports = Github; diff --git a/gulpfile.js b/gulpfile.js index ce55e31e..12dd3f29 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -1,21 +1,47 @@ 'use strict'; var gulp = require('gulp'); -var jshint = require('gulp-jshint'); var jscs = require('gulp-jscs'); -var rename = require('gulp-rename'); -var uglify = require('gulp-uglify'); +var jshint = require('gulp-jshint'); +var webpack = require('webpack-stream'); var stylish = require('gulp-jscs-stylish'); + var path = require('path'); var karma = require('karma'); +console.log(Object.keys(webpack)); + +/* + * Code style enforcement + */ +gulp.task('lint', function() { + return gulp.src([ + path.join(__dirname, '/*.js'), + path.join(__dirname, '/test/*.js') + ], + { + base: './' + }) + .pipe(jshint()) + .pipe(jscs({ + fix: true + })) + .pipe(stylish.combineWithHintResults()) + .pipe(jscs.reporter()) + .pipe(jshint.reporter('jshint-stylish')) + .pipe(gulp.dest('.')); +}); + +/* + * Testing fixtures + */ function runTests(singleRun, isCI, done) { var reporters = ['mocha']; var preprocessors = {}; var files = [ path.join(__dirname, 'test/vendor/*.js'), // PhantomJS 1.x polyfills - path.join(__dirname, 'github.js'), + path.join(__dirname, 'dist', 'github.min.js'), path.join(__dirname, 'test/test.*.js') ]; @@ -72,25 +98,7 @@ function runTests(singleRun, isCI, done) { server.start(); } // End runTests() -gulp.task('lint', function() { - return gulp.src([ - path.join(__dirname, '/*.js'), - path.join(__dirname, '/test/*.js') - ], - { - base: './' - }) - .pipe(jshint()) - .pipe(jscs({ - fix: true - })) - .pipe(stylish.combineWithHintResults()) - .pipe(jscs.reporter()) - .pipe(jshint.reporter('jshint-stylish')) - .pipe(gulp.dest('.')); -}); - -gulp.task('test', function(done) { +gulp.task('test', ['build'], function(done) { runTests(true, false, done); }); @@ -102,11 +110,35 @@ gulp.task('test:auto', function(done) { runTests(false, false, done); }); +/* + * Build + */ +var WEBPACK_CONFIG = { + entry: { + github: './github.js' + }, + output: { + library: 'Github', + libraryTarget: 'umd', + filename: '[name].min.js' + }, + resolve: { + extensions: ['', '.js'] + }, + plugins: [ + new webpack.webpack.optimize.UglifyJsPlugin() + ], + devtool: 'source-map', + stats: { + colors: 'true' + } +}; + gulp.task('build', function() { - return gulp.src('github.js') - .pipe(uglify()) - .pipe(rename('github.min.js')) - .pipe(gulp.dest('dist/')); + return gulp.src('./github.js') + .pipe(webpack(WEBPACK_CONFIG)) + .pipe(gulp.dest('dist/')) + ; }); gulp.task('default', function() { diff --git a/lib/Gist.js b/lib/Gist.js new file mode 100644 index 00000000..4fc21ef0 --- /dev/null +++ b/lib/Gist.js @@ -0,0 +1,66 @@ +'use strict'; + +/*! + * @overview Gist.js + * + * @copyright (c) 2015 Michael Aufreiter, Development Seed + * Github.js is freely distributable. + * @license Licensed under BSD-3-Clause-Clear + * + * For all details and documentation: + * http://substance.io/michael/github + */ +var inherits = require('inherits'); +var Requestable = require('./helpers/Requestable'); + +function Gist(options) { + Gist.super_.call(this, options); + this.gistPath = '/gists/' + options.id; +} + +inherits(Gist, Requestable); + +Gist.prototype.read = function(cb) { + this._request('GET', this.gistPath, null, cb); +}; + +// Create the gist +// -------- +// { +// "description": "the description for this gist", +// "public": true, +// "files": { +// "file1.txt": { +// "content": "String file contents" +// } +// } +// } +Gist.prototype.create = function(options, cb) { + this._request('POST', '/gists', options, cb); +}; + +Gist.prototype.delete = function(cb) { + this._request('DELETE', this.gistPath, null, cb); +}; + +Gist.prototype.fork = function(cb) { + this._request('POST', this.gistPath + '/fork', null, cb); +}; + +Gist.prototype.update = function(options, cb) { + this._request('PATCH', this.gistPath, options, cb); +}; + +Gist.prototype.star = function(cb) { + this._request('PUT', this.gistPath + '/star', null, cb); +}; + +Gist.prototype.unstar = function(cb) { + this._request('DELETE', this.gistPath + '/star', null, cb); +}; + +Gist.prototype.isStarred = function(cb) { + this._request('GET', this.gistPath + '/star', null, cb); +}; + +module.exports = Gist; diff --git a/lib/Issue.js b/lib/Issue.js new file mode 100644 index 00000000..88d0f7a1 --- /dev/null +++ b/lib/Issue.js @@ -0,0 +1,40 @@ +'use strict'; + +/*! + * @overview Issue.js + * + * @copyright (c) 2015 Michael Aufreiter, Development Seed + * Github.js is freely distributable. + * @license Licensed under BSD-3-Clause-Clear + * + * For all details and documentation: + * http://substance.io/michael/github + */ + +var inherits = require('inherits'); +var Requestable = require('./helpers/Requestable'); + +function Issue(options) { + Issue.super_.call(this, options); + this.path = '/repos/' + options.user + '/' + options.repo + '/issues'; +} + +inherits(Issue, Requestable); + +Issue.prototype.list = function(options, cb) { + var query = Object.keys(options).map(function(key) { + return encodeURIComponent(key) + '=' + encodeURIComponent(options[key]); + }); + + this._requestAllPages(this.path + '?' + query.join('&'), cb); +}; + +Issue.prototype.comment = function(issue, comment, cb) { + this._request('POST', issue.comments_url, { + body: comment + }, function(err, res) { + cb(err, res); + }); +}; + +module.exports = Issue; diff --git a/lib/Repository.js b/lib/Repository.js new file mode 100644 index 00000000..69c2f72d --- /dev/null +++ b/lib/Repository.js @@ -0,0 +1,493 @@ +'use strict'; + +/*! + * @overview Repository.js + * + * @copyright (c) 2015 Michael Aufreiter, Development Seed + * Github.js is freely distributable. + * @license Licensed under BSD-3-Clause-Clear + * + * For all details and documentation: + * http://substance.io/michael/github + */ + +var debug = require('debug')('Github:Repository'); +var inherits = require('inherits'); +var Base64 = require('js-base64').Base64; + +var Requestable = require('./helpers/Requestable'); +var User = require('./User'); + +function Repository(options) { + Repository.super_.call(this, options); + this.repo = options.name; + this.user = options.user; + this.fullname = options.fullname; + + if (this.fullname) { + this.repoPath = '/repos/' + this.fullname; + } else { + this.repoPath = '/repos/' + this.user + '/' + this.repo; + } + + this.currentTree = { + branch: null, + sha: null + }; +} + +inherits(Repository, Requestable); + +// Uses the cache if branch has not been changed +// ------- +Repository.prototype._updateTree = function(branch, cb) { + if (branch === this.currentTree.branch && this.currentTree.sha) { + return cb(null, this.currentTree.sha); + } + + this.getRef('heads/' + branch, function(err, sha) { + this.currentTree.branch = branch; + this.currentTree.sha = sha; + cb(err, sha); + }); +}; + +Repository.prototype.getRef = function(ref, cb) { + this._request('GET', this.repoPath + '/git/refs/' + ref, null, function(err, res, xhr) { + if (err) { + return cb(err); + } + + cb(null, res.object.sha, xhr); + }); +}; + +// Create a new reference +// -------- +// +// { +// "ref": "refs/heads/my-new-branch-name", +// "sha": "827efc6d56897b048c772eb4087f854f46256132" +// } +Repository.prototype.createRef = function(options, cb) { + this._request('POST', this.repoPath + '/git/refs', options, cb); +}; + +// Delete a reference +// -------- +// +// Repo.deleteRef('heads/gh-pages') +// repo.deleteRef('tags/v1.0') +Repository.prototype.deleteRef = function(ref, cb) { + this._request('DELETE', this.repoPath + '/git/refs/' + ref, null, cb); +}; + +Repository.prototype.createRepo = function(options, cb) { + this._request('POST', '/user/repos', options, cb); +}; + +Repository.prototype.deleteRepo = function(cb) { + this._request('DELETE', this.repoPath, null, cb); +}; + +Repository.prototype.listTags = function(cb) { + this._request('GET', this.repoPath + '/tags', null, function(err, tags, xhr) { + if (err) { + return cb(err); + } + + cb(null, tags, xhr); + }); +}; + +Repository.prototype.listPulls = function(options, cb) { + options = options || {}; + var url = this.repoPath + '/pulls'; + var params = []; + + if (typeof options === 'string') { + // Backward compatibility + params.push('state=' + options); + } else { + if (options.state) { + params.push('state=' + encodeURIComponent(options.state)); + } + + if (options.head) { + params.push('head=' + encodeURIComponent(options.head)); + } + + if (options.base) { + params.push('base=' + encodeURIComponent(options.base)); + } + + if (options.sort) { + params.push('sort=' + encodeURIComponent(options.sort)); + } + + if (options.direction) { + params.push('direction=' + encodeURIComponent(options.direction)); + } + + if (options.page) { + params.push('page=' + options.page); + } + + if (options.per_page) { + params.push('per_page=' + options.per_page); + } + } + + if (params.length > 0) { + url += '?' + params.join('&'); + } + + this._request('GET', url, null, function(err, pulls, xhr) { + if (err) return cb(err); + cb(null, pulls, xhr); + }); +}; + +Repository.prototype.getPull = function(number, cb) { + this._request('GET', this.repoPath + '/pulls/' + number, null, function(err, pull, xhr) { + if (err) return cb(err); + cb(null, pull, xhr); + }); +}; + +Repository.prototype.compare = function(base, head, cb) { + this._request('GET', this.repoPath + '/compare/' + base + '...' + head, null, function(err, diff, xhr) { + if (err) return cb(err); + cb(null, diff, xhr); + }); +}; + +Repository.prototype.listBranches = function(cb) { + this._request('GET', this.repoPath + '/git/refs/heads', null, function(err, heads, xhr) { + if (err) return cb(err); + cb(null, heads.map(function(head) { + return head.ref.replace(/^refs\/heads\//, ''); + }), xhr); + }); +}; + +Repository.prototype.getBlob = function(sha, cb) { + this._request('GET', this.repoPath + '/git/blobs/' + sha, null, cb, 'raw'); +}; + +Repository.prototype.getCommit = function(branch, sha, cb) { + this._request('GET', this.repoPath + '/git/commits/' + sha, null, function(err, commit, xhr) { + if (err) return cb(err); + cb(null, commit, xhr); + }); +}; + +Repository.prototype.getSha = function(branch, path, cb) { + if (!path || path === '') return this.getRef('heads/' + branch, cb); + + this._request('GET', this.repoPath + '/contents/' + path + (branch ? '?ref=' + branch : ''), + null, function(err, pathContent, xhr) { + debug(err); + + if (err) return cb(err); + cb(null, pathContent.sha, xhr); + }); +}; + +Repository.prototype.getStatuses = function(sha, cb) { + this._request('GET', this.repoPath + '/statuses/' + sha, null, cb); +}; + +Repository.prototype.getTree = function(tree, cb) { + this._request('GET', this.repoPath + '/git/trees/' + tree, null, function(err, res, xhr) { + if (err) return cb(err); + cb(null, res.tree, xhr); + }); +}; + +Repository.prototype.postBlob = function(content, cb) { + if (typeof (content) === 'string') { + content = { + content: content, + encoding: 'utf-8' + }; + } else { + content = { + content: Base64.encode(content), + encoding: 'base64' + }; + } + + this._request('POST', this.repoPath + '/git/blobs', content, function(err, res) { + if (err) return cb(err); + cb(null, res.sha); + }); +}; + +Repository.prototype.updateTree = function(baseTree, path, blob, cb) { + var data = { + base_tree: baseTree, + tree: [ + { + path: path, + mode: '100644', + type: 'blob', + sha: blob + } + ] + }; + + this._request('POST', this.repoPath + '/git/trees', data, function(err, res) { + if (err) return cb(err); + cb(null, res.sha); + }); +}; + +Repository.prototype.postTree = function(tree, cb) { + this._request('POST', this.repoPath + '/git/trees', { + tree: tree + }, function(err, res) { + if (err) return cb(err); + cb(null, res.sha); + }); +}; + +Repository.prototype.commit = function(parent, tree, message, cb) { + var user = new User(this.__requestableConfig); + var that = this; + + user.show(null, function(err, userData) { + if (err) return cb(err); + var data = { + message: message, + author: { + name: that.__requestableConfig.user, + email: userData.email + }, + parents: [ + parent + ], + tree: tree + }; + + that._request('POST', that.repoPath + '/git/commits', data, function(err, res) { + if (err) return cb(err); + that.currentTree.sha = res.sha; // Update latest commit + cb(null, res.sha); + }); + }); +}; + +Repository.prototype.updateHead = function(head, commit, cb) { + this._request('PATCH', this.repoPath + '/git/refs/heads/' + head, { + sha: commit + }, function(err) { + cb(err); + }); +}; + +Repository.prototype.show = function(cb) { + this._request('GET', this.repoPath, null, cb); +}; + +Repository.prototype.contributors = function (cb, retry) { + retry = retry || 1000; + var that = this; + + this._request('GET', this.repoPath + '/stats/contributors', null, function (err, data, xhr) { + if (err) return cb(err); + + if (xhr.status === 202) { + setTimeout( + function () { + that.contributors(cb, retry); + }, + retry + ); + } else { + cb(err, data, xhr); + } + }); +}; + +Repository.prototype.contents = function(ref, path, cb) { + path = encodeURI(path); + this._request('GET', this.repoPath + '/contents' + (path ? '/' + path : ''), { + ref: ref + }, cb); +}; + +Repository.prototype.fork = function(cb) { + this._request('POST', this.repoPath + '/forks', null, cb); +}; + +Repository.prototype.listForks = function(cb) { + this._request('GET', this.repoPath + '/forks', null, cb); +}; + +Repository.prototype.branch = function(oldBranch, newBranch, cb) { + var that = this; + + if (arguments.length === 2 && typeof arguments[1] === 'function') { + cb = newBranch; + newBranch = oldBranch; + oldBranch = 'master'; + } + + this.getRef('heads/' + oldBranch, function(err, ref) { + if (err && cb) return cb(err); + that.createRef({ + ref: 'refs/heads/' + newBranch, + sha: ref + }, cb); + }); +}; + +Repository.prototype.createPullRequest = function(options, cb) { + this._request('POST', this.repoPath + '/pulls', options, cb); +}; + +Repository.prototype.listHooks = function(cb) { + this._request('GET', this.repoPath + '/hooks', null, cb); +}; + +Repository.prototype.getHook = function(id, cb) { + this._request('GET', this.repoPath + '/hooks/' + id, null, cb); +}; + +Repository.prototype.createHook = function(options, cb) { + this._request('POST', this.repoPath + '/hooks', options, cb); +}; + +Repository.prototype.editHook = function(id, options, cb) { + this._request('PATCH', this.repoPath + '/hooks/' + id, options, cb); +}; + +Repository.prototype.deleteHook = function(id, cb) { + this._request('DELETE', this.repoPath + '/hooks/' + id, null, cb); +}; + +Repository.prototype.read = function(branch, path, cb) { + this._request('GET', this.repoPath + '/contents/' + encodeURI(path) + (branch ? '?ref=' + branch : ''), + null, function(err, obj, xhr) { + if (err && err.error === 404) return cb('not found', null, null); + + if (err) return cb(err); + cb(null, obj, xhr); + }, true); +}; + +Repository.prototype.remove = Repository.prototype.delete = function(branch, path, cb) { + var that = this; + + this.getSha(branch, path, function(err, sha) { + if (err) return cb(err); + + that._request('DELETE', that.repoPath + '/contents/' + path, { + message: path + ' is removed', + sha: sha, + branch: branch + }, cb); + }); +}; + +Repository.prototype.move = function(branch, path, newPath, cb) { + var that = this; + + this._updateTree(branch, function(err, latestCommit) { + that.getTree(latestCommit + '?recursive=true', function(err, tree) { + // Update Tree + tree.forEach(function(ref) { + if (ref.path === path) ref.path = newPath; + + if (ref.type === 'tree') delete ref.sha; + }); + + that.postTree(tree, function(err, rootTree) { + that.commit(latestCommit, rootTree, 'Deleted ' + path , function(err, commit) { + that.updateHead(branch, commit, function(err) { + cb(err); + }); + }); + }); + }); + }); +}; + +Repository.prototype.write = function(branch, path, content, message, options, cb) { + var that = this; + + if (typeof cb === 'undefined') { + cb = options; + options = {}; + } + + options = options || {}; + + var committer = options.committer; + var author = options.author; + + this.getSha(branch, encodeURI(path), function(err, sha) { + var writeOptions = { + message: message, + content: Base64.encode(content), + branch: branch, + committer: committer, + author: author + }; + + // If no error, we set the sha to overwrite an existing file + if (!(err && err.error !== 404)) writeOptions.sha = sha; + that._request('PUT', that.repoPath + '/contents/' + encodeURI(path), writeOptions, cb); + }); +}; + +Repository.prototype.getCommits = function(options, cb) { + options = options || {}; + var url = this.repoPath + '/commits'; + var params = []; + + if (options.sha) { + params.push('sha=' + encodeURIComponent(options.sha)); + } + + if (options.path) { + params.push('path=' + encodeURIComponent(options.path)); + } + + if (options.since) { + var since = options.since; + + if (since.constructor === Date) { + since = since.toISOString(); + } + + params.push('since=' + encodeURIComponent(since)); + } + + if (options.until) { + var until = options.until; + + if (until.constructor === Date) { + until = until.toISOString(); + } + + params.push('until=' + encodeURIComponent(until)); + } + + if (options.page) { + params.push('page=' + options.page); + } + + if (options.perpage) { + params.push('per_page=' + options.perpage); + } + + if (params.length > 0) { + url += '?' + params.join('&'); + } + + this._request('GET', url, null, cb); +}; + +module.exports = Repository; diff --git a/lib/Search.js b/lib/Search.js new file mode 100644 index 00000000..617c5409 --- /dev/null +++ b/lib/Search.js @@ -0,0 +1,41 @@ +'use strict'; + +/*! + * @overview Search.js + * + * @copyright (c) 2015 Michael Aufreiter, Development Seed + * Github.js is freely distributable. + * @license Licensed under BSD-3-Clause-Clear + * + * For all details and documentation: + * http://substance.io/michael/github + */ + +var inherits = require('inherits'); +var Requestable = require('./helpers/Requestable'); + +function Search(options) { + Search.super_.call(this, options); + this.path = '/search/'; + this.query = '?q=' + options.query; +} + +inherits(Search, Requestable); + +Search.prototype.repositories = function(options, cb) { + this._request('GET', this.path + 'repositories' + this.query, options, cb); +}; + +Search.prototype.code = function(options, cb) { + this._request('GET', this.path + 'code' + this.query, options, cb); +}; + +Search.prototype.issues = function(options, cb) { + this._request('GET', this.path + 'issues' + this.query, options, cb); +}; + +Search.prototype.users = function(options, cb) { + this._request('GET', this.path + 'users' + this.query, options, cb); +}; + +module.exports = Search; diff --git a/lib/User.js b/lib/User.js new file mode 100644 index 00000000..6e08ffdb --- /dev/null +++ b/lib/User.js @@ -0,0 +1,143 @@ +'use strict'; + +/*! + * @overview User.js + * + * @copyright (c) 2015 Michael Aufreiter, Development Seed + * Github.js is freely distributable. + * @license Licensed under BSD-3-Clause-Clear + * + * For all details and documentation: + * http://substance.io/michael/github + */ + +var inherits = require('inherits'); +var Requestable = require('./helpers/Requestable'); + +function User(options) { + User.super_.call(this, options); +} + +inherits(User, Requestable); + +User.prototype.repos = function(options, cb) { + if (arguments.length === 1 && typeof arguments[0] === 'function') { + cb = options; + options = {}; + } + + options = options || {}; + + var url = '/user/repos'; + var params = []; + + params.push('type=' + encodeURIComponent(options.type || 'all')); + params.push('sort=' + encodeURIComponent(options.sort || 'updated')); + params.push('per_page=' + encodeURIComponent(options.per_page || '1000')); // jscs:ignore + + if (options.page) { + params.push('page=' + encodeURIComponent(options.page)); + } + + url += '?' + params.join('&'); + + this._request('GET', url, null, cb); +}; + +User.prototype.orgs = function(cb) { + this._request('GET', '/user/orgs', null, cb); +}; + +User.prototype.gists = function(cb) { + this._request('GET', '/gists', null, cb); +}; + +User.prototype.notifications = function(options, cb) { + if (arguments.length === 1 && typeof arguments[0] === 'function') { + cb = options; + options = {}; + } + + options = options || {}; + var url = '/notifications'; + var params = []; + + if (options.all) { + params.push('all=true'); + } + + if (options.participating) { + params.push('participating=true'); + } + + if (options.since) { + var since = options.since; + + if (since.constructor === Date) { + since = since.toISOString(); + } + + params.push('since=' + encodeURIComponent(since)); + } + + if (options.before) { + var before = options.before; + + if (before.constructor === Date) { + before = before.toISOString(); + } + + params.push('before=' + encodeURIComponent(before)); + } + + if (options.page) { + params.push('page=' + encodeURIComponent(options.page)); + } + + if (params.length > 0) { + url += '?' + params.join('&'); + } + + this._request('GET', url, null, cb); +}; + +User.prototype.show = function(username, cb) { + var command = username ? '/users/' + username : '/user'; + + this._request('GET', command, null, cb); +}; + +User.prototype.userRepos = function(username, cb) { + // Github does not always honor the 1000 limit so we want to iterate over the data set. + this._requestAllPages('/users/' + username + '/repos?type=all&per_page=1000&sort=updated', cb); +}; + +User.prototype.userStarred = function(username, cb) { + // Github does not always honor the 1000 limit so we want to iterate over the data set. + this._requestAllPages('/users/' + username + '/starred?type=all&per_page=1000', function(err, res) { + cb(err, res); + }); +}; + +User.prototype.userGists = function(username, cb) { + this._request('GET', '/users/' + username + '/gists', null, cb); +}; + +User.prototype.orgRepos = function(orgname, cb) { + // Github does not always honor the 1000 limit so we want to iterate over the data set. + this._requestAllPages('/orgs/' + orgname + '/repos?type=all&&page_num=1000&sort=updated&direction=desc', cb); +}; + +User.prototype.follow = function(username, cb) { + this._request('PUT', '/user/following/' + username, null, cb); +}; + +User.prototype.unfollow = function(username, cb) { + this._request('DELETE', '/user/following/' + username, null, cb); +}; + +User.prototype.createRepo = function(options, cb) { + this._request('POST', '/user/repos', options, cb); +}; + +module.exports = User; diff --git a/lib/helpers/Requestable.js b/lib/helpers/Requestable.js new file mode 100644 index 00000000..61a6bd75 --- /dev/null +++ b/lib/helpers/Requestable.js @@ -0,0 +1,150 @@ +'use strict'; + +/*! + * @overview Requestable.js + * + * @copyright (c) 2015 Michael Aufreiter, Development Seed + * Github.js is freely distributable. + * @license Licensed under BSD-3-Clause-Clear + * + * For all details and documentation: + * http://substance.io/michael/github + */ + +var debug = require('debug')('Github:Requestable'); +var Base64 = require('js-base64').Base64; +var Request = require('axios'); + +function Requestable(options) { + this.__baseUrl = options.apiUrl; + this.__requestableConfig = { + apiUrl: options.apiUrl, + token: options.token, + username: options.username, + password: options.password + }; + + if ((options.token) || (options.username && options.password)) { + this.__authorizationHeader = options.token ? + 'token ' + options.token : + 'Basic ' + Base64.encode(options.username + ':' + options.password); + } +} + +Requestable.prototype._getURL = function(path) { + var url = path.indexOf('//') >= 0 ? path : this.__baseUrl + path; + + url += ((/\?/).test(url) ? '&' : '?'); + + return url; +}; + +function buildError(path, response) { + return { + path: path, + request: response.config, + response: response, + status: response.status + }; +} + +// This is a hack util axios's next update +// (which will include following redirects in NodeJS) +function getRequestPromise(config) { + debug('', config.method, 'to', config.url); + + return Request(config).catch(function(response) { + if (response.status === 301 || response.status === 302) { + debug('', 'Redirecting to ' + response.headers.location); + config.url = response.headers.location; + + return getRequestPromise(config); + } + + throw response; + }); +} + +Requestable.prototype._request = function(method, path, data, cb, raw) { + var headers = { + Accept: raw ? 'application/vnd.github.v3.raw+json' : 'application/vnd.github.v3+json', + 'Content-Type': 'application/json;charset=UTF-8' + }; + var url = this._getURL(path); + var queryParams = {}; + + if (this.__authorizationHeader) { + headers.Authorization = this.__authorizationHeader; + } + + if (data && typeof data === 'object' && ['GET', 'HEAD', 'DELETE'].indexOf(method) !== -1) { + queryParams = data; + data = {}; + url = url.replace(/(×tamp=\d+)/, '') + + (typeof window !== 'undefined' ? '×tamp=' + new Date().getTime() : ''); + } + + var config = { + url: url, + method: method, + headers: headers, + params: queryParams, + data: data, + responseType: raw ? 'text' : 'json' + }; + var requestPromise = getRequestPromise(config); + + if (cb) { + requestPromise.then(function(response) { + cb(null, response.data || true, response); + }).catch(function(response) { + debug('', 'catch', response.config.method, response.config.url); + cb(buildError(path, response)); + }); + } else { + return requestPromise; + } +}; + +Requestable.prototype._requestAllPages = function(path, cb, results) { + var that = this; + + results = results || []; + + return this._request('GET', path, null) + .then(function(response) { + results.push.apply(results, response.data); + + var linksHeader = response.headers.link || ''; + var links = linksHeader.split(/\s*,\s*/g); + var next = null; + + links.forEach(function(link) { + next = link.search(/rel="next"/) !== -1 ? link : next; + }); + + if (next) { + next = (next.match(/<(.*)>/) || [])[1]; + } + + if (next) { + debug('', 'getting next page', next); + + return that._requestAllPages(next, cb, results); + } + + if (cb) { + cb(null, results); + } else { + return Promise.resolve(results); + } + }).catch(function(response) { + if (cb) { + cb(buildError(path, response)); + } else { + throw response; + } + }); +}; + +module.exports = Requestable; From 998b78fc7d352fb968bb718d77cc4eccde2c9a3e Mon Sep 17 00:00:00 2001 From: Clay Reimann Date: Sun, 6 Dec 2015 19:31:58 -0600 Subject: [PATCH 3/5] Change gulp test to use Chrome PhantomJS appears not to support the full XHR2 spec. Test pass in Chrome but in PhantomJS they with DOM Exception 12 on the line where axios sets `XHR.responseType` --- karma.conf.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/karma.conf.js b/karma.conf.js index 226a0049..2d6aa312 100644 --- a/karma.conf.js +++ b/karma.conf.js @@ -15,9 +15,9 @@ module.exports = function(config) { autoWatch: false, - frameworks: ['mocha', 'chai'], + frameworks: ['mocha', 'chai', 'phantomjs-shim'], - browsers: ['PhantomJS'], + browsers: ['Chrome'], coverageReporter: { reporters: [ From 67b27d313ef3e11af4ef9676ec6f930f35d7e032 Mon Sep 17 00:00:00 2001 From: Clay Reimann Date: Mon, 7 Dec 2015 10:20:37 -0600 Subject: [PATCH 4/5] Fix tests to actually fail instead of timing out MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Errors thrown in the callbacks don’t bubble up to the calling test and make the tests timeout instead of fail. --- .gitignore | 10 +- gulpfile.js | 5 +- karma.conf.js | 2 +- test/helpers.js | 18 ++ test/test.auth.js | 44 ++-- test/test.issue.js | 26 +- test/test.repo.js | 627 ++++++++++++++++++++++---------------------- test/test.search.js | 32 +-- test/test.user.js | 68 +++-- 9 files changed, 420 insertions(+), 412 deletions(-) create mode 100644 test/helpers.js diff --git a/.gitignore b/.gitignore index 8bcfe81d..7c78223e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,8 @@ -.DS_Store -.idea -node_modules/ -npm-debug.log +dist/ coverage/ +node_modules/ + +.idea +.DS_Store sauce.json +npm-debug.log diff --git a/gulpfile.js b/gulpfile.js index 12dd3f29..37eda7e4 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -9,8 +9,6 @@ var stylish = require('gulp-jscs-stylish'); var path = require('path'); var karma = require('karma'); -console.log(Object.keys(webpack)); - /* * Code style enforcement */ @@ -42,7 +40,8 @@ function runTests(singleRun, isCI, done) { var files = [ path.join(__dirname, 'test/vendor/*.js'), // PhantomJS 1.x polyfills path.join(__dirname, 'dist', 'github.min.js'), - path.join(__dirname, 'test/test.*.js') + path.join(__dirname, 'test', 'helpers.js'), + path.join(__dirname, 'test', 'test.*.js') ]; if (singleRun) { diff --git a/karma.conf.js b/karma.conf.js index 2d6aa312..49a79eb5 100644 --- a/karma.conf.js +++ b/karma.conf.js @@ -15,7 +15,7 @@ module.exports = function(config) { autoWatch: false, - frameworks: ['mocha', 'chai', 'phantomjs-shim'], + frameworks: ['mocha', 'chai'], browsers: ['Chrome'], diff --git a/test/helpers.js b/test/helpers.js new file mode 100644 index 00000000..0f695e6f --- /dev/null +++ b/test/helpers.js @@ -0,0 +1,18 @@ +'use strict'; + +function callbackWithError(done, fn) { + var cb = function(err, response, xhr) { + try { + fn(err, response, xhr); + } catch(e) { + done(e); + } + }; + + return cb; +} + +if (typeof window === 'undefined') { + // Export stuff (comment here to make linter happy) + module.exports = callbackWithError; +} diff --git a/test/test.auth.js b/test/test.auth.js index af93d28f..cf2f4f33 100644 --- a/test/test.auth.js +++ b/test/test.auth.js @@ -1,56 +1,52 @@ 'use strict'; -var testUser, github, user; +var testUser; if (typeof window === 'undefined') { - // Module dependencies - var chai = require('chai'); var Github = require('../'); + var callbackWithError = require('./helpers.js'); testUser = require('./user.json'); + // Module dependencies + var chai = require('chai'); + // Use should flavour for Mocha var should = chai.should(); } -describe('Github constructor', function() { +describe('Authentication', function() { before(function() { if (typeof window !== 'undefined') testUser = window.__fixtures__['test/user']; + }); - github = new Github({ + it('should authenticate with valid credentials', function(done) { + var github = new Github({ username: testUser.USERNAME, password: testUser.PASSWORD, auth: 'basic' }); + var user = github.getUser(); - user = github.getUser(); - }); - - it('should authenticate and return no errors', function(done) { - user.notifications(function(err) { + user.notifications(callbackWithError(done, function(err) { should.not.exist(err); done(); - }); + })); }); -}); - -describe('Github constructor (failing case)', function() { - before(function() { - if (typeof window !== 'undefined') testUser = window.__fixtures__['test/user']; - github = new Github({ + it('should fail authentication with invalid credentials', function(done) { + var github = new Github({ username: testUser.USERNAME, password: 'fake124', auth: 'basic' }); - user = github.getUser(); - }); + var user = github.getUser(); + + user.notifications(callbackWithError(done, function(err) { + err.status.should.equal(401, 'Return 401 status for bad auth'); + err.response.data.message.should.equal('Bad credentials'); - it('should fail authentication and return err', function(done) { - user.notifications(function(err) { - err.request.status.should.equal(401, 'Return 401 status for bad auth'); - JSON.parse(err.request.responseText).message.should.equal('Bad credentials'); done(); - }); + })); }); }); diff --git a/test/test.issue.js b/test/test.issue.js index c261a58c..ed2acdb4 100644 --- a/test/test.issue.js +++ b/test/test.issue.js @@ -1,22 +1,25 @@ 'use strict'; -var testUser, github, issues; +var testUser; if (typeof window === 'undefined') { - // Module dependencies - var chai = require('chai'); var Github = require('../'); + var callbackWithError = require('./helpers.js'); testUser = require('./user.json'); + // Module dependencies + var chai = require('chai'); + // Use should flavour for Mocha var should = chai.should(); } -describe('Github.Issue', function() { +describe('Issues', function() { + var github, issues; + before(function() { if (typeof window !== 'undefined') testUser = window.__fixtures__['test/user']; - github = new Github({ username: testUser.USERNAME, password: testUser.PASSWORD, @@ -27,20 +30,21 @@ describe('Github.Issue', function() { }); it('should list issues', function(done) { - issues.list({}, function(err, issues) { + issues.list({}, callbackWithError(done, function(err, issues) { should.not.exist(err); issues.should.have.length.above(0); done(); - }); + })); }); it('should post issue comment', function(done) { - issues.list({}, function(err, issuesList) { - issues.comment(issuesList[0], 'Comment test', function(err, res) { + issues.list({}, callbackWithError(done, function(err, issuesList) { + should.not.exist(err); + issues.comment(issuesList[0], 'Comment test', callbackWithError(done, function(err, res) { should.not.exist(err); res.body.should.equal('Comment test'); done(); - }); - }); + })); + })); }); }); diff --git a/test/test.repo.js b/test/test.repo.js index d5bab61f..1e50cb66 100644 --- a/test/test.repo.js +++ b/test/test.repo.js @@ -1,439 +1,428 @@ 'use strict'; -var github, repo, user, testUser, imageB64, imageBlob; +var testUser, imageB64, imageBlob; if (typeof window === 'undefined') { // We're in NodeJS - // Module dependencies - var chai = require('chai'); var Github = require('../'); - - testUser = require('./user.json'); - - // Use should flavour for Mocha - var should = chai.should(); - + var callbackWithError = require('./helpers.js'); var fs = require('fs'); var path = require('path'); + testUser = require('./user.json'); imageBlob = fs.readFileSync(path.join(__dirname, 'gh.png')); // This is a Buffer(). imageB64 = imageBlob.toString('base64'); -} else { // We're in the browser - if (typeof window._phantom !== 'undefined') { - var xhr = new XMLHttpRequest(); - - xhr.responseType = 'blob'; - xhr.open('GET', 'base/test/gh.png'); - xhr.onload = function() { - var reader = new FileReader(); - - reader.onloadend = function() { - imageB64 = btoa(reader.result); - imageBlob = reader.result; - console.log(imageBlob); - }; - reader.readAsBinaryString(xhr.response); + // Module dependencies + var chai = require('chai'); + + // Use should flavour for Mocha + var should = chai.should(); +} else if (typeof window._phantom !== 'undefined') { + // We're in the Phantom + var xhr = new XMLHttpRequest(); + + xhr.responseType = 'blob'; + xhr.open('GET', 'base/test/gh.png'); + xhr.onload = function() { + var reader = new FileReader(); + + reader.onloadend = function() { + imageB64 = btoa(reader.result); + imageBlob = reader.result; + console.log(imageBlob); }; - xhr.send(); - } else { - // jscs:disable - imageB64 = 'iVBORw0KGgoAAAANSUhEUgAAACsAAAAmCAAAAAB4qD3CAAABgElEQVQ4y9XUsUocURQGYN/pAyMWBhGtrEIMiFiooGuVIoYsSBAsRSQvYGFWC4uFhUBYsilXLERQsDA20YAguIbo5PQp3F3inVFTheSvZoavGO79z+mJP0/Pv2nPtlfLpfLq9tljNquO62S8mj1kmy/8nrHm/Xaz1930bt5n1+SzVmyrilItsod9ON0td1V59xR9hwV2HsMRsbfROLo4amzsRcQw5vO2CZPJEU5CM2cXYTCxg7CY2mwIVhK7AkNZYg9g4CqxVwNwkNg6zOTKMQP1xFZgKWeXoJLYdSjl7BysJ7YBIzk7Ap8TewLOE3oOTtIz6y/64bfQn55ZTIAPd2gNTOTurcbzp7z50v1y/Pq2Q7Wczca8vFjG6LvbMo92hiPL96xO+eYVPkVExMdONetFXZ+l+eP9cuV7RER8a9PZwrloTXv2tfv285ZOt4rnrTXlydxCu9sZmGrdN8eXC3ATERHXsHD5wC7ZL3HdsaX9R3bUzlb7YWvn/9ipf93+An8cHsx3W3WHAAAAAElFTkSuQmCC'; - imageBlob = new Blob(); - // jscs:enable - } + reader.readAsBinaryString(xhr.response); + }; + + xhr.send(); +} else { + // We're in the browser + // jscs:disable + imageB64 = 'iVBORw0KGgoAAAANSUhEUgAAACsAAAAmCAAAAAB4qD3CAAABgElEQVQ4y9XUsUocURQGYN/pAyMWBhGtrEIMiFiooGuVIoYsSBAsRSQvYGFWC4uFhUBYsilXLERQsDA20YAguIbo5PQp3F3inVFTheSvZoavGO79z+mJP0/Pv2nPtlfLpfLq9tljNquO62S8mj1kmy/8nrHm/Xaz1930bt5n1+SzVmyrilItsod9ON0td1V59xR9hwV2HsMRsbfROLo4amzsRcQw5vO2CZPJEU5CM2cXYTCxg7CY2mwIVhK7AkNZYg9g4CqxVwNwkNg6zOTKMQP1xFZgKWeXoJLYdSjl7BysJ7YBIzk7Ap8TewLOE3oOTtIz6y/64bfQn55ZTIAPd2gNTOTurcbzp7z50v1y/Pq2Q7Wczca8vFjG6LvbMo92hiPL96xO+eYVPkVExMdONetFXZ+l+eP9cuV7RER8a9PZwrloTXv2tfv285ZOt4rnrTXlydxCu9sZmGrdN8eXC3ATERHXsHD5wC7ZL3HdsaX9R3bUzlb7YWvn/9ipf93+An8cHsx3W3WHAAAAAElFTkSuQmCC'; + imageBlob = new Blob(); + + // jscs:enable } -describe('Github.Repository', function() { +var repoTest = Math.floor(Math.random() * (100000 - 0)) + 0; + +describe('Repository', function() { + var github, repo, user; + before(function() { if (typeof window !== 'undefined') testUser = window.__fixtures__['test/user']; - github = new Github({ username: testUser.USERNAME, password: testUser.PASSWORD, auth: 'basic' }); - - repo = github.getRepo('michael', 'github'); + user = github.getUser(); }); - it('should show repo', function(done) { - repo.show(function(err, res) { - should.not.exist(err); - res.full_name.should.equal('michael/github'); // jscs:ignore - done(); + describe('read from repository', function() { + before(function() { + repo = github.getRepo('michael', 'github'); }); - }); - it('should show repo contents', function(done) { - repo.contents('master', './', function(err) { - should.not.exist(err); - - // @TODO write better assertion. - done(); + it('should show repo', function(done) { + repo.show(callbackWithError(done, function(err, res) { + should.not.exist(err); + res.full_name.should.equal('michael/github'); // jscs:ignore + done(); + })); }); - }); - it('should fork repo', function(done) { - repo.fork(function(err) { - should.not.exist(err); + it('should show repo contents', function(done) { + repo.contents('master', './', callbackWithError(done, function(err) { + // @TODO write better assertion. + should.not.exist(err); - // @TODO write better assertion. - done(); + done(); + })); }); - }); - it('should list forks of repo', function(done) { - repo.listForks(function(err) { - should.not.exist(err); + it('should fork repo', function(done) { + repo.fork(callbackWithError(done, function(err) { + // @TODO write better assertion. + should.not.exist(err); - // @TODO write better assertion. - done(); + done(); + })); }); - }); - it('should show repo contributors', function(done) { - repo.contributors(function(err, res) { - should.not.exist(err); - res.should.be.instanceof(Array); - res.should.have.length.above(1); - should.exist(res[0].author); - should.exist(res[0].total); - should.exist(res[0].weeks); - done(); + it('should list forks of repo', function(done) { + repo.listForks(callbackWithError(done, function(err) { + // @TODO write better assertion. + should.not.exist(err); + + done(); + })); }); - }); - // @TODO repo.branch, repo.pull + it('should show repo contributors', function(done) { + repo.contributors(callbackWithError(done, function(err, res) { + should.not.exist(err); + res.should.be.instanceof(Array); + res.should.have.length.above(1); + should.exist(res[0].author); + should.exist(res[0].total); + should.exist(res[0].weeks); - it('should list repo branches', function(done) { - repo.listBranches(function(err) { - should.not.exist(err); - done(); + done(); + })); }); - }); - it('should read repo', function(done) { - repo.read('master', 'README.md', function(err, res) { - res.indexOf('# Github.js').should.be.above(-1); - done(); - }); - }); + // @TODO repo.branch, repo.pull - it('should get commit from repo', function(done) { - repo.getCommit('master', '20fcff9129005d14cc97b9d59b8a3d37f4fb633b', function(err, commit) { - should.not.exist(err); - commit.message.should.equal('v0.10.4'); - commit.author.date.should.equal('2015-03-20T17:01:42Z'); - done(); - }); - }); + it('should list repo branches', function(done) { + repo.listBranches(callbackWithError(done, function(err) { + should.not.exist(err); - it('should get statuses for a SHA from a repo', function(done) { - repo.getStatuses('20fcff9129005d14cc97b9d59b8a3d37f4fb633b', function(err, statuses) { - statuses.length.should.equal(6) - statuses.every(function(status) { - return status.url === 'https://api.github.com/repos/michael/github/statuses/20fcff9129005d14cc97b9d59b8a3d37f4fb633b' - }).should.equal(true); - done(); + done(); + })); }); - }); - it('should get a SHA from a repo', function(done) { - repo.getSha('master', '.gitignore', function(err, sha) { - should.not.exist(err); - done(); + it('should read repo', function(done) { + repo.read('master', 'README.md', callbackWithError(done, function(err, res) { + should.not.exist(err); + res.indexOf('# Github.js').should.be.above(-1); + done(); + })); }); - }); - it('should get a repo by fullname', function(done) { - var repo2 = github.getRepo('michael/github'); - - repo2.show(function(err, res) { - should.not.exist(err); - res.full_name.should.equal('michael/github'); // jscs:ignore - done(); + it('should get commit from repo', function(done) { + repo.getCommit('master', '20fcff9129005d14cc97b9d59b8a3d37f4fb633b', + callbackWithError(done, function(err, commit) { + should.not.exist(err); + commit.message.should.equal('v0.10.4'); + commit.author.date.should.equal('2015-03-20T17:01:42Z'); + done(); + }) + ); }); - }); -}); - -var repoTest = Math.floor(Math.random() * (100000 - 0)) + 0; -describe('Creating new Github.Repository', function() { - before(function() { - if (typeof window !== 'undefined') testUser = window.__fixtures__['test/user']; - - github = new Github({ - username: testUser.USERNAME, - password: testUser.PASSWORD, - auth: 'basic' + it('should get statuses for a SHA from a repo', function(done) { + repo.getStatuses('20fcff9129005d14cc97b9d59b8a3d37f4fb633b', callbackWithError(done, function(err, statuses) { + should.not.exist(err); + statuses.length.should.equal(6); + statuses.every(function(status) { + return status.url === 'https://api.github.com/repos/michael/github/statuses/20fcff9129005d14cc97b9d59b8a3d37f4fb633b'; + }).should.equal(true); + done(); + })); }); - user = github.getUser(); - repo = github.getRepo(testUser.USERNAME, repoTest); - }); - - it('should create repo', function(done) { - user.createRepo({ - name: repoTest - }, function(err, res) { - should.not.exist(err); - res.name.should.equal(repoTest.toString()); - done(); + it('should get a SHA from a repo', function(done) { + repo.getSha('master', '.gitignore', callbackWithError(done, function(err, sha) { + should.not.exist(err); + done(); + })); }); - }); - it('should write to repo', function(done) { - repo.write('master', 'TEST.md', 'THIS IS A TEST', 'Creating test', function(err) { - should.not.exist(err); + it('should get a repo by fullname', function(done) { + var repo2 = github.getRepo('michael/github'); - // @TODO write a better assertion. - done(); - }); - }); - - it('should write to repo branch', function(done) { - repo.branch('master', 'dev', function(err) { - should.not.exist(err); - repo.write('dev', 'TEST.md', 'THIS IS AN UPDATED TEST', 'Updating test', function(err) { + repo2.show(callbackWithError(done, function(err, res) { should.not.exist(err); - repo.read('dev', 'TEST.md', function(err, res) { - res.should.equal('THIS IS AN UPDATED TEST'); - should.not.exist(err); - done(); - }); - }); + res.full_name.should.equal('michael/github'); // jscs:ignore + done(); + })); }); }); - it('should get ref from repo', function(done) { - repo.getRef('heads/master', function(err) { - should.not.exist(err); - - // @TODO write better assertion - done(); + describe('creating new repository', function() { + before(function() { + repo = github.getRepo(testUser.USERNAME, repoTest); }); - }); - it('should create ref on repo', function(done) { - repo.getRef('heads/master', function(err, sha) { - var refSpec = { - ref: 'refs/heads/new-test-branch', sha: sha + it('should create repo', function(done) { + var newRepo = { + name: repoTest }; - repo.createRef(refSpec, function(err) { + user.createRepo(newRepo, callbackWithError(done, function(err, res) { should.not.exist(err); + res.name.should.equal(repoTest.toString()); - // @TODO write better assertion done(); - }); + })); }); - }); - - it('should delete ref on repo', function(done) { - repo.deleteRef('heads/new-test-branch', function(err) { - should.not.exist(err); - // @TODO write better assertion - done(); + it('should write to repo', function(done) { + repo.write('master', 'TEST.md', 'THIS IS A TEST', 'Creating test', callbackWithError(done, function(err) { + // @TODO write a better assertion. + should.not.exist(err); + done(); + })); }); - }); - - it('should list tags on repo', function(done) { - repo.listTags(function(err) { - should.not.exist(err); - // @TODO write better assertion - done(); + it('should write to repo branch', function(done) { + repo.branch('master', 'dev', callbackWithError(done, function(err) { + should.not.exist(err); + repo.write('dev', 'TEST.md', 'THIS IS AN UPDATED TEST', 'Updating test', + callbackWithError(done, function(err) { + should.not.exist(err); + repo.read('dev', 'TEST.md', callbackWithError(done, function(err, res) { + should.not.exist(err); + res.should.equal('THIS IS AN UPDATED TEST'); + done(); + })); + }) + ); + })); + }); + + it('should get ref from repo', function(done) { + repo.getRef('heads/master', callbackWithError(done, function(err) { + // @TODO write better assertion + should.not.exist(err); + done(); + })); }); - }); - it('should list pulls on repo', function(done) { - var repo = github.getRepo('michael', 'github'); - var options = { - state: 'all', - sort: 'updated', - direction: 'desc', - page: 1, - per_page: 10 - }; + it('should create ref on repo', function(done) { + repo.getRef('heads/master', callbackWithError(done, function(err, sha) { + should.not.exist(err); + var refSpec = { + ref: 'refs/heads/new-test-branch', sha: sha + }; - repo.listPulls(options, function(err, pull_list) { - should.not.exist(err); - pull_list.should.be.instanceof(Array); - pull_list.should.have.length(10); - should.exist(pull_list[0].title); - should.exist(pull_list[0].body); - should.exist(pull_list[0].url); - done(); + repo.createRef(refSpec, callbackWithError(done, function(err) { + // @TODO write better assertion + should.not.exist(err); + done(err); + })); + })); }); - }); - it('should get pull requests on repo', function(done) { - var repo = github.getRepo('michael', 'github'); - - repo.getPull(153, function(err) { - should.not.exist(err); - - // @TODO write better assertion - done(); + it('should delete ref on repo', function(done) { + repo.deleteRef('heads/new-test-branch', callbackWithError(done, function(err) { + // @TODO write better assertion + should.not.exist(err); + done(); + })); }); - }); - - it('should delete a file on the repo', function(done) { - repo.write('master', 'REMOVE-TEST.md', 'THIS IS A TEST', 'Remove test', function(err) { - should.not.exist(err); - repo.remove('master', 'REMOVE-TEST.md', function(err) { + it('should list tags on repo', function(done) { + repo.listTags(callbackWithError(done, function(err) { + // @TODO write better assertion should.not.exist(err); done(); - }); + })); }); - }); - it('should use repo.delete as an alias for repo.remove', function(done) { - repo.write('master', 'REMOVE-TEST.md', 'THIS IS A TEST', 'Remove test', function(err) { - should.not.exist(err); + it('should list pulls on repo', function(done) { + var repo = github.getRepo('michael', 'github'); + var options = { + state: 'all', + sort: 'updated', + direction: 'desc', + page: 1, + per_page: 10 + }; - repo.delete('master', 'REMOVE-TEST.md', function(err) { + repo.listPulls(options, callbackWithError(done, function(err, pull_list) { should.not.exist(err); + pull_list.should.be.instanceof(Array); + pull_list.should.have.length(10); + should.exist(pull_list[0].title); + should.exist(pull_list[0].body); + should.exist(pull_list[0].url); done(); - }); + })); }); - }); - it('should write author and committer to repo', function(done) { - var options = { - author: { - name: 'Author Name', email: 'author@example.com' - }, - committer: { - name: 'Committer Name', email: 'committer@example.com' - } - }; + it('should get pull requests on repo', function(done) { + var repo = github.getRepo('michael', 'github'); - repo.write('dev', 'TEST.md', 'THIS IS A TEST BY AUTHOR AND COMMITTER', 'Updating', options, function(err, res) { - should.not.exist(err); - repo.getCommit('dev', res.commit.sha, function(err, commit) { + repo.getPull(153, callbackWithError(done, function(err) { + // @TODO write better assertion should.not.exist(err); - commit.author.name.should.equal('Author Name'); - commit.author.email.should.equal('author@example.com'); - commit.committer.name.should.equal('Committer Name'); - commit.committer.email.should.equal('committer@example.com'); - done(); - }); + })); }); - }); - it('should be able to write CJK unicode to repo', function(done) { - repo.write('master', '中文测试.md', 'THIS IS A TEST', 'Creating test', function(err) { - should.not.exist(err); + it('should delete a file on the repo', function(done) { + repo.write('master', 'REMOVE-TEST.md', 'THIS IS A TEST', 'Remove test', callbackWithError(done, function(err) { + should.not.exist(err); + repo.remove('master', 'REMOVE-TEST.md', function(err) { + done(err); + }); + })); + }); - // @TODO write better assertion - done(); + it('should use repo.delete as an alias for repo.remove', function(done) { + repo.write('master', 'REMOVE-TEST.md', 'THIS IS A TEST', 'Remove test', callbackWithError(done, function(err) { + should.not.exist(err); + repo.delete('master', 'REMOVE-TEST.md', function(err) { + done(err); + }); + })); }); - }); - it('should be able to write unicode to repo', function(done) { - repo.write('master', 'TEST_unicode.md', '\u2014', 'Long dash unicode', function(err) { - should.not.exist(err); + it('should write author and committer to repo', function(done) { + var options = { + author: { + name: 'Author Name', email: 'author@example.com' + }, + committer: { + name: 'Committer Name', email: 'committer@example.com' + } + }; - if (err) console.log(err); - repo.read('master', 'TEST_unicode.md', function(err, obj) { - should.not.exist(err); - obj.should.equal('\u2014'); + repo.write('dev', 'TEST.md', 'THIS IS A TEST BY AUTHOR AND COMMITTER', 'Updating', options, + callbackWithError(done, function(err, res) { + should.not.exist(err); + repo.getCommit('dev', res.commit.sha, callbackWithError(done, function(err, commit) { + should.not.exist(err); + commit.author.name.should.equal('Author Name'); + commit.author.email.should.equal('author@example.com'); + commit.committer.name.should.equal('Committer Name'); + commit.committer.email.should.equal('committer@example.com'); + done(); + })); + }) + ); + }); + + it('should be able to write CJK unicode to repo', function(done) { + repo.write('master', '中文测试.md', 'THIS IS A TEST', 'Creating test', callbackWithError(done, function(err) { + // @TODO write better assertion + should.not.exist(err); done(); - }); + })); }); - }); - it('should pass a regression test for _request (#14)', function(done) { - repo.getRef('heads/master', function(err, sha) { - var refSpec = { - ref: 'refs/heads/testing-14', sha: sha - }; + it('should be able to write unicode to repo', function(done) { + repo.write('master', 'TEST_unicode.md', '\u2014', 'Long dash unicode', callbackWithError(done, function(err) { + if (err) console.log(err); + should.not.exist(err); + repo.read('master', 'TEST_unicode.md', callbackWithError(done, function(err, obj) { + should.not.exist(err); + obj.should.equal('\u2014'); + done(); + })); + })); + }); - repo.createRef(refSpec, function(err) { + it('should pass a regression test for _request (#14)', function(done) { + repo.getRef('heads/master', callbackWithError(done, function(err, sha) { should.not.exist(err); + var refSpec = { + ref: 'refs/heads/testing-14', sha: sha + }; - // Triggers GET: - // https://api.github.com/repos/michael/cmake_cdt7_stalled/git/refs/heads/prose-integration - repo.getRef('heads/master', function(err) { + repo.createRef(refSpec, callbackWithError(done, function(err) { should.not.exist(err); - // Triggers DELETE: + // Triggers GET: // https://api.github.com/repos/michael/cmake_cdt7_stalled/git/refs/heads/prose-integration - repo.deleteRef('heads/testing-14', function(err, res, xhr) { + repo.getRef('heads/master', callbackWithError(done, function(err) { should.not.exist(err); - xhr.status.should.equal(204); - done(); - }); - }); - }); - }); - }); - - it('should be able to write an image to the repo', function(done) { - repo.write('master', 'TEST_image.png', imageB64, 'Image test', { - encode: false - }, function(err) { - if (err) console.log(err); - should.not.exist(err); - done(); - }); - }); - it('should be able to write a blob to the repo', function(done) { - repo.postBlob('String test', function(err) { // Test strings - should.not.exist(err); + // Triggers DELETE: + // https://api.github.com/repos/michael/cmake_cdt7_stalled/git/refs/heads/prose-integration + repo.deleteRef('heads/testing-14', callbackWithError(done, function(err, res, xhr) { + should.not.exist(err); + xhr.status.should.equal(204); + done(); + })); + })); + })); + })); + }); + + it('should be able to write an image to the repo', function(done) { + var options = { + encode: false + }; - repo.postBlob(imageBlob, function(err) { // Test non-strings + repo.write('master', 'TEST_image.png', imageB64, 'Image test', options, callbackWithError(done, function(err) { + if (err) console.log(err); should.not.exist(err); done(); - }); + })); }); - }); -}); -describe('deleting a Github.Repository', function() { - before(function() { - if (typeof window !== 'undefined') testUser = window.__fixtures__['test/user']; - github = new Github({ - username: testUser.USERNAME, - password: testUser.PASSWORD, - auth: 'basic' + it('should be able to write a blob to the repo', function(done) { + repo.postBlob('String test', callbackWithError(done, function(err) { // Test strings + should.not.exist(err); + repo.postBlob(imageBlob, callbackWithError(done, function(err) { // Test non-strings + should.not.exist(err); + done(err); + })); + })); }); - repo = github.getRepo(testUser.USERNAME, repoTest); }); - it('should delete the repo', function(done) { - repo.deleteRepo(function(err, res) { - should.not.exist(err); - res.should.be.true; // jshint ignore:line - done(); + describe('deleting a repository', function() { + before(function() { + repo = github.getRepo(testUser.USERNAME, repoTest); }); - }); -}); -describe('Repo returns commit errors correctly', function() { - before(function() { - if (typeof window !== 'undefined') testUser = window.__fixtures__['test/user']; - github = new Github({ - username: testUser.USERNAME, - password: testUser.PASSWORD, - auth: 'basic' + it('should delete the repo', function(done) { + repo.deleteRepo(callbackWithError(done, function(err, res) { + should.not.exist(err); + res.should.be.true; // jshint ignore:line + done(); + })); }); - repo = github.getRepo(testUser.USERNAME, testUser.REPO); }); - it('should fail on broken commit', function(done) { - repo.commit('broken-parent-hash', 'broken-tree-hash', 'commit message', function(err) { - should.exist(err); - should.exist(err.request); - err.request.status.should.equal(422); - done(); + describe('returns commit errors correctly', function() { + before(function() { + repo = github.getRepo(testUser.USERNAME, testUser.REPO); + }); + + it('should fail on broken commit', function(done) { + repo.commit('broken-parent-hash', 'broken-tree-hash', 'commit message', callbackWithError(done, function(err) { + should.exist(err); + should.exist(err.request); + err.status.should.equal(422); + done(); + })); }); }); }); diff --git a/test/test.search.js b/test/test.search.js index a084d126..5185842f 100644 --- a/test/test.search.js +++ b/test/test.search.js @@ -3,16 +3,18 @@ var github; if (typeof window === 'undefined') { - // Module dependencies - var chai = require('chai'); var Github = require('../'); var testUser = require('./user.json'); + var callbackWithError = require('./helpers.js'); + + // Module dependencies + var chai = require('chai'); // Use should flavour for Mocha var should = chai.should(); } -describe('Github.Search', function() { +describe('Search', function() { before(function() { if (typeof window !== 'undefined') testUser = window.__fixtures__['test/user']; github = new Github({ @@ -22,43 +24,43 @@ describe('Github.Search', function() { }); }); - it('should search.repositories', function(done) { + it('should search repositories', function(done) { var search = github.getSearch('tetris+language:assembly&sort=stars&order=desc'); var options = null; - search.repositories(options, function (err) { + search.repositories(options, callbackWithError(done, function (err) { should.not.exist(err); done(); - }); + })); }); - it('should search.code', function(done) { + it('should search code', function(done) { var search = github.getSearch('addClass+in:file+language:js+repo:jquery/jquery'); var options = null; - search.code(options, function (err) { + search.code(options, callbackWithError(done, function (err) { should.not.exist(err); done(); - }); + })); }); - it('should search.issues', function(done) { + it('should search issues', function(done) { var search = github.getSearch('windows+label:bug+language:python+state:open&sort=created&order=asc'); var options = null; - search.issues(options, function (err) { + search.issues(options, callbackWithError(done, function (err) { should.not.exist(err); done(); - }); + })); }); - it('should search.users', function(done) { + it('should search users', function(done) { var search = github.getSearch('tom+repos:%3E42+followers:%3E1000'); var options = null; - search.users(options, function (err) { + search.users(options, callbackWithError(done, function (err) { should.not.exist(err); done(); - }); + })); }); }); diff --git a/test/test.user.js b/test/test.user.js index c4d3d77e..396b3107 100644 --- a/test/test.user.js +++ b/test/test.user.js @@ -1,11 +1,12 @@ 'use strict'; -var testUser, user, github; +var testUser; if (typeof window === 'undefined') { // Module dependencies var chai = require('chai'); var Github = require('../'); + var callbackWithError = require('./helpers.js'); testUser = require('./user.json'); @@ -14,6 +15,8 @@ if (typeof window === 'undefined') { } describe('Github.User', function() { + var user, github; + before(function() { if (typeof window !== 'undefined') testUser = window.__fixtures__['test/user']; github = new Github({ @@ -25,10 +28,10 @@ describe('Github.User', function() { }); it('should get user.repos', function(done) { - user.repos(function(err) { + user.repos(callbackWithError(done, function(err) { should.not.exist(err); done(); - }); + })); }); it('should get user.repos with options', function(done) { @@ -39,19 +42,19 @@ describe('Github.User', function() { page: 1 }; - user.repos(options, function(err, repos) { + user.repos(options, callbackWithError(done, function(err, repos) { repos.should.have.length(10); should.not.exist(err); done(); - }); + })); }); it('should get user.orgs', function(done) { - user.orgs(function(err) { + user.orgs(callbackWithError(done, function(err) { should.not.exist(err); done(); - }); + })); }); it('should get user.gists', function(done) { @@ -62,10 +65,10 @@ describe('Github.User', function() { }); it('should get user.notifications', function(done) { - user.notifications(function(err) { + user.notifications(callbackWithError(done, function(err) { should.not.exist(err); done(); - }); + })); }); it('should get user.notifications with options', function(done) { @@ -76,77 +79,72 @@ describe('Github.User', function() { before: '2015-02-01T00:00:00Z' }; - user.notifications(options, function(err) { + user.notifications(options, callbackWithError(done, function(err) { should.not.exist(err); done(); - }); + })); }); it('should show user', function(done) { - user.show('ingalls', function(err) { + user.show('ingalls', callbackWithError(done, function(err) { should.not.exist(err); done(); - }); + })); }); it('should show user\'s repos', function(done) { // This is odd; userRepos times out on the test user, but user.repos does not. - user.userRepos('aendrew', function(err) { + user.userRepos('aendrew', callbackWithError(done, function(err) { should.not.exist(err); done(); - }); + })); }); it('should show user\'s starred repos', function(done) { - user.userStarred(testUser.USERNAME, function(err) { + user.userStarred(testUser.USERNAME, callbackWithError(done, function(err) { should.not.exist(err); done(); - }); + })); }); it('should show user\'s gists', function(done) { - user.userGists(testUser.USERNAME, function(err) { + user.userGists(testUser.USERNAME, callbackWithError(done, function(err) { should.not.exist(err); done(); - }); + })); }); it('should show user\'s organisation repos', function(done) { - user.orgRepos('openaddresses', function(err) { + user.orgRepos('openaddresses', callbackWithError(done, function(err) { should.not.exist(err); done(); - }); + })); }); it('should follow user', function(done) { - user.follow('ingalls', function(err) { + user.follow('ingalls', callbackWithError(done, function(err) { should.not.exist(err); done(); - }); + })); }); it('should unfollow user', function(done) { - user.unfollow('ingalls', function(err) { + user.unfollow('ingalls', callbackWithError(done, function(err) { should.not.exist(err); done(); - }); + })); }); it('should create a repo', function(done) { var repoTest = Math.floor(Math.random() * (100000 - 0)) + 0; - var github = new Github({ - username: testUser.USERNAME, - password: testUser.PASSWORD, - auth: 'basic' - }); - var user = github.getUser(); - - user.createRepo({ + var testRepo = { name: repoTest - }, function (err, res) { + }; + + user.createRepo(testRepo, callbackWithError(done, function (err, res) { should.not.exist(err); res.name.should.equal(repoTest.toString()); done(); - }); + })); }); }); From 17dee9a9024cfa6125c92d62bd08ac7eaa10fd04 Mon Sep 17 00:00:00 2001 From: Clay Reimann Date: Wed, 9 Dec 2015 10:35:08 -0600 Subject: [PATCH 5/5] Update Travis with sauce user + apikey. Build with one command to fail faster. * add new dev dependencies * reformat test names in test.user --- .travis.yml | 10 ++-- gulpfile.js | 100 ++++++++++++++++++++++--------------- karma.conf.js | 10 ++-- lib/User.js | 4 ++ lib/helpers/Requestable.js | 4 ++ package.json | 8 +-- test/test.repo.js | 9 ++-- test/test.user.js | 14 +++--- 8 files changed, 98 insertions(+), 61 deletions(-) diff --git a/.travis.yml b/.travis.yml index 05882674..ad568664 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,7 +10,9 @@ node_js: - "0.10" script: - - gulp lint - - gulp test:ci - - mocha test/ - - npm run-script codecov + - > + gulp lint && + gulp build && + gulp test:node && + gulp test:ci && + gulp codecov diff --git a/gulpfile.js b/gulpfile.js index 37eda7e4..673e3bc5 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -2,12 +2,15 @@ var gulp = require('gulp'); var jscs = require('gulp-jscs'); +var mocha = require('gulp-mocha'); var jshint = require('gulp-jshint'); var webpack = require('webpack-stream'); +var codecov = require('gulp-codecov.io'); var stylish = require('gulp-jscs-stylish'); var path = require('path'); var karma = require('karma'); +var sequence = require('run-sequence'); /* * Code style enforcement @@ -33,6 +36,43 @@ gulp.task('lint', function() { /* * Testing fixtures */ +var sauceLaunchers = { + SL_Chrome: { + base: 'SauceLabs', + browserName: 'chrome', + version: '45' + }, + SL_Firefox: { + base: 'SauceLabs', + browserName: 'firefox', + version: '39' + }, + SL_Safari: { + base: 'SauceLabs', + browserName: 'safari', + platform: 'OS X 10.10', + version: '8' + }, + SL_IE_10: { + base: 'SauceLabs', + browserName: 'internet explorer', + platform: 'Windows 2012', + version: '10' + }, + SL_IE_11: { + base: 'SauceLabs', + browserName: 'internet explorer', + platform: 'Windows 8.1', + version: '11' + }, + SL_iOS: { + base: 'SauceLabs', + browserName: 'iphone', + platform: 'OS X 10.10', + version: '8.1' + } +}; + function runTests(singleRun, isCI, done) { var reporters = ['mocha']; var preprocessors = {}; @@ -72,7 +112,9 @@ function runTests(singleRun, isCI, done) { localConfig.sauceLabs = { testName: 'GitHub.js UAT tests', idleTimeout: 120000, - recordVideo: false + recordVideo: false, + username: 'clayreimann', + accessKey: '16176d74-6882-4457-b4cd-d8f36aa9d0be' }; // Increase timeouts massively so Karma doesn't timeout in Sauce tunnel. @@ -97,10 +139,19 @@ function runTests(singleRun, isCI, done) { server.start(); } // End runTests() -gulp.task('test', ['build'], function(done) { +gulp.task('test', ['test:node', 'test:web']); + +gulp.task('test:web', ['build'], function(done) { runTests(true, false, done); }); +gulp.task('test:node', function() { + return gulp.src('test/test.*.js') + .pipe(mocha({ + timeout: 10000 + })); +}); + gulp.task('test:ci', function(done) { runTests(true, true, done); }); @@ -109,6 +160,11 @@ gulp.task('test:auto', function(done) { runTests(false, false, done); }); +gulp.task('codecov', function() { + return gulp.src('coverage/*/lcov.info') + .pipe(codecov()); +}); + /* * Build */ @@ -140,43 +196,9 @@ gulp.task('build', function() { ; }); +/* + * Miscellaneous tasks + */ gulp.task('default', function() { gulp.start('lint', 'test', 'build'); }); - -var sauceLaunchers = { - SL_Chrome: { - base: 'SauceLabs', - browserName: 'chrome', - version: '45' - }, - SL_Firefox: { - base: 'SauceLabs', - browserName: 'firefox', - version: '39' - }, - SL_Safari: { - base: 'SauceLabs', - browserName: 'safari', - platform: 'OS X 10.10', - version: '8' - }, - SL_IE_10: { - base: 'SauceLabs', - browserName: 'internet explorer', - platform: 'Windows 2012', - version: '10' - }, - SL_IE_11: { - base: 'SauceLabs', - browserName: 'internet explorer', - platform: 'Windows 8.1', - version: '11' - }, - SL_iOS: { - base: 'SauceLabs', - browserName: 'iphone', - platform: 'OS X 10.10', - version: '8.1' - } -}; diff --git a/karma.conf.js b/karma.conf.js index 49a79eb5..1b3120f9 100644 --- a/karma.conf.js +++ b/karma.conf.js @@ -22,11 +22,11 @@ module.exports = function(config) { coverageReporter: { reporters: [ { - type: 'lcov' - }, - { - type: 'text-summary' - } + type: 'lcov' + }, + { + type: 'text-summary' + } ], instrumenterOptions: { istanbul: { diff --git a/lib/User.js b/lib/User.js index 6e08ffdb..4035a571 100644 --- a/lib/User.js +++ b/lib/User.js @@ -140,4 +140,8 @@ User.prototype.createRepo = function(options, cb) { this._request('POST', '/user/repos', options, cb); }; +User.prototype.rateLimit = function(cb) { + this._request('GET', '/rate_limit', null, cb); +}; + module.exports = User; diff --git a/lib/helpers/Requestable.js b/lib/helpers/Requestable.js index 61a6bd75..1519b28a 100644 --- a/lib/helpers/Requestable.js +++ b/lib/helpers/Requestable.js @@ -95,13 +95,17 @@ Requestable.prototype._request = function(method, path, data, cb, raw) { var requestPromise = getRequestPromise(config); if (cb) { + debug('callback provided: making request for user'); requestPromise.then(function(response) { + debug('', 'response', response.config.method, response.config.url); cb(null, response.data || true, response); }).catch(function(response) { debug('', 'catch', response.config.method, response.config.url); cb(buildError(path, response)); }); } else { + debug('no callback: returning promise'); + return requestPromise; } }; diff --git a/package.json b/package.json index 9f61b682..e7cefa52 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,7 @@ "license": "BSD-3-Clause-Clear", "readmeFilename": "README.md", "scripts": { - "test": "gulp test && gulp lint", + "test": "gulp lint && gulp test", "lint": "gulp lint", "codecov": "cat coverage/*/lcov.info | codecov" }, @@ -32,16 +32,17 @@ "debug": "^2.2.0", "es6-promise": "^3.0.2", "inherits": "^2.0.1", - "js-base64": "^2.1.8", - "xmlhttprequest": "~1.7.0" + "js-base64": "^2.1.8" }, "devDependencies": { "chai": "^3.4.0", "codecov": "^1.0.1", "gulp": "^3.9.0", + "gulp-codecov.io": "^1.0.1", "gulp-jscs": "^3.0.1", "gulp-jscs-stylish": "^1.2.1", "gulp-jshint": "^1.11.2", + "gulp-mocha": "^2.2.0", "istanbul": "^0.3.13", "jshint": "^2.5.8", "jshint-stylish": "^2.0.1", @@ -54,6 +55,7 @@ "karma-mocha-reporter": "^1.1.1", "karma-sauce-launcher": "^0.3.0", "mocha": "^2.3.3", + "run-sequence": "^1.1.5", "webpack-stream": "^2.3.0" }, "gitHead": "aa8aa3c8cd5ce5240373d4fd1d06a7ab4af41a36" diff --git a/test/test.repo.js b/test/test.repo.js index 1e50cb66..b94239e5 100644 --- a/test/test.repo.js +++ b/test/test.repo.js @@ -147,7 +147,9 @@ describe('Repository', function() { should.not.exist(err); statuses.length.should.equal(6); statuses.every(function(status) { + // jscs:disable return status.url === 'https://api.github.com/repos/michael/github/statuses/20fcff9129005d14cc97b9d59b8a3d37f4fb633b'; + // jscs:enable }).should.equal(true); done(); })); @@ -295,9 +297,10 @@ describe('Repository', function() { it('should use repo.delete as an alias for repo.remove', function(done) { repo.write('master', 'REMOVE-TEST.md', 'THIS IS A TEST', 'Remove test', callbackWithError(done, function(err) { should.not.exist(err); - repo.delete('master', 'REMOVE-TEST.md', function(err) { - done(err); - }); + repo.delete('master', 'REMOVE-TEST.md', callbackWithError(done, function(err) { + should.not.exist(err); + done(); + })); })); }); diff --git a/test/test.user.js b/test/test.user.js index 396b3107..11bea77a 100644 --- a/test/test.user.js +++ b/test/test.user.js @@ -14,7 +14,7 @@ if (typeof window === 'undefined') { var should = chai.should(); } -describe('Github.User', function() { +describe('User', function() { var user, github; before(function() { @@ -27,14 +27,14 @@ describe('Github.User', function() { user = github.getUser(); }); - it('should get user.repos', function(done) { + it('should get user repos', function(done) { user.repos(callbackWithError(done, function(err) { should.not.exist(err); done(); })); }); - it('should get user.repos with options', function(done) { + it('should get user repos with options', function(done) { var options = { type: 'owner', sort: 'updated', @@ -50,28 +50,28 @@ describe('Github.User', function() { })); }); - it('should get user.orgs', function(done) { + it('should get user orgs', function(done) { user.orgs(callbackWithError(done, function(err) { should.not.exist(err); done(); })); }); - it('should get user.gists', function(done) { + it('should get user gists', function(done) { user.gists(function(err) { should.not.exist(err); done(); }); }); - it('should get user.notifications', function(done) { + it('should get user notifications', function(done) { user.notifications(callbackWithError(done, function(err) { should.not.exist(err); done(); })); }); - it('should get user.notifications with options', function(done) { + it('should get user notifications with options', function(done) { var options = { all: true, participating: true,