diff --git a/src/js/modules/github/issues.js b/src/js/modules/github/issues.js index 28a36ab..5449691 100644 --- a/src/js/modules/github/issues.js +++ b/src/js/modules/github/issues.js @@ -6,7 +6,6 @@ import request from './request.js'; // Fetch issues for a milestone. export default { - fetchAll: (user, repo, cb) => { // For each `open` and `closed` issues in parallel. async.parallel([ @@ -16,7 +15,6 @@ export default { cb(err, { open, closed }); }); } - }; // Calculate size of either open or closed issues. @@ -27,7 +25,8 @@ let calcSize = (list) => { switch (config.chart.points) { case 'ONE_SIZE': size = list.length; - for (let issue of list) { issue.size = 1; } + // TODO: check we have an object? + for (let issue of list) issue.size = 1; break; case 'LABELS': diff --git a/test/fixtures/issues.json b/test/fixtures/issues.json new file mode 100644 index 0000000..f64dc76 --- /dev/null +++ b/test/fixtures/issues.json @@ -0,0 +1,96 @@ +[ + { + "url": "https://api.github.com/repos/octocat/Hello-World/issues/1347", + "html_url": "https://github.com/octocat/Hello-World/issues/1347", + "number": 1347, + "state": "open", + "title": "Found a bug", + "body": "I'm having a problem with this.", + "user": { + "login": "octocat", + "id": 1, + "avatar_url": "https://github.com/images/error/octocat_happy.gif", + "gravatar_id": "", + "url": "https://api.github.com/users/octocat", + "html_url": "https://github.com/octocat", + "followers_url": "https://api.github.com/users/octocat/followers", + "following_url": "https://api.github.com/users/octocat/following{/other_user}", + "gists_url": "https://api.github.com/users/octocat/gists{/gist_id}", + "starred_url": "https://api.github.com/users/octocat/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/octocat/subscriptions", + "organizations_url": "https://api.github.com/users/octocat/orgs", + "repos_url": "https://api.github.com/users/octocat/repos", + "events_url": "https://api.github.com/users/octocat/events{/privacy}", + "received_events_url": "https://api.github.com/users/octocat/received_events", + "type": "User", + "site_admin": false + }, + "labels": [ + { + "url": "https://api.github.com/repos/octocat/Hello-World/labels/bug", + "name": "bug", + "color": "f29513" + } + ], + "assignee": { + "login": "octocat", + "id": 1, + "avatar_url": "https://github.com/images/error/octocat_happy.gif", + "gravatar_id": "", + "url": "https://api.github.com/users/octocat", + "html_url": "https://github.com/octocat", + "followers_url": "https://api.github.com/users/octocat/followers", + "following_url": "https://api.github.com/users/octocat/following{/other_user}", + "gists_url": "https://api.github.com/users/octocat/gists{/gist_id}", + "starred_url": "https://api.github.com/users/octocat/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/octocat/subscriptions", + "organizations_url": "https://api.github.com/users/octocat/orgs", + "repos_url": "https://api.github.com/users/octocat/repos", + "events_url": "https://api.github.com/users/octocat/events{/privacy}", + "received_events_url": "https://api.github.com/users/octocat/received_events", + "type": "User", + "site_admin": false + }, + "milestone": { + "url": "https://api.github.com/repos/octocat/Hello-World/milestones/1", + "number": 1, + "state": "open", + "title": "v1.0", + "description": "", + "creator": { + "login": "octocat", + "id": 1, + "avatar_url": "https://github.com/images/error/octocat_happy.gif", + "gravatar_id": "", + "url": "https://api.github.com/users/octocat", + "html_url": "https://github.com/octocat", + "followers_url": "https://api.github.com/users/octocat/followers", + "following_url": "https://api.github.com/users/octocat/following{/other_user}", + "gists_url": "https://api.github.com/users/octocat/gists{/gist_id}", + "starred_url": "https://api.github.com/users/octocat/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/octocat/subscriptions", + "organizations_url": "https://api.github.com/users/octocat/orgs", + "repos_url": "https://api.github.com/users/octocat/repos", + "events_url": "https://api.github.com/users/octocat/events{/privacy}", + "received_events_url": "https://api.github.com/users/octocat/received_events", + "type": "User", + "site_admin": false + }, + "open_issues": 4, + "closed_issues": 8, + "created_at": "2011-04-10T20:09:31Z", + "updated_at": "2014-03-03T18:58:10Z", + "due_on": null + }, + "comments": 0, + "pull_request": { + "url": "https://api.github.com/repos/octocat/Hello-World/pulls/1347", + "html_url": "https://github.com/octocat/Hello-World/pull/1347", + "diff_url": "https://github.com/octocat/Hello-World/pull/1347.diff", + "patch_url": "https://github.com/octocat/Hello-World/pull/1347.patch" + }, + "closed_at": null, + "created_at": "2011-04-22T13:33:48Z", + "updated_at": "2011-04-22T13:33:48Z" + } +] \ No newline at end of file diff --git a/test/fixtures/milestone.json b/test/fixtures/milestone.json new file mode 100644 index 0000000..8f5a762 --- /dev/null +++ b/test/fixtures/milestone.json @@ -0,0 +1,31 @@ +{ + "url": "https://api.github.com/repos/octocat/Hello-World/milestones/1", + "number": 1, + "state": "open", + "title": "v1.0", + "description": "", + "creator": { + "login": "octocat", + "id": 1, + "avatar_url": "https://github.com/images/error/octocat_happy.gif", + "gravatar_id": "", + "url": "https://api.github.com/users/octocat", + "html_url": "https://github.com/octocat", + "followers_url": "https://api.github.com/users/octocat/followers", + "following_url": "https://api.github.com/users/octocat/following{/other_user}", + "gists_url": "https://api.github.com/users/octocat/gists{/gist_id}", + "starred_url": "https://api.github.com/users/octocat/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/octocat/subscriptions", + "organizations_url": "https://api.github.com/users/octocat/orgs", + "repos_url": "https://api.github.com/users/octocat/repos", + "events_url": "https://api.github.com/users/octocat/events{/privacy}", + "received_events_url": "https://api.github.com/users/octocat/received_events", + "type": "User", + "site_admin": false + }, + "open_issues": 4, + "closed_issues": 8, + "created_at": "2011-04-10T20:09:31Z", + "updated_at": "2014-03-03T18:58:10Z", + "due_on": null +} \ No newline at end of file diff --git a/test/fixtures/milestones.json b/test/fixtures/milestones.json new file mode 100644 index 0000000..de0cc0b --- /dev/null +++ b/test/fixtures/milestones.json @@ -0,0 +1,33 @@ +[ + { + "url": "https://api.github.com/repos/octocat/Hello-World/milestones/1", + "number": 1, + "state": "open", + "title": "v1.0", + "description": "", + "creator": { + "login": "octocat", + "id": 1, + "avatar_url": "https://github.com/images/error/octocat_happy.gif", + "gravatar_id": "", + "url": "https://api.github.com/users/octocat", + "html_url": "https://github.com/octocat", + "followers_url": "https://api.github.com/users/octocat/followers", + "following_url": "https://api.github.com/users/octocat/following{/other_user}", + "gists_url": "https://api.github.com/users/octocat/gists{/gist_id}", + "starred_url": "https://api.github.com/users/octocat/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/octocat/subscriptions", + "organizations_url": "https://api.github.com/users/octocat/orgs", + "repos_url": "https://api.github.com/users/octocat/repos", + "events_url": "https://api.github.com/users/octocat/events{/privacy}", + "received_events_url": "https://api.github.com/users/octocat/received_events", + "type": "User", + "site_admin": false + }, + "open_issues": 4, + "closed_issues": 8, + "created_at": "2011-04-10T20:09:31Z", + "updated_at": "2014-03-03T18:58:10Z", + "due_on": null + } +] \ No newline at end of file diff --git a/test/fixtures/repositories.json b/test/fixtures/repositories.json new file mode 100644 index 0000000..b24b4a3 --- /dev/null +++ b/test/fixtures/repositories.json @@ -0,0 +1,56 @@ +[ + { + "id": 1296269, + "owner": { + "login": "octocat", + "id": 1, + "avatar_url": "https://github.com/images/error/octocat_happy.gif", + "gravatar_id": "", + "url": "https://api.github.com/users/octocat", + "html_url": "https://github.com/octocat", + "followers_url": "https://api.github.com/users/octocat/followers", + "following_url": "https://api.github.com/users/octocat/following{/other_user}", + "gists_url": "https://api.github.com/users/octocat/gists{/gist_id}", + "starred_url": "https://api.github.com/users/octocat/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/octocat/subscriptions", + "organizations_url": "https://api.github.com/users/octocat/orgs", + "repos_url": "https://api.github.com/users/octocat/repos", + "events_url": "https://api.github.com/users/octocat/events{/privacy}", + "received_events_url": "https://api.github.com/users/octocat/received_events", + "type": "User", + "site_admin": false + }, + "name": "Hello-World", + "full_name": "octocat/Hello-World", + "description": "This your first repo!", + "private": false, + "fork": false, + "url": "https://api.github.com/repos/octocat/Hello-World", + "html_url": "https://github.com/octocat/Hello-World", + "clone_url": "https://github.com/octocat/Hello-World.git", + "git_url": "git://github.com/octocat/Hello-World.git", + "ssh_url": "git@github.com:octocat/Hello-World.git", + "svn_url": "https://svn.github.com/octocat/Hello-World", + "mirror_url": "git://git.example.com/octocat/Hello-World", + "homepage": "https://github.com", + "language": null, + "forks_count": 9, + "stargazers_count": 80, + "watchers_count": 80, + "size": 108, + "default_branch": "master", + "open_issues_count": 0, + "has_issues": true, + "has_wiki": true, + "has_pages": false, + "has_downloads": true, + "pushed_at": "2011-01-26T19:06:43Z", + "created_at": "2011-01-26T19:01:12Z", + "updated_at": "2011-01-26T19:14:43Z", + "permissions": { + "admin": false, + "push": false, + "pull": true + } + } +] \ No newline at end of file diff --git a/test/issues.js b/test/issues.js new file mode 100644 index 0000000..ed1023b --- /dev/null +++ b/test/issues.js @@ -0,0 +1,296 @@ +import { assert } from 'chai'; +import moment from 'moment'; +import _ from 'lodash'; +import opa from 'object-path'; + +import request from '../src/js/modules/github/request.js'; +import issues from '../src/js/modules/github/issues.js'; +import config from '../src/js/models/config.js'; + +import json from './fixtures/issues.json'; + +let repo = { + 'owner': 'radekstepan', + 'name': 'burnchart', + 'milestone': 1 +}; + +export default { + 'issues - time format': (done) => { + // ISO 8601 dates are in UTC timezone. + let utc = moment(json[0].created_at).toDate().toUTCString(); + assert(utc, 'Fri, 22 Apr 2011 13:33:48 GMT'); + done(); + }, + + 'issues - all empty': (done) => { + let called = 0; + request.allIssues = (user, repo, opts, cb) => { + called += 1; + cb(null, []); + }; + + opa.set(config, 'chart.points', 'ONE_SIZE'); + + issues.fetchAll({}, repo, (err, { open, closed }) => { + assert.isNull(err); + assert(called, 2); + assert.strictEqual(open.size, 0); + assert.strictEqual(closed.size, 0); + done(); + }); + }, + + 'issues - open empty': (done) => { + let called = 0; + request.allIssues = (user, repo, opts, cb) => { + called += 1; + cb(null, called === 1 ? [] : [{ 'number': 1 }]); + }; + + opa.set(config, 'chart.points', 'ONE_SIZE'); + + issues.fetchAll({}, repo, (err, { open, closed }) => { + assert.isNull(err); + assert(called, 2); + assert.strictEqual(open.size, 0); + assert.strictEqual(open.list.length, 0); + assert(closed.size, 1); + assert(closed.list.length, 1); + done(); + }); + }, + + 'issues - closed empty': (done) => { + let called = 0; + request.allIssues = (user, repo, opts, cb) => { + called += 1; + cb(null, called === 2 ? [] : [{ 'number': 1 }]); + }; + + opa.set(config, 'chart.points', 'ONE_SIZE'); + + issues.fetchAll({}, repo, (err, { open, closed }) => { + assert.isNull(err); + assert(called, 2); + assert(open.size, 1); + assert.strictEqual(closed.size, 0); + done(); + }); + }, + + 'issues - both not empty': (done) => { + let called = 0; + request.allIssues = (user, repo, opts, cb) => { + called += 1; + cb(null, [{ 'number': 1 } ]); + }; + + opa.set(config, 'chart.points', 'ONE_SIZE'); + + issues.fetchAll({}, repo, (err, { open, closed }) => { + assert.isNull(err); + assert(called, 2); + assert(open.size, 1); + assert(closed.size, 1); + done(); + }); + }, + + 'issues - 99 results on a page': (done) => { + let called = 0; + request.allIssues = (user, repo, opts, cb) => { + called += 1; + cb(null, _.range(99).map((number) => { return { number }; } )); + }; + + opa.set(config, 'chart.points', 'ONE_SIZE'); + + issues.fetchAll({}, repo, (err, { open, closed }) => { + assert.isNull(err); + assert(called, 2); + assert(open.size, 99); + assert(closed.size, 99); + done(); + }); + }, + + 'issues - 100 results on a page': (done) => { + let called = 0; + request.allIssues = (user, repo, opts, cb) => { + called += 1; + switch(opts.page) { + case 1: + return cb(null, _.range(100).map((number) => { return { number }; })); + case 2: + return cb(null, []); + default: + assert(false); + } + }; + + opa.set(config, 'chart.points', 'ONE_SIZE'); + + issues.fetchAll({}, repo, function(err, { open, closed }) { + assert.isNull(err); + assert(called, 4); + assert(open.size, 100); + assert(closed.size, 100); + done(); + }); + }, + + 'issues - 101 total results': (done) => { + let called = 0; + request.allIssues = (user, repo, opts, cb) => { + called += 1; + switch(opts.page) { + case 1: + return cb(null, _.range(100).map((number) => { return { number }; })); + case 2: + return cb(null, [{ 'number': 100 }]); + default: + assert(false); + } + }; + + opa.set(config, 'chart.points', 'ONE_SIZE'); + + issues.fetchAll({}, repo, (err, { open, closed }) => { + assert.isNull(err); + assert(called, 4); + assert(open.size, 101); + assert(closed.size, 101); + assert.deepEqual(open.list[100], { 'number': 100, 'size': 1 }); + assert.deepEqual(closed.list[100], { 'number': 100, 'size': 1 }); + done(); + }); + }, + + 'issues - 201 total results': (done) => { + let called = 0; + request.allIssues = (user, repo, opts, cb) => { + called += 1; + switch(opts.page) { + case 1: + case 2: + let i = 100 * (opts.page - 1); + return cb(null, _.range(i, i + 100).map((number) => { return { number }; })); + case 3: + return cb(null, [{ 'number': 200 }]); + default: + assert(false); + } + }; + + opa.set(config, 'chart.points', 'ONE_SIZE'); + + issues.fetchAll({}, repo, (err, { open, closed }) => { + assert.isNull(err); + assert(called, 6); + assert(open.size, 201); + assert(closed.size, 201); + _.each([ open, closed ], ({ list }) => { + _.each([ 100, 200 ], (number) => { + assert.deepEqual(list[number], { number, 'size': 1 }); + }); + }); + done(); + }); + }, + + 'issues - get all when not found': (done) => { + let called = 0; + request.allIssues = (user, repo, opts, cb) => { + called += 1; + cb('Not Found'); + }; + + opa.set(config, 'chart.points', 'ONE_SIZE'); + + issues.fetchAll({}, repo, (err, { open, closed }) => { + assert(err, 'Not Found'); + assert(called, 1); + done(); + }); + }, + + 'issues - size based on a label': (done) => { + opa.set(config, 'chart.points', 'LABELS'); + + request.allIssues = (user, repo, opts, cb) => { + cb(null, [ + { 'labels': [{ 'name': 'size 2' }]}, + { 'labels': [{ 'name': 'size 10' }]}, + { 'labels': [{ 'name': 'size A' }]} + ]); + }; + + issues.fetchAll({}, repo, (err, { open, closed }) => { + assert.isNull(err); + assert(open.size, 12); + assert(open.list[0].size, 2); + done(); + }); + }, + + 'issues - filter when no labels': (done) => { + opa.set(config, 'chart.points', 'LABELS'); + + request.allIssues = (user, repo, opts, cb) => cb(null, [{}]); + + issues.fetchAll({}, repo, (err, { open, closed }) => { + assert.isNull(err); + assert.strictEqual(open.size, 0); + done(); + }); + }, + + 'issues - filter when empty labels': (done) => { + opa.set(config, 'chart.points', 'LABELS'); + + request.allIssues = (user, repo, opts, cb) => { + cb(null, [{ 'labels': [] }]); + }; + + issues.fetchAll({}, repo, (err, { open, closed }) => { + assert.isNull(err); + assert.strictEqual(open.size, 0); + done(); + }); + }, + + 'issues - filter when not matching regex': (done) => { + opa.set(config, 'chart.points', 'LABELS'); + + request.allIssues = (user, repo, opts, cb) => { + cb(null, [{ 'labels': [{ 'name': 'size 1A' }] }]); + }; + + issues.fetchAll({}, repo, (err, { open, closed }) => { + assert.isNull(err); + assert.strictEqual(open.size, 0); + done(); + }); + }, + + 'issues - filter when multiple match the regex': (done) => { + opa.set(config, 'chart.points', 'LABELS'); + + request.allIssues = (user, repo, opts, cb) => { + cb(null, [ + { 'labels': [ { 'name': 'size 1' }, { 'name': 'size 6' } ]}, + { 'labels': [ { 'name': 'size really big' }, { 'name': 'size 4' } ]} + ]); + }; + + issues.fetchAll({}, repo, (err, { open, closed }) => { + assert.isNull(err); + assert(open.size, 11); + let [ a, b ] = open.list; + assert(a.size, 7); + assert(b.size, 4); + done(); + }); + } +};