npm-cli-gh-issue-preparator 1.0.3 → 1.0.4
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/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
## [1.0.4](https://github.com/HiromiShikata/npm-cli-gh-issue-preparator/compare/v1.0.3...v1.0.4) (2026-01-10)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Bug Fixes
|
|
5
|
+
|
|
6
|
+
* **ci:** add GH_TOKEN to test workflow and fix test expectations ([86dbac0](https://github.com/HiromiShikata/npm-cli-gh-issue-preparator/commit/86dbac0bd66e0806531a26338b225ddec22c7826))
|
|
7
|
+
|
|
1
8
|
## [1.0.3](https://github.com/HiromiShikata/npm-cli-gh-issue-preparator/compare/v1.0.2...v1.0.3) (2025-12-14)
|
|
2
9
|
|
|
3
10
|
|
package/package.json
CHANGED
package/renovate.json
CHANGED
|
@@ -11,12 +11,6 @@
|
|
|
11
11
|
"branchConcurrentLimit": 2,
|
|
12
12
|
"ignorePaths": ["**/generated/*", "**/_gen/*"],
|
|
13
13
|
"packageRules": [
|
|
14
|
-
{
|
|
15
|
-
"matchPackagePatterns": ["*"],
|
|
16
|
-
"matchUpdateTypes": ["minor", "patch"],
|
|
17
|
-
"groupName": "all non-major dependencies",
|
|
18
|
-
"groupSlug": "all-minor-patch"
|
|
19
|
-
},
|
|
20
14
|
{
|
|
21
15
|
"matchPackageNames": ["eslint"],
|
|
22
16
|
"allowedVersions": "<9.0.0"
|
|
@@ -32,6 +26,10 @@
|
|
|
32
26
|
{
|
|
33
27
|
"matchPackageNames": ["eslint-plugin-unused-imports"],
|
|
34
28
|
"allowedVersions": "<4.0.0"
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
"matchPackageNames": ["@google/clasp"],
|
|
32
|
+
"allowedVersions": "3.1.0"
|
|
35
33
|
}
|
|
36
34
|
]
|
|
37
35
|
}
|
|
@@ -270,6 +270,123 @@ describe('GitHubIssueRepository', () => {
|
|
|
270
270
|
expect(issues).toEqual([]);
|
|
271
271
|
});
|
|
272
272
|
|
|
273
|
+
it('should skip items with content but undefined url', async () => {
|
|
274
|
+
mockFetch.mockResolvedValueOnce({
|
|
275
|
+
ok: true,
|
|
276
|
+
json: jest.fn().mockResolvedValue({
|
|
277
|
+
data: {
|
|
278
|
+
organization: {
|
|
279
|
+
projectV2: {
|
|
280
|
+
items: {
|
|
281
|
+
totalCount: 2,
|
|
282
|
+
pageInfo: {
|
|
283
|
+
endCursor: null,
|
|
284
|
+
hasNextPage: false,
|
|
285
|
+
},
|
|
286
|
+
nodes: [
|
|
287
|
+
{
|
|
288
|
+
id: 'item-with-undefined-url',
|
|
289
|
+
content: {
|
|
290
|
+
url: undefined,
|
|
291
|
+
title: 'Item Without URL',
|
|
292
|
+
number: 1,
|
|
293
|
+
labels: {
|
|
294
|
+
nodes: [],
|
|
295
|
+
},
|
|
296
|
+
},
|
|
297
|
+
fieldValues: {
|
|
298
|
+
nodes: [
|
|
299
|
+
{
|
|
300
|
+
name: 'Done',
|
|
301
|
+
field: {
|
|
302
|
+
name: 'Status',
|
|
303
|
+
},
|
|
304
|
+
},
|
|
305
|
+
],
|
|
306
|
+
},
|
|
307
|
+
},
|
|
308
|
+
{
|
|
309
|
+
id: 'valid-issue',
|
|
310
|
+
content: {
|
|
311
|
+
url: 'https://github.com/owner/repo/issues/5',
|
|
312
|
+
title: 'Valid Issue',
|
|
313
|
+
number: 5,
|
|
314
|
+
labels: {
|
|
315
|
+
nodes: [],
|
|
316
|
+
},
|
|
317
|
+
},
|
|
318
|
+
fieldValues: {
|
|
319
|
+
nodes: [
|
|
320
|
+
{
|
|
321
|
+
name: 'Done',
|
|
322
|
+
field: {
|
|
323
|
+
name: 'Status',
|
|
324
|
+
},
|
|
325
|
+
},
|
|
326
|
+
],
|
|
327
|
+
},
|
|
328
|
+
},
|
|
329
|
+
],
|
|
330
|
+
},
|
|
331
|
+
},
|
|
332
|
+
},
|
|
333
|
+
},
|
|
334
|
+
}),
|
|
335
|
+
});
|
|
336
|
+
|
|
337
|
+
const issues = await repository.getAllOpened(mockProject);
|
|
338
|
+
|
|
339
|
+
expect(issues).toHaveLength(1);
|
|
340
|
+
expect(issues[0].id).toBe('valid-issue');
|
|
341
|
+
});
|
|
342
|
+
|
|
343
|
+
it('should handle items with null labels nodes', async () => {
|
|
344
|
+
mockFetch.mockResolvedValueOnce({
|
|
345
|
+
ok: true,
|
|
346
|
+
json: jest.fn().mockResolvedValue({
|
|
347
|
+
data: {
|
|
348
|
+
organization: {
|
|
349
|
+
projectV2: {
|
|
350
|
+
items: {
|
|
351
|
+
totalCount: 1,
|
|
352
|
+
pageInfo: {
|
|
353
|
+
endCursor: null,
|
|
354
|
+
hasNextPage: false,
|
|
355
|
+
},
|
|
356
|
+
nodes: [
|
|
357
|
+
{
|
|
358
|
+
id: 'issue-no-labels',
|
|
359
|
+
content: {
|
|
360
|
+
url: 'https://github.com/owner/repo/issues/10',
|
|
361
|
+
title: 'Issue Without Labels',
|
|
362
|
+
number: 10,
|
|
363
|
+
labels: null,
|
|
364
|
+
},
|
|
365
|
+
fieldValues: {
|
|
366
|
+
nodes: [
|
|
367
|
+
{
|
|
368
|
+
name: 'Done',
|
|
369
|
+
field: {
|
|
370
|
+
name: 'Status',
|
|
371
|
+
},
|
|
372
|
+
},
|
|
373
|
+
],
|
|
374
|
+
},
|
|
375
|
+
},
|
|
376
|
+
],
|
|
377
|
+
},
|
|
378
|
+
},
|
|
379
|
+
},
|
|
380
|
+
},
|
|
381
|
+
}),
|
|
382
|
+
});
|
|
383
|
+
|
|
384
|
+
const issues = await repository.getAllOpened(mockProject);
|
|
385
|
+
|
|
386
|
+
expect(issues).toHaveLength(1);
|
|
387
|
+
expect(issues[0].labels).toEqual([]);
|
|
388
|
+
});
|
|
389
|
+
|
|
273
390
|
it('should handle issue without Status field', async () => {
|
|
274
391
|
mockFetch.mockResolvedValueOnce({
|
|
275
392
|
ok: true,
|
|
@@ -343,6 +460,10 @@ describe('GitHubIssueRepository', () => {
|
|
|
343
460
|
organization: {
|
|
344
461
|
projectV2: {
|
|
345
462
|
fields: {
|
|
463
|
+
pageInfo: {
|
|
464
|
+
hasNextPage: false,
|
|
465
|
+
endCursor: null,
|
|
466
|
+
},
|
|
346
467
|
nodes: [
|
|
347
468
|
{
|
|
348
469
|
id: 'field-1',
|
|
@@ -400,6 +521,10 @@ describe('GitHubIssueRepository', () => {
|
|
|
400
521
|
organization: {
|
|
401
522
|
projectV2: {
|
|
402
523
|
fields: {
|
|
524
|
+
pageInfo: {
|
|
525
|
+
hasNextPage: false,
|
|
526
|
+
endCursor: null,
|
|
527
|
+
},
|
|
403
528
|
nodes: [
|
|
404
529
|
{
|
|
405
530
|
id: 'field-1',
|
|
@@ -453,6 +578,10 @@ describe('GitHubIssueRepository', () => {
|
|
|
453
578
|
organization: {
|
|
454
579
|
projectV2: {
|
|
455
580
|
fields: {
|
|
581
|
+
pageInfo: {
|
|
582
|
+
hasNextPage: false,
|
|
583
|
+
endCursor: null,
|
|
584
|
+
},
|
|
456
585
|
nodes: [
|
|
457
586
|
{
|
|
458
587
|
id: 'field-1',
|
|
@@ -525,6 +654,10 @@ describe('GitHubIssueRepository', () => {
|
|
|
525
654
|
organization: {
|
|
526
655
|
projectV2: {
|
|
527
656
|
fields: {
|
|
657
|
+
pageInfo: {
|
|
658
|
+
hasNextPage: false,
|
|
659
|
+
endCursor: null,
|
|
660
|
+
},
|
|
528
661
|
nodes: [
|
|
529
662
|
{
|
|
530
663
|
id: 'field-1',
|
|
@@ -560,6 +693,10 @@ describe('GitHubIssueRepository', () => {
|
|
|
560
693
|
organization: {
|
|
561
694
|
projectV2: {
|
|
562
695
|
fields: {
|
|
696
|
+
pageInfo: {
|
|
697
|
+
hasNextPage: false,
|
|
698
|
+
endCursor: null,
|
|
699
|
+
},
|
|
563
700
|
nodes: [
|
|
564
701
|
{
|
|
565
702
|
id: 'field-1',
|
|
@@ -600,6 +737,10 @@ describe('GitHubIssueRepository', () => {
|
|
|
600
737
|
organization: {
|
|
601
738
|
projectV2: {
|
|
602
739
|
fields: {
|
|
740
|
+
pageInfo: {
|
|
741
|
+
hasNextPage: false,
|
|
742
|
+
endCursor: null,
|
|
743
|
+
},
|
|
603
744
|
nodes: [
|
|
604
745
|
{
|
|
605
746
|
id: 'field-1',
|
|
@@ -642,6 +783,10 @@ describe('GitHubIssueRepository', () => {
|
|
|
642
783
|
organization: {
|
|
643
784
|
projectV2: {
|
|
644
785
|
fields: {
|
|
786
|
+
pageInfo: {
|
|
787
|
+
hasNextPage: false,
|
|
788
|
+
endCursor: null,
|
|
789
|
+
},
|
|
645
790
|
nodes: [
|
|
646
791
|
{
|
|
647
792
|
id: 'field-1',
|
|
@@ -682,6 +827,10 @@ describe('GitHubIssueRepository', () => {
|
|
|
682
827
|
user: {
|
|
683
828
|
projectV2: {
|
|
684
829
|
fields: {
|
|
830
|
+
pageInfo: {
|
|
831
|
+
hasNextPage: false,
|
|
832
|
+
endCursor: null,
|
|
833
|
+
},
|
|
685
834
|
nodes: [
|
|
686
835
|
{
|
|
687
836
|
id: 'field-1',
|
|
@@ -1032,5 +1181,60 @@ describe('GitHubIssueRepository', () => {
|
|
|
1032
1181
|
|
|
1033
1182
|
expect(result).toBeNull();
|
|
1034
1183
|
});
|
|
1184
|
+
|
|
1185
|
+
it('should handle issue with null labels in get method', async () => {
|
|
1186
|
+
mockFetch.mockResolvedValueOnce({
|
|
1187
|
+
ok: true,
|
|
1188
|
+
json: jest.fn().mockResolvedValue({
|
|
1189
|
+
data: {
|
|
1190
|
+
organization: {
|
|
1191
|
+
projectV2: {
|
|
1192
|
+
items: {
|
|
1193
|
+
totalCount: 1,
|
|
1194
|
+
pageInfo: {
|
|
1195
|
+
endCursor: null,
|
|
1196
|
+
hasNextPage: false,
|
|
1197
|
+
},
|
|
1198
|
+
nodes: [
|
|
1199
|
+
{
|
|
1200
|
+
id: 'issue-null-labels',
|
|
1201
|
+
content: {
|
|
1202
|
+
url: 'https://github.com/owner/repo/issues/1',
|
|
1203
|
+
title: 'Issue With Null Labels',
|
|
1204
|
+
number: 1,
|
|
1205
|
+
labels: null,
|
|
1206
|
+
},
|
|
1207
|
+
fieldValues: {
|
|
1208
|
+
nodes: [
|
|
1209
|
+
{
|
|
1210
|
+
name: 'Done',
|
|
1211
|
+
field: {
|
|
1212
|
+
name: 'Status',
|
|
1213
|
+
},
|
|
1214
|
+
},
|
|
1215
|
+
],
|
|
1216
|
+
},
|
|
1217
|
+
},
|
|
1218
|
+
],
|
|
1219
|
+
},
|
|
1220
|
+
},
|
|
1221
|
+
},
|
|
1222
|
+
},
|
|
1223
|
+
}),
|
|
1224
|
+
});
|
|
1225
|
+
|
|
1226
|
+
const result = await repository.get(
|
|
1227
|
+
'https://github.com/owner/repo/issues/1',
|
|
1228
|
+
mockProject,
|
|
1229
|
+
);
|
|
1230
|
+
|
|
1231
|
+
expect(result).toEqual({
|
|
1232
|
+
id: 'issue-null-labels',
|
|
1233
|
+
url: 'https://github.com/owner/repo/issues/1',
|
|
1234
|
+
title: 'Issue With Null Labels',
|
|
1235
|
+
labels: [],
|
|
1236
|
+
status: 'Done',
|
|
1237
|
+
});
|
|
1238
|
+
});
|
|
1035
1239
|
});
|
|
1036
1240
|
});
|
|
@@ -67,7 +67,7 @@ describe('StartPreparationUseCase', () => {
|
|
|
67
67
|
expect(mockIssueRepository.update.mock.calls[0][1]).toBe(mockProject);
|
|
68
68
|
expect(mockLocalCommandRunner.runCommand.mock.calls).toHaveLength(1);
|
|
69
69
|
expect(mockLocalCommandRunner.runCommand.mock.calls[0][0]).toBe(
|
|
70
|
-
'aw https://github.com/user/repo
|
|
70
|
+
'aw url1 impl https://github.com/user/repo',
|
|
71
71
|
);
|
|
72
72
|
});
|
|
73
73
|
it('should assign workspace to awaiting issues', async () => {
|
|
@@ -100,18 +100,13 @@ describe('StartPreparationUseCase', () => {
|
|
|
100
100
|
preparationStatus: 'Preparation',
|
|
101
101
|
defaultAgentName: 'agent1',
|
|
102
102
|
});
|
|
103
|
-
expect(mockIssueRepository.update.mock.calls).toHaveLength(
|
|
103
|
+
expect(mockIssueRepository.update.mock.calls).toHaveLength(1);
|
|
104
104
|
expect(mockIssueRepository.update.mock.calls[0][0]).toMatchObject({
|
|
105
|
-
id: '1',
|
|
106
|
-
status: 'Preparation',
|
|
107
|
-
});
|
|
108
|
-
expect(mockIssueRepository.update.mock.calls[0][1]).toBe(mockProject);
|
|
109
|
-
expect(mockIssueRepository.update.mock.calls[1][0]).toMatchObject({
|
|
110
105
|
id: '2',
|
|
111
106
|
status: 'Preparation',
|
|
112
107
|
});
|
|
113
|
-
expect(mockIssueRepository.update.mock.calls[
|
|
114
|
-
expect(mockLocalCommandRunner.runCommand.mock.calls).toHaveLength(
|
|
108
|
+
expect(mockIssueRepository.update.mock.calls[0][1]).toBe(mockProject);
|
|
109
|
+
expect(mockLocalCommandRunner.runCommand.mock.calls).toHaveLength(1);
|
|
115
110
|
});
|
|
116
111
|
it('should not assign workspace if maximum preparing issues reached', async () => {
|
|
117
112
|
const preparationIssues: Issue[] = Array.from({ length: 6 }, (_, i) => ({
|
|
@@ -147,4 +142,45 @@ describe('StartPreparationUseCase', () => {
|
|
|
147
142
|
expect(issue7UpdateCalls).toHaveLength(0);
|
|
148
143
|
expect(mockLocalCommandRunner.runCommand.mock.calls).toHaveLength(0);
|
|
149
144
|
});
|
|
145
|
+
it('should handle defensive break when pop returns undefined', async () => {
|
|
146
|
+
const awaitingIssue: Issue = {
|
|
147
|
+
id: '1',
|
|
148
|
+
url: 'url1',
|
|
149
|
+
title: 'Issue 1',
|
|
150
|
+
labels: [],
|
|
151
|
+
status: 'Awaiting Workspace',
|
|
152
|
+
};
|
|
153
|
+
let popCallCount = 0;
|
|
154
|
+
const issuesWithMockedPop: Issue[] = [awaitingIssue, awaitingIssue];
|
|
155
|
+
const mockedPop = jest.fn((): Issue | undefined => {
|
|
156
|
+
popCallCount++;
|
|
157
|
+
if (popCallCount === 1) {
|
|
158
|
+
return awaitingIssue;
|
|
159
|
+
}
|
|
160
|
+
return undefined;
|
|
161
|
+
});
|
|
162
|
+
Object.defineProperty(issuesWithMockedPop, 'pop', { value: mockedPop });
|
|
163
|
+
Object.defineProperty(issuesWithMockedPop, 'filter', {
|
|
164
|
+
value: () => issuesWithMockedPop,
|
|
165
|
+
});
|
|
166
|
+
const allIssues: Issue[] = [];
|
|
167
|
+
Object.defineProperty(allIssues, 'filter', {
|
|
168
|
+
value: jest.fn(() => issuesWithMockedPop),
|
|
169
|
+
});
|
|
170
|
+
mockProjectRepository.getByUrl.mockResolvedValue(mockProject);
|
|
171
|
+
mockIssueRepository.getAllOpened.mockResolvedValueOnce(allIssues);
|
|
172
|
+
mockLocalCommandRunner.runCommand.mockResolvedValue({
|
|
173
|
+
stdout: '',
|
|
174
|
+
stderr: '',
|
|
175
|
+
exitCode: 0,
|
|
176
|
+
});
|
|
177
|
+
await useCase.run({
|
|
178
|
+
projectUrl: 'https://github.com/user/repo',
|
|
179
|
+
awaitingWorkspaceStatus: 'Awaiting Workspace',
|
|
180
|
+
preparationStatus: 'Preparation',
|
|
181
|
+
defaultAgentName: 'agent1',
|
|
182
|
+
});
|
|
183
|
+
expect(mockIssueRepository.update.mock.calls).toHaveLength(1);
|
|
184
|
+
expect(mockLocalCommandRunner.runCommand.mock.calls).toHaveLength(1);
|
|
185
|
+
});
|
|
150
186
|
});
|