@opentermsarchive/engine 2.3.1 → 2.3.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/reporter/github.js +71 -46
- package/src/reporter/github.test.js +129 -202
- package/src/reporter/index.js +4 -0
package/package.json
CHANGED
package/src/reporter/github.js
CHANGED
|
@@ -28,6 +28,24 @@ export default class GitHub {
|
|
|
28
28
|
const [ owner, repo ] = repository.split('/');
|
|
29
29
|
|
|
30
30
|
this.commonParams = { owner, repo };
|
|
31
|
+
|
|
32
|
+
this.issuesCache = new Map();
|
|
33
|
+
this._issuesPromise = null;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
get issues() {
|
|
37
|
+
if (!this._issuesPromise) {
|
|
38
|
+
logger.info('Loading issues from GitHub…');
|
|
39
|
+
this._issuesPromise = this.loadAllIssues();
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return this._issuesPromise;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
clearCache() {
|
|
46
|
+
this.issuesCache.clear();
|
|
47
|
+
this._issuesPromise = null;
|
|
48
|
+
logger.info('Issues cache cleared');
|
|
31
49
|
}
|
|
32
50
|
|
|
33
51
|
async initialize() {
|
|
@@ -53,6 +71,33 @@ export default class GitHub {
|
|
|
53
71
|
}
|
|
54
72
|
}
|
|
55
73
|
|
|
74
|
+
async loadAllIssues() {
|
|
75
|
+
try {
|
|
76
|
+
const issues = await this.octokit.paginate('GET /repos/{owner}/{repo}/issues', {
|
|
77
|
+
...this.commonParams,
|
|
78
|
+
state: GitHub.ISSUE_STATE_ALL,
|
|
79
|
+
per_page: 100,
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
const onlyIssues = issues.filter(issue => !issue.pull_request); // Filter out pull requests since GitHub treats them as a special type of issue
|
|
83
|
+
|
|
84
|
+
onlyIssues.forEach(issue => {
|
|
85
|
+
const cachedIssue = this.issuesCache.get(issue.title);
|
|
86
|
+
|
|
87
|
+
if (!cachedIssue || new Date(issue.created_at) < new Date(cachedIssue.created_at)) { // Only work on the oldest issue if there are duplicates, in order to consolidate the longest history possible
|
|
88
|
+
this.issuesCache.set(issue.title, issue);
|
|
89
|
+
}
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
logger.info(`Cached ${onlyIssues.length} issues from the GitHub repository`);
|
|
93
|
+
|
|
94
|
+
return this.issuesCache;
|
|
95
|
+
} catch (error) {
|
|
96
|
+
logger.error(`Failed to load issues: ${error.message}`);
|
|
97
|
+
throw error;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
56
101
|
async getRepositoryLabels() {
|
|
57
102
|
const { data: labels } = await this.octokit.request('GET /repos/{owner}/{repo}/labels', { ...this.commonParams });
|
|
58
103
|
|
|
@@ -68,6 +113,10 @@ export default class GitHub {
|
|
|
68
113
|
});
|
|
69
114
|
}
|
|
70
115
|
|
|
116
|
+
async getIssue(title) {
|
|
117
|
+
return (await this.issues).get(title);
|
|
118
|
+
}
|
|
119
|
+
|
|
71
120
|
async createIssue({ title, description: body, labels }) {
|
|
72
121
|
const { data: issue } = await this.octokit.request('POST /repos/{owner}/{repo}/issues', {
|
|
73
122
|
...this.commonParams,
|
|
@@ -76,43 +125,22 @@ export default class GitHub {
|
|
|
76
125
|
labels,
|
|
77
126
|
});
|
|
78
127
|
|
|
128
|
+
this.issuesCache.set(issue.title, issue);
|
|
129
|
+
|
|
79
130
|
return issue;
|
|
80
131
|
}
|
|
81
132
|
|
|
82
|
-
async
|
|
83
|
-
await this.octokit.request('
|
|
133
|
+
async updateIssue(issue, { state, labels }) {
|
|
134
|
+
const { data: updatedIssue } = await this.octokit.request('PATCH /repos/{owner}/{repo}/issues/{issue_number}', {
|
|
84
135
|
...this.commonParams,
|
|
85
136
|
issue_number: issue.number,
|
|
137
|
+
state,
|
|
86
138
|
labels,
|
|
87
139
|
});
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
async openIssue(issue) {
|
|
91
|
-
await this.octokit.request('PATCH /repos/{owner}/{repo}/issues/{issue_number}', {
|
|
92
|
-
...this.commonParams,
|
|
93
|
-
issue_number: issue.number,
|
|
94
|
-
state: GitHub.ISSUE_STATE_OPEN,
|
|
95
|
-
});
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
async closeIssue(issue) {
|
|
99
|
-
await this.octokit.request('PATCH /repos/{owner}/{repo}/issues/{issue_number}', {
|
|
100
|
-
...this.commonParams,
|
|
101
|
-
issue_number: issue.number,
|
|
102
|
-
state: GitHub.ISSUE_STATE_CLOSED,
|
|
103
|
-
});
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
async getIssue({ title, ...searchParams }) {
|
|
107
|
-
const issues = await this.octokit.paginate('GET /repos/{owner}/{repo}/issues', {
|
|
108
|
-
...this.commonParams,
|
|
109
|
-
per_page: 100,
|
|
110
|
-
...searchParams,
|
|
111
|
-
}, response => response.data);
|
|
112
140
|
|
|
113
|
-
|
|
141
|
+
this.issuesCache.set(updatedIssue.title, updatedIssue);
|
|
114
142
|
|
|
115
|
-
return
|
|
143
|
+
return updatedIssue;
|
|
116
144
|
}
|
|
117
145
|
|
|
118
146
|
async addCommentToIssue({ issue, comment: body }) {
|
|
@@ -127,24 +155,25 @@ export default class GitHub {
|
|
|
127
155
|
|
|
128
156
|
async closeIssueWithCommentIfExists({ title, comment }) {
|
|
129
157
|
try {
|
|
130
|
-
const
|
|
158
|
+
const issue = await this.getIssue(title);
|
|
131
159
|
|
|
132
|
-
if (!
|
|
160
|
+
if (!issue || issue.state == GitHub.ISSUE_STATE_CLOSED) {
|
|
133
161
|
return;
|
|
134
162
|
}
|
|
135
163
|
|
|
136
|
-
await this.addCommentToIssue({ issue
|
|
137
|
-
|
|
164
|
+
await this.addCommentToIssue({ issue, comment });
|
|
165
|
+
|
|
166
|
+
const updatedIssue = await this.updateIssue(issue, { state: GitHub.ISSUE_STATE_CLOSED });
|
|
138
167
|
|
|
139
|
-
|
|
168
|
+
logger.info(`Closed issue with comment #${updatedIssue.number}: ${updatedIssue.html_url}`);
|
|
140
169
|
} catch (error) {
|
|
141
|
-
logger.error(`Failed to
|
|
170
|
+
logger.error(`Failed to close issue with comment "${title}": ${error.stack}`);
|
|
142
171
|
}
|
|
143
172
|
}
|
|
144
173
|
|
|
145
174
|
async createOrUpdateIssue({ title, description, label }) {
|
|
146
175
|
try {
|
|
147
|
-
const issue = await this.getIssue(
|
|
176
|
+
const issue = await this.getIssue(title);
|
|
148
177
|
|
|
149
178
|
if (!issue) {
|
|
150
179
|
const createdIssue = await this.createIssue({ title, description, labels: [label] });
|
|
@@ -152,25 +181,21 @@ export default class GitHub {
|
|
|
152
181
|
return logger.info(`Created issue #${createdIssue.number} "${title}": ${createdIssue.html_url}`);
|
|
153
182
|
}
|
|
154
183
|
|
|
155
|
-
if (issue.state == GitHub.ISSUE_STATE_CLOSED) {
|
|
156
|
-
await this.openIssue(issue);
|
|
157
|
-
logger.info(`Reopened issue #${issue.number}: ${issue.html_url}`);
|
|
158
|
-
}
|
|
159
|
-
|
|
160
184
|
const managedLabelsNames = this.MANAGED_LABELS.map(label => label.name);
|
|
161
|
-
const
|
|
185
|
+
const labelsNotManagedToKeep = issue.labels.map(label => label.name).filter(label => !managedLabelsNames.includes(label));
|
|
186
|
+
const [managedLabel] = issue.labels.filter(label => managedLabelsNames.includes(label.name)); // It is assumed that only one specific reason for failure is possible at a time, making managed labels mutually exclusive
|
|
162
187
|
|
|
163
|
-
if (managedLabel?.name
|
|
188
|
+
if (issue.state !== GitHub.ISSUE_STATE_CLOSED && managedLabel?.name === label) {
|
|
164
189
|
return;
|
|
165
190
|
}
|
|
166
191
|
|
|
167
|
-
const
|
|
192
|
+
const updatedIssue = await this.updateIssue(issue, { state: GitHub.ISSUE_STATE_OPEN, labels: [ label, ...labelsNotManagedToKeep ] });
|
|
168
193
|
|
|
169
|
-
await this.setIssueLabels({ issue, labels: [ label, ...labelsNotManagedToKeep ] });
|
|
170
194
|
await this.addCommentToIssue({ issue, comment: description });
|
|
171
|
-
|
|
195
|
+
|
|
196
|
+
logger.info(`Updated issue with comment #${updatedIssue.number}: ${updatedIssue.html_url}`);
|
|
172
197
|
} catch (error) {
|
|
173
|
-
logger.error(`Failed to update issue "${title}": ${error.
|
|
198
|
+
logger.error(`Failed to update issue "${title}": ${error.stack}`);
|
|
174
199
|
}
|
|
175
200
|
}
|
|
176
201
|
}
|
|
@@ -12,10 +12,17 @@ describe('GitHub', function () {
|
|
|
12
12
|
|
|
13
13
|
let MANAGED_LABELS;
|
|
14
14
|
let github;
|
|
15
|
+
const EXISTING_OPEN_ISSUE = { number: 1, title: 'Opened issue', description: 'Issue description', state: GitHub.ISSUE_STATE_OPEN, labels: [{ name: 'location' }] };
|
|
16
|
+
const EXISTING_CLOSED_ISSUE = { number: 2, title: 'Closed issue', description: 'Issue description', state: GitHub.ISSUE_STATE_CLOSED, labels: [{ name: '403' }] };
|
|
15
17
|
|
|
16
|
-
before(() => {
|
|
18
|
+
before(async () => {
|
|
17
19
|
MANAGED_LABELS = require('./labels.json');
|
|
18
20
|
github = new GitHub('owner/repo');
|
|
21
|
+
nock('https://api.github.com')
|
|
22
|
+
.get('/repos/owner/repo/issues')
|
|
23
|
+
.query(true)
|
|
24
|
+
.reply(200, [ EXISTING_OPEN_ISSUE, EXISTING_CLOSED_ISSUE ]);
|
|
25
|
+
await github.clearCache();
|
|
19
26
|
});
|
|
20
27
|
|
|
21
28
|
describe('#initialize', () => {
|
|
@@ -71,137 +78,88 @@ describe('GitHub', function () {
|
|
|
71
78
|
});
|
|
72
79
|
|
|
73
80
|
describe('#createLabel', () => {
|
|
74
|
-
let scope;
|
|
75
81
|
const LABEL = { name: 'new_label', color: 'ffffff' };
|
|
76
82
|
|
|
77
|
-
|
|
78
|
-
|
|
83
|
+
afterEach(nock.cleanAll);
|
|
84
|
+
|
|
85
|
+
it('creates the new label successfully', async () => {
|
|
86
|
+
const scope = nock('https://api.github.com')
|
|
79
87
|
.post('/repos/owner/repo/labels', body => body.name === LABEL.name)
|
|
80
88
|
.reply(200, LABEL);
|
|
81
89
|
|
|
82
90
|
await github.createLabel(LABEL);
|
|
91
|
+
expect(scope.isDone()).to.be.true;
|
|
83
92
|
});
|
|
84
93
|
|
|
85
|
-
|
|
94
|
+
it('throws an error when creating a label fails', async () => {
|
|
95
|
+
nock('https://api.github.com')
|
|
96
|
+
.post('/repos/owner/repo/labels')
|
|
97
|
+
.reply(400, { message: 'Bad Request' });
|
|
86
98
|
|
|
87
|
-
|
|
88
|
-
expect(scope.isDone()).to.be.true;
|
|
99
|
+
await expect(github.createLabel(LABEL)).to.be.rejected;
|
|
89
100
|
});
|
|
90
101
|
});
|
|
91
102
|
|
|
92
103
|
describe('#createIssue', () => {
|
|
93
104
|
let scope;
|
|
94
105
|
let result;
|
|
106
|
+
|
|
95
107
|
const ISSUE = {
|
|
96
108
|
title: 'New Issue',
|
|
97
109
|
description: 'Description of the new issue',
|
|
98
|
-
labels: ['bug'],
|
|
110
|
+
labels: [{ name: 'bug' }],
|
|
99
111
|
};
|
|
100
112
|
const CREATED_ISSUE = { number: 123, ...ISSUE };
|
|
101
113
|
|
|
102
114
|
before(async () => {
|
|
103
115
|
scope = nock('https://api.github.com')
|
|
104
|
-
.post('/repos/owner/repo/issues', request =>
|
|
116
|
+
.post('/repos/owner/repo/issues', request =>
|
|
117
|
+
request.title === ISSUE.title && request.body === ISSUE.description && request.labels[0].name === ISSUE.labels[0].name)
|
|
105
118
|
.reply(200, CREATED_ISSUE);
|
|
106
119
|
|
|
107
120
|
result = await github.createIssue(ISSUE);
|
|
108
121
|
});
|
|
109
122
|
|
|
110
|
-
after(
|
|
123
|
+
after(() => {
|
|
124
|
+
nock.cleanAll();
|
|
125
|
+
github.issuesCache.delete(ISSUE.title);
|
|
126
|
+
});
|
|
111
127
|
|
|
112
128
|
it('creates the new issue', () => {
|
|
113
129
|
expect(scope.isDone()).to.be.true;
|
|
114
130
|
});
|
|
115
131
|
|
|
116
|
-
it('returns the created issue', () => {
|
|
132
|
+
it('returns the newly created issue', () => {
|
|
117
133
|
expect(result).to.deep.equal(CREATED_ISSUE);
|
|
118
134
|
});
|
|
119
|
-
});
|
|
120
|
-
|
|
121
|
-
describe('#setIssueLabels', () => {
|
|
122
|
-
let scope;
|
|
123
|
-
const ISSUE_NUMBER = 123;
|
|
124
|
-
const LABELS = [ 'bug', 'enhancement' ];
|
|
125
|
-
|
|
126
|
-
before(async () => {
|
|
127
|
-
scope = nock('https://api.github.com')
|
|
128
|
-
.put(`/repos/owner/repo/issues/${ISSUE_NUMBER}/labels`, { labels: LABELS })
|
|
129
|
-
.reply(200);
|
|
130
|
-
|
|
131
|
-
await github.setIssueLabels({ issue: { number: ISSUE_NUMBER }, labels: LABELS });
|
|
132
|
-
});
|
|
133
|
-
|
|
134
|
-
after(nock.cleanAll);
|
|
135
|
-
|
|
136
|
-
it('sets labels on the issue', () => {
|
|
137
|
-
expect(scope.isDone()).to.be.true;
|
|
138
|
-
});
|
|
139
|
-
});
|
|
140
|
-
|
|
141
|
-
describe('#openIssue', () => {
|
|
142
|
-
let scope;
|
|
143
|
-
const ISSUE = { number: 123 };
|
|
144
|
-
const EXPECTED_REQUEST_BODY = { state: 'open' };
|
|
145
|
-
|
|
146
|
-
before(async () => {
|
|
147
|
-
scope = nock('https://api.github.com')
|
|
148
|
-
.patch(`/repos/owner/repo/issues/${ISSUE.number}`, EXPECTED_REQUEST_BODY)
|
|
149
|
-
.reply(200);
|
|
150
|
-
|
|
151
|
-
await github.openIssue(ISSUE);
|
|
152
|
-
});
|
|
153
|
-
|
|
154
|
-
after(nock.cleanAll);
|
|
155
|
-
|
|
156
|
-
it('opens the issue', () => {
|
|
157
|
-
expect(scope.isDone()).to.be.true;
|
|
158
|
-
});
|
|
159
|
-
});
|
|
160
135
|
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
before(async () => {
|
|
167
|
-
scope = nock('https://api.github.com')
|
|
168
|
-
.patch(`/repos/owner/repo/issues/${ISSUE.number}`, EXPECTED_REQUEST_BODY)
|
|
169
|
-
.reply(200);
|
|
170
|
-
|
|
171
|
-
await github.closeIssue(ISSUE);
|
|
172
|
-
});
|
|
173
|
-
|
|
174
|
-
after(nock.cleanAll);
|
|
136
|
+
it('throws error when creating issue fails', async () => {
|
|
137
|
+
nock('https://api.github.com')
|
|
138
|
+
.post('/repos/owner/repo/issues')
|
|
139
|
+
.reply(400, { message: 'Bad Request' });
|
|
175
140
|
|
|
176
|
-
|
|
177
|
-
expect(scope.isDone()).to.be.true;
|
|
141
|
+
await expect(github.createIssue(ISSUE)).to.be.rejected;
|
|
178
142
|
});
|
|
179
143
|
});
|
|
180
144
|
|
|
181
145
|
describe('#getIssue', () => {
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
const ISSUE = { number: 123, title: 'Test Issue' };
|
|
186
|
-
const ANOTHER_ISSUE = { number: 124, title: 'Test Issue 2' };
|
|
187
|
-
|
|
188
|
-
before(async () => {
|
|
189
|
-
scope = nock('https://api.github.com')
|
|
146
|
+
before(() => {
|
|
147
|
+
nock('https://api.github.com')
|
|
190
148
|
.get('/repos/owner/repo/issues')
|
|
191
149
|
.query(true)
|
|
192
|
-
.reply(200, [
|
|
193
|
-
|
|
194
|
-
result = await github.getIssue({ title: ISSUE.title });
|
|
150
|
+
.reply(200, [ EXISTING_OPEN_ISSUE, EXISTING_CLOSED_ISSUE ]);
|
|
195
151
|
});
|
|
196
152
|
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
153
|
+
context('when the issue exists in the cache', () => {
|
|
154
|
+
it('returns the cached issue', async () => {
|
|
155
|
+
expect(await github.getIssue(EXISTING_OPEN_ISSUE.title)).to.deep.equal(EXISTING_OPEN_ISSUE);
|
|
156
|
+
});
|
|
201
157
|
});
|
|
202
158
|
|
|
203
|
-
|
|
204
|
-
|
|
159
|
+
context('when the issue does not exist in the cache', () => {
|
|
160
|
+
it('returns undefined', async () => {
|
|
161
|
+
expect(await github.getIssue('Non-existent Issue')).to.be.undefined;
|
|
162
|
+
});
|
|
205
163
|
});
|
|
206
164
|
});
|
|
207
165
|
|
|
@@ -223,35 +181,33 @@ describe('GitHub', function () {
|
|
|
223
181
|
it('adds the comment to the issue', () => {
|
|
224
182
|
expect(scope.isDone()).to.be.true;
|
|
225
183
|
});
|
|
184
|
+
|
|
185
|
+
it('throws an error when adding a comment fails', async () => {
|
|
186
|
+
nock('https://api.github.com')
|
|
187
|
+
.post(`/repos/owner/repo/issues/${ISSUE_NUMBER}/comments`)
|
|
188
|
+
.reply(400, { message: 'Bad Request' });
|
|
189
|
+
|
|
190
|
+
await expect(github.addCommentToIssue({ issue: { number: ISSUE_NUMBER }, comment: COMMENT })).to.be.rejected;
|
|
191
|
+
});
|
|
226
192
|
});
|
|
227
193
|
|
|
228
194
|
describe('#closeIssueWithCommentIfExists', () => {
|
|
229
195
|
after(nock.cleanAll);
|
|
230
196
|
|
|
231
197
|
context('when the issue exists and is open', () => {
|
|
232
|
-
const ISSUE = {
|
|
233
|
-
number: 123,
|
|
234
|
-
title: 'Open Issue',
|
|
235
|
-
state: GitHub.ISSUE_STATE_OPEN,
|
|
236
|
-
};
|
|
237
198
|
let addCommentScope;
|
|
238
199
|
let closeIssueScope;
|
|
239
200
|
|
|
240
201
|
before(async () => {
|
|
241
|
-
nock('https://api.github.com')
|
|
242
|
-
.get('/repos/owner/repo/issues')
|
|
243
|
-
.query(true)
|
|
244
|
-
.reply(200, [ISSUE]);
|
|
245
|
-
|
|
246
202
|
addCommentScope = nock('https://api.github.com')
|
|
247
|
-
.post(`/repos/owner/repo/issues/${
|
|
203
|
+
.post(`/repos/owner/repo/issues/${EXISTING_OPEN_ISSUE.number}/comments`)
|
|
248
204
|
.reply(200);
|
|
249
205
|
|
|
250
206
|
closeIssueScope = nock('https://api.github.com')
|
|
251
|
-
.patch(`/repos/owner/repo/issues/${
|
|
207
|
+
.patch(`/repos/owner/repo/issues/${EXISTING_OPEN_ISSUE.number}`, { state: GitHub.ISSUE_STATE_CLOSED })
|
|
252
208
|
.reply(200);
|
|
253
209
|
|
|
254
|
-
await github.closeIssueWithCommentIfExists({ title:
|
|
210
|
+
await github.closeIssueWithCommentIfExists({ title: EXISTING_OPEN_ISSUE.title, comment: 'Closing comment' });
|
|
255
211
|
});
|
|
256
212
|
|
|
257
213
|
it('adds comment to the issue', () => {
|
|
@@ -264,29 +220,19 @@ describe('GitHub', function () {
|
|
|
264
220
|
});
|
|
265
221
|
|
|
266
222
|
context('when the issue exists and is closed', () => {
|
|
267
|
-
const ISSUE = {
|
|
268
|
-
number: 123,
|
|
269
|
-
title: 'Closed Issue',
|
|
270
|
-
state: GitHub.ISSUE_STATE_CLOSED,
|
|
271
|
-
};
|
|
272
223
|
let addCommentScope;
|
|
273
224
|
let closeIssueScope;
|
|
274
225
|
|
|
275
226
|
before(async () => {
|
|
276
|
-
nock('https://api.github.com')
|
|
277
|
-
.get('/repos/owner/repo/issues')
|
|
278
|
-
.query(true)
|
|
279
|
-
.reply(200, []);
|
|
280
|
-
|
|
281
227
|
addCommentScope = nock('https://api.github.com')
|
|
282
|
-
.post(`/repos/owner/repo/issues/${
|
|
228
|
+
.post(`/repos/owner/repo/issues/${EXISTING_CLOSED_ISSUE.number}/comments`)
|
|
283
229
|
.reply(200);
|
|
284
230
|
|
|
285
231
|
closeIssueScope = nock('https://api.github.com')
|
|
286
|
-
.patch(`/repos/owner/repo/issues/${
|
|
232
|
+
.patch(`/repos/owner/repo/issues/${EXISTING_CLOSED_ISSUE.number}`, { state: GitHub.ISSUE_STATE_CLOSED })
|
|
287
233
|
.reply(200);
|
|
288
234
|
|
|
289
|
-
await github.closeIssueWithCommentIfExists({ title:
|
|
235
|
+
await github.closeIssueWithCommentIfExists({ title: EXISTING_CLOSED_ISSUE.title, comment: 'Closing comment' });
|
|
290
236
|
});
|
|
291
237
|
|
|
292
238
|
it('does not add comment', () => {
|
|
@@ -303,11 +249,6 @@ describe('GitHub', function () {
|
|
|
303
249
|
let closeIssueScope;
|
|
304
250
|
|
|
305
251
|
before(async () => {
|
|
306
|
-
nock('https://api.github.com')
|
|
307
|
-
.get('/repos/owner/repo/issues')
|
|
308
|
-
.query(true)
|
|
309
|
-
.reply(200, []);
|
|
310
|
-
|
|
311
252
|
addCommentScope = nock('https://api.github.com')
|
|
312
253
|
.post(/\/repos\/owner\/repo\/issues\/\d+\/comments/)
|
|
313
254
|
.reply(200);
|
|
@@ -330,12 +271,8 @@ describe('GitHub', function () {
|
|
|
330
271
|
});
|
|
331
272
|
|
|
332
273
|
describe('#createOrUpdateIssue', () => {
|
|
333
|
-
before(
|
|
334
|
-
|
|
335
|
-
.get('/repos/owner/repo/labels')
|
|
336
|
-
.reply(200, MANAGED_LABELS);
|
|
337
|
-
|
|
338
|
-
await github.initialize();
|
|
274
|
+
before(() => {
|
|
275
|
+
github.MANAGED_LABELS = require('./labels.json');
|
|
339
276
|
});
|
|
340
277
|
|
|
341
278
|
context('when the issue does not exist', () => {
|
|
@@ -347,11 +284,6 @@ describe('GitHub', function () {
|
|
|
347
284
|
};
|
|
348
285
|
|
|
349
286
|
before(async () => {
|
|
350
|
-
nock('https://api.github.com')
|
|
351
|
-
.get('/repos/owner/repo/issues')
|
|
352
|
-
.query(true)
|
|
353
|
-
.reply(200, []); // Simulate that there is no issues on the repository
|
|
354
|
-
|
|
355
287
|
createIssueScope = nock('https://api.github.com')
|
|
356
288
|
.post('/repos/owner/repo/issues', {
|
|
357
289
|
title: ISSUE_TO_CREATE.title,
|
|
@@ -363,58 +295,39 @@ describe('GitHub', function () {
|
|
|
363
295
|
await github.createOrUpdateIssue(ISSUE_TO_CREATE);
|
|
364
296
|
});
|
|
365
297
|
|
|
298
|
+
afterEach(() => {
|
|
299
|
+
github.issuesCache.delete(ISSUE_TO_CREATE.title);
|
|
300
|
+
});
|
|
301
|
+
|
|
366
302
|
it('creates the issue', () => {
|
|
367
303
|
expect(createIssueScope.isDone()).to.be.true;
|
|
368
304
|
});
|
|
369
305
|
});
|
|
370
306
|
|
|
371
307
|
context('when the issue already exists', () => {
|
|
372
|
-
const ISSUE = {
|
|
373
|
-
title: 'Existing Issue',
|
|
374
|
-
description: 'New comment',
|
|
375
|
-
label: 'location',
|
|
376
|
-
};
|
|
377
|
-
|
|
378
308
|
context('when issue is closed', () => {
|
|
379
|
-
let
|
|
309
|
+
let updateIssueScope;
|
|
380
310
|
let addCommentScope;
|
|
381
|
-
let openIssueScope;
|
|
382
|
-
|
|
383
|
-
const GITHUB_RESPONSE_FOR_EXISTING_ISSUE = {
|
|
384
|
-
number: 123,
|
|
385
|
-
title: ISSUE.title,
|
|
386
|
-
description: ISSUE.description,
|
|
387
|
-
labels: [{ name: 'selectors' }],
|
|
388
|
-
state: GitHub.ISSUE_STATE_CLOSED,
|
|
389
|
-
};
|
|
390
311
|
|
|
391
312
|
before(async () => {
|
|
392
|
-
nock('https://api.github.com')
|
|
393
|
-
.
|
|
394
|
-
.query(true)
|
|
395
|
-
.reply(200, [GITHUB_RESPONSE_FOR_EXISTING_ISSUE]);
|
|
396
|
-
|
|
397
|
-
openIssueScope = nock('https://api.github.com')
|
|
398
|
-
.patch(`/repos/owner/repo/issues/${GITHUB_RESPONSE_FOR_EXISTING_ISSUE.number}`, { state: GitHub.ISSUE_STATE_OPEN })
|
|
399
|
-
.reply(200);
|
|
400
|
-
|
|
401
|
-
setIssueLabelsScope = nock('https://api.github.com')
|
|
402
|
-
.put(`/repos/owner/repo/issues/${GITHUB_RESPONSE_FOR_EXISTING_ISSUE.number}/labels`, { labels: ['location'] })
|
|
313
|
+
updateIssueScope = nock('https://api.github.com')
|
|
314
|
+
.patch(`/repos/owner/repo/issues/${EXISTING_CLOSED_ISSUE.number}`, { state: GitHub.ISSUE_STATE_OPEN, labels: ['location'] })
|
|
403
315
|
.reply(200);
|
|
404
316
|
|
|
405
317
|
addCommentScope = nock('https://api.github.com')
|
|
406
|
-
.post(`/repos/owner/repo/issues/${
|
|
318
|
+
.post(`/repos/owner/repo/issues/${EXISTING_CLOSED_ISSUE.number}/comments`, { body: EXISTING_CLOSED_ISSUE.description })
|
|
407
319
|
.reply(200);
|
|
408
320
|
|
|
409
|
-
await github.createOrUpdateIssue(
|
|
321
|
+
await github.createOrUpdateIssue({ title: EXISTING_CLOSED_ISSUE.title, description: EXISTING_CLOSED_ISSUE.description, label: 'location' });
|
|
410
322
|
});
|
|
411
323
|
|
|
412
|
-
|
|
413
|
-
|
|
324
|
+
after(() => {
|
|
325
|
+
github.issuesCache.delete(EXISTING_CLOSED_ISSUE.title);
|
|
326
|
+
github.issuesCache.set(EXISTING_CLOSED_ISSUE.title, EXISTING_CLOSED_ISSUE);
|
|
414
327
|
});
|
|
415
328
|
|
|
416
|
-
it(
|
|
417
|
-
expect(
|
|
329
|
+
it('reopens the issue and updates its labels', () => {
|
|
330
|
+
expect(updateIssueScope.isDone()).to.be.true;
|
|
418
331
|
});
|
|
419
332
|
|
|
420
333
|
it('adds comment to the issue', () => {
|
|
@@ -423,49 +336,63 @@ describe('GitHub', function () {
|
|
|
423
336
|
});
|
|
424
337
|
|
|
425
338
|
context('when issue is already opened', () => {
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
.reply(200);
|
|
455
|
-
|
|
456
|
-
await github.createOrUpdateIssue(ISSUE);
|
|
339
|
+
context('when the reason is new', () => {
|
|
340
|
+
let addCommentScope;
|
|
341
|
+
let updateIssueScope;
|
|
342
|
+
|
|
343
|
+
before(async () => {
|
|
344
|
+
updateIssueScope = nock('https://api.github.com')
|
|
345
|
+
.patch(`/repos/owner/repo/issues/${EXISTING_OPEN_ISSUE.number}`, { state: GitHub.ISSUE_STATE_OPEN, labels: ['404'] })
|
|
346
|
+
.reply(200);
|
|
347
|
+
|
|
348
|
+
addCommentScope = nock('https://api.github.com')
|
|
349
|
+
.post(`/repos/owner/repo/issues/${EXISTING_OPEN_ISSUE.number}/comments`, { body: EXISTING_OPEN_ISSUE.description })
|
|
350
|
+
.reply(200);
|
|
351
|
+
|
|
352
|
+
await github.createOrUpdateIssue({ title: EXISTING_OPEN_ISSUE.title, description: EXISTING_OPEN_ISSUE.description, label: '404' });
|
|
353
|
+
});
|
|
354
|
+
|
|
355
|
+
after(() => {
|
|
356
|
+
github.issuesCache.delete(EXISTING_OPEN_ISSUE.title);
|
|
357
|
+
github.issuesCache.set(EXISTING_OPEN_ISSUE.title, EXISTING_OPEN_ISSUE);
|
|
358
|
+
});
|
|
359
|
+
|
|
360
|
+
it("updates the issue's labels", () => {
|
|
361
|
+
expect(updateIssueScope.isDone()).to.be.true;
|
|
362
|
+
});
|
|
363
|
+
|
|
364
|
+
it('adds a comment to the issue', () => {
|
|
365
|
+
expect(addCommentScope.isDone()).to.be.true;
|
|
366
|
+
});
|
|
457
367
|
});
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
368
|
+
context('when the reason did not change', () => {
|
|
369
|
+
let addCommentScope;
|
|
370
|
+
let updateIssueScope;
|
|
371
|
+
|
|
372
|
+
before(async () => {
|
|
373
|
+
updateIssueScope = nock('https://api.github.com')
|
|
374
|
+
.patch(`/repos/owner/repo/issues/${EXISTING_OPEN_ISSUE.number}`, { state: GitHub.ISSUE_STATE_OPEN, labels: EXISTING_OPEN_ISSUE.labels })
|
|
375
|
+
.reply(200);
|
|
376
|
+
|
|
377
|
+
addCommentScope = nock('https://api.github.com')
|
|
378
|
+
.post(`/repos/owner/repo/issues/${EXISTING_OPEN_ISSUE.number}/comments`, { body: EXISTING_OPEN_ISSUE.description })
|
|
379
|
+
.reply(200);
|
|
380
|
+
|
|
381
|
+
await github.createOrUpdateIssue({ title: EXISTING_OPEN_ISSUE.title, description: EXISTING_OPEN_ISSUE.description, label: EXISTING_OPEN_ISSUE.labels[0].name });
|
|
382
|
+
});
|
|
383
|
+
|
|
384
|
+
after(() => {
|
|
385
|
+
github.issuesCache.delete(EXISTING_OPEN_ISSUE.title);
|
|
386
|
+
github.issuesCache.set(EXISTING_OPEN_ISSUE.title, EXISTING_OPEN_ISSUE);
|
|
387
|
+
});
|
|
388
|
+
|
|
389
|
+
it('does not attempt to updates the issue’s labels', () => {
|
|
390
|
+
expect(updateIssueScope.isDone()).to.be.false;
|
|
391
|
+
});
|
|
392
|
+
|
|
393
|
+
it('does not attempt to add any comment to the issue', () => {
|
|
394
|
+
expect(addCommentScope.isDone()).to.be.false;
|
|
395
|
+
});
|
|
469
396
|
});
|
|
470
397
|
});
|
|
471
398
|
});
|
package/src/reporter/index.js
CHANGED
|
@@ -49,6 +49,10 @@ export default class Reporter {
|
|
|
49
49
|
return this.github.initialize();
|
|
50
50
|
}
|
|
51
51
|
|
|
52
|
+
onTrackingStarted() {
|
|
53
|
+
return this.github.clearCache();
|
|
54
|
+
}
|
|
55
|
+
|
|
52
56
|
async onVersionRecorded(version) {
|
|
53
57
|
await this.github.closeIssueWithCommentIfExists({
|
|
54
58
|
title: Reporter.generateTitleID(version.serviceId, version.termsType),
|