github-issue-tower-defence-management 1.144.0 → 1.144.2
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 +14 -0
- package/bin/adapter/entry-points/console/ui-dist/assets/index-BYoG8oQb.js +82 -0
- package/bin/adapter/entry-points/console/ui-dist/index.html +1 -1
- package/bin/domain/usecases/console/GenerateConsoleListsUseCase.js +28 -4
- package/bin/domain/usecases/console/GenerateConsoleListsUseCase.js.map +1 -1
- package/package.json +1 -1
- package/src/adapter/entry-points/console/ui/e2e/consoleTestHarness.ts +3 -0
- package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsoleDetailPrefetch.test.ts +1 -0
- package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsoleItemDetailData.test.ts +2 -0
- package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsoleTabData.test.ts +39 -0
- package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsoleTabData.ts +14 -1
- package/src/adapter/entry-points/console/ui/src/features/console/logic/grouping.test.ts +1 -0
- package/src/adapter/entry-points/console/ui/src/features/console/logic/overlay.test.ts +1 -0
- package/src/adapter/entry-points/console/ui/src/features/console/logic/types.ts +1 -0
- package/src/adapter/entry-points/console/ui/src/features/console/pages/ConsoleItemDetailContainer.test.tsx +42 -1
- package/src/adapter/entry-points/console/ui/src/features/console/pages/ConsoleItemDetailContainer.tsx +7 -2
- package/src/adapter/entry-points/console/ui/src/features/console/pages/ConsolePage.test.tsx +7 -0
- package/src/adapter/entry-points/console/ui/src/features/console/testing/fixtures.ts +5 -0
- package/src/adapter/entry-points/console/ui-dist/assets/index-BYoG8oQb.js +82 -0
- package/src/adapter/entry-points/console/ui-dist/index.html +1 -1
- package/src/domain/usecases/console/GenerateConsoleListsUseCase.test.ts +92 -0
- package/src/domain/usecases/console/GenerateConsoleListsUseCase.ts +52 -4
- package/types/domain/usecases/console/GenerateConsoleListsUseCase.d.ts +3 -0
- package/types/domain/usecases/console/GenerateConsoleListsUseCase.d.ts.map +1 -1
- package/bin/adapter/entry-points/console/ui-dist/assets/index-y6LyqXlu.js +0 -82
- package/src/adapter/entry-points/console/ui-dist/assets/index-y6LyqXlu.js +0 -82
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
<meta charset="UTF-8" />
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
6
|
<title>TDPM Console</title>
|
|
7
|
-
<script type="module" crossorigin src="/assets/index-
|
|
7
|
+
<script type="module" crossorigin src="/assets/index-BYoG8oQb.js"></script>
|
|
8
8
|
<link rel="stylesheet" crossorigin href="/assets/index-CKX2ny-z.css">
|
|
9
9
|
</head>
|
|
10
10
|
<body>
|
|
@@ -152,6 +152,36 @@ describe('GenerateConsoleListsUseCase', () => {
|
|
|
152
152
|
const result = run([makeIssue({ status: 'Unread' })]);
|
|
153
153
|
expect(result.unread.items).toHaveLength(1);
|
|
154
154
|
});
|
|
155
|
+
|
|
156
|
+
it('accepts an unread issue that has no assignee', () => {
|
|
157
|
+
const result = run([makeIssue({ status: 'Unread', assignees: [] })]);
|
|
158
|
+
expect(result.unread.items).toHaveLength(1);
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
it('accepts an unread pull request that has no assignee', () => {
|
|
162
|
+
const result = run([
|
|
163
|
+
makeIssue({ status: 'Unread', assignees: [], isPr: true }),
|
|
164
|
+
]);
|
|
165
|
+
expect(result.unread.items).toHaveLength(1);
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
it('accepts a todo-by-human issue that has no assignee', () => {
|
|
169
|
+
const result = run([
|
|
170
|
+
makeIssue({ status: 'Todo by human', assignees: [] }),
|
|
171
|
+
]);
|
|
172
|
+
expect(result['todo-by-human'].items).toHaveLength(1);
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
it('keeps rejecting an unassigned issue that is blocked by a depended issue url', () => {
|
|
176
|
+
const result = run([
|
|
177
|
+
makeIssue({
|
|
178
|
+
status: 'Unread',
|
|
179
|
+
assignees: [],
|
|
180
|
+
dependedIssueUrls: ['https://github.com/demo/repo/issues/99'],
|
|
181
|
+
}),
|
|
182
|
+
]);
|
|
183
|
+
expect(result.unread.items).toHaveLength(0);
|
|
184
|
+
});
|
|
155
185
|
});
|
|
156
186
|
|
|
157
187
|
describe('per-tab selectors', () => {
|
|
@@ -378,6 +408,7 @@ describe('GenerateConsoleListsUseCase', () => {
|
|
|
378
408
|
'nextActionHour',
|
|
379
409
|
'number',
|
|
380
410
|
'projectItemId',
|
|
411
|
+
'relatedOpenPullRequestUrls',
|
|
381
412
|
'repo',
|
|
382
413
|
'status',
|
|
383
414
|
'story',
|
|
@@ -582,6 +613,67 @@ describe('GenerateConsoleListsUseCase', () => {
|
|
|
582
613
|
});
|
|
583
614
|
});
|
|
584
615
|
|
|
616
|
+
describe('related open pull requests', () => {
|
|
617
|
+
const awaitingQualityCheckIssue = (): Issue =>
|
|
618
|
+
makeIssue({ status: 'Awaiting Quality Check' });
|
|
619
|
+
|
|
620
|
+
it('projects the open pull request that closes the issue', () => {
|
|
621
|
+
const issue = awaitingQualityCheckIssue();
|
|
622
|
+
const pullRequest = makeIssue({
|
|
623
|
+
status: 'Awaiting Quality Check',
|
|
624
|
+
isPr: true,
|
|
625
|
+
url: 'https://github.com/demo/repo/pull/501',
|
|
626
|
+
closingIssueReferenceUrls: [issue.url],
|
|
627
|
+
});
|
|
628
|
+
const result = run([issue, pullRequest]);
|
|
629
|
+
const projected = result.prs.items.find((item) => item.url === issue.url);
|
|
630
|
+
expect(projected?.relatedOpenPullRequestUrls).toEqual([
|
|
631
|
+
'https://github.com/demo/repo/pull/501',
|
|
632
|
+
]);
|
|
633
|
+
});
|
|
634
|
+
|
|
635
|
+
it('ignores a closed pull request that closes the issue', () => {
|
|
636
|
+
const issue = awaitingQualityCheckIssue();
|
|
637
|
+
const pullRequest = makeIssue({
|
|
638
|
+
isPr: true,
|
|
639
|
+
isClosed: true,
|
|
640
|
+
url: 'https://github.com/demo/repo/pull/502',
|
|
641
|
+
closingIssueReferenceUrls: [issue.url],
|
|
642
|
+
});
|
|
643
|
+
const result = run([issue, pullRequest]);
|
|
644
|
+
const projected = result.prs.items.find((item) => item.url === issue.url);
|
|
645
|
+
expect(projected?.relatedOpenPullRequestUrls).toEqual([]);
|
|
646
|
+
});
|
|
647
|
+
|
|
648
|
+
it('collects the linkage from a pull request that no tab shows', () => {
|
|
649
|
+
const issue = awaitingQualityCheckIssue();
|
|
650
|
+
const pullRequest = makeIssue({
|
|
651
|
+
status: 'In Tmux by agent',
|
|
652
|
+
assignees: ['another-person'],
|
|
653
|
+
isPr: true,
|
|
654
|
+
url: 'https://github.com/demo/repo/pull/503',
|
|
655
|
+
closingIssueReferenceUrls: [issue.url],
|
|
656
|
+
});
|
|
657
|
+
const result = run([issue, pullRequest]);
|
|
658
|
+
const projected = result.prs.items.find((item) => item.url === issue.url);
|
|
659
|
+
expect(projected?.relatedOpenPullRequestUrls).toEqual([
|
|
660
|
+
'https://github.com/demo/repo/pull/503',
|
|
661
|
+
]);
|
|
662
|
+
});
|
|
663
|
+
|
|
664
|
+
it('leaves the list empty when no open pull request closes the issue', () => {
|
|
665
|
+
const issue = awaitingQualityCheckIssue();
|
|
666
|
+
const unrelatedPullRequest = makeIssue({
|
|
667
|
+
isPr: true,
|
|
668
|
+
url: 'https://github.com/demo/repo/pull/504',
|
|
669
|
+
closingIssueReferenceUrls: ['https://github.com/demo/repo/issues/999'],
|
|
670
|
+
});
|
|
671
|
+
const result = run([issue, unrelatedPullRequest]);
|
|
672
|
+
const projected = result.prs.items.find((item) => item.url === issue.url);
|
|
673
|
+
expect(projected?.relatedOpenPullRequestUrls).toEqual([]);
|
|
674
|
+
});
|
|
675
|
+
});
|
|
676
|
+
|
|
585
677
|
describe('project without a story field', () => {
|
|
586
678
|
const projectNoStory = baseProject(null);
|
|
587
679
|
|
|
@@ -25,6 +25,7 @@ export type ConsoleListItem = {
|
|
|
25
25
|
dependedIssueUrls: string[];
|
|
26
26
|
labels: string[];
|
|
27
27
|
createdAt: string;
|
|
28
|
+
relatedOpenPullRequestUrls: string[];
|
|
28
29
|
};
|
|
29
30
|
|
|
30
31
|
export type ConsoleFieldOption = {
|
|
@@ -96,6 +97,9 @@ export class GenerateConsoleListsUseCase {
|
|
|
96
97
|
const storyOrder = storyOptions.map((option) => option.name);
|
|
97
98
|
const statusOptions = project.status.statuses;
|
|
98
99
|
|
|
100
|
+
const relatedOpenPullRequestUrlsByIssueUrl =
|
|
101
|
+
this.buildRelatedOpenPullRequestUrlsByIssueUrl(issues);
|
|
102
|
+
|
|
99
103
|
const visibleIssues = issues.filter(
|
|
100
104
|
(issue) =>
|
|
101
105
|
issue.status?.toLowerCase() !==
|
|
@@ -117,7 +121,14 @@ export class GenerateConsoleListsUseCase {
|
|
|
117
121
|
storyOrder,
|
|
118
122
|
storyColors: this.buildStoryColorsObject(storyOptions),
|
|
119
123
|
items: this.sortByStoryOrder(
|
|
120
|
-
sourceIssues
|
|
124
|
+
sourceIssues
|
|
125
|
+
.filter(selector)
|
|
126
|
+
.map((issue) =>
|
|
127
|
+
this.projectItem(
|
|
128
|
+
issue,
|
|
129
|
+
relatedOpenPullRequestUrlsByIssueUrl.get(issue.url) ?? [],
|
|
130
|
+
),
|
|
131
|
+
),
|
|
121
132
|
storyOrder,
|
|
122
133
|
),
|
|
123
134
|
});
|
|
@@ -183,7 +194,12 @@ export class GenerateConsoleListsUseCase {
|
|
|
183
194
|
issue.status?.toLowerCase() !==
|
|
184
195
|
IN_TMUX_BY_AGENT_STATUS_NAME.toLowerCase(),
|
|
185
196
|
)
|
|
186
|
-
.map((issue) =>
|
|
197
|
+
.map((issue) =>
|
|
198
|
+
this.projectItem(
|
|
199
|
+
issue,
|
|
200
|
+
relatedOpenPullRequestUrlsByIssueUrl.get(issue.url) ?? [],
|
|
201
|
+
),
|
|
202
|
+
),
|
|
187
203
|
storyOrder,
|
|
188
204
|
),
|
|
189
205
|
},
|
|
@@ -192,11 +208,17 @@ export class GenerateConsoleListsUseCase {
|
|
|
192
208
|
|
|
193
209
|
private isActionable = (issue: Issue, assigneeLogin: string): boolean =>
|
|
194
210
|
issue.isClosed === false &&
|
|
195
|
-
|
|
211
|
+
this.isOwnedByNobodyElse(issue, assigneeLogin) &&
|
|
196
212
|
issue.dependedIssueUrls.length === 0 &&
|
|
197
213
|
issue.nextActionDate === null &&
|
|
198
214
|
issue.nextActionHour === null;
|
|
199
215
|
|
|
216
|
+
private isOwnedByNobodyElse = (
|
|
217
|
+
issue: Issue,
|
|
218
|
+
assigneeLogin: string,
|
|
219
|
+
): boolean =>
|
|
220
|
+
issue.assignees.length === 0 || issue.assignees.includes(assigneeLogin);
|
|
221
|
+
|
|
200
222
|
private workflowBlockerSelector = (
|
|
201
223
|
workflowBlockerStoryName: string | null,
|
|
202
224
|
): ((issue: Issue) => boolean) => {
|
|
@@ -208,7 +230,32 @@ export class GenerateConsoleListsUseCase {
|
|
|
208
230
|
issue.story !== null && issue.story.toLowerCase() === target;
|
|
209
231
|
};
|
|
210
232
|
|
|
211
|
-
private
|
|
233
|
+
private buildRelatedOpenPullRequestUrlsByIssueUrl = (
|
|
234
|
+
issues: Issue[],
|
|
235
|
+
): Map<string, string[]> => {
|
|
236
|
+
const urlsByIssueUrl = new Map<string, string[]>();
|
|
237
|
+
for (const issue of issues) {
|
|
238
|
+
if (!issue.isPr || issue.isClosed) {
|
|
239
|
+
continue;
|
|
240
|
+
}
|
|
241
|
+
for (const referencedIssueUrl of issue.closingIssueReferenceUrls) {
|
|
242
|
+
const existing = urlsByIssueUrl.get(referencedIssueUrl);
|
|
243
|
+
if (existing === undefined) {
|
|
244
|
+
urlsByIssueUrl.set(referencedIssueUrl, [issue.url]);
|
|
245
|
+
continue;
|
|
246
|
+
}
|
|
247
|
+
if (!existing.includes(issue.url)) {
|
|
248
|
+
existing.push(issue.url);
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
return urlsByIssueUrl;
|
|
253
|
+
};
|
|
254
|
+
|
|
255
|
+
private projectItem = (
|
|
256
|
+
issue: Issue,
|
|
257
|
+
relatedOpenPullRequestUrls: string[],
|
|
258
|
+
): ConsoleListItem => ({
|
|
212
259
|
number: issue.number,
|
|
213
260
|
title: issue.title,
|
|
214
261
|
url: issue.url,
|
|
@@ -225,6 +272,7 @@ export class GenerateConsoleListsUseCase {
|
|
|
225
272
|
dependedIssueUrls: issue.dependedIssueUrls,
|
|
226
273
|
labels: issue.labels,
|
|
227
274
|
createdAt: issue.createdAt.toISOString(),
|
|
275
|
+
relatedOpenPullRequestUrls,
|
|
228
276
|
});
|
|
229
277
|
|
|
230
278
|
private buildFieldOptions = (
|
|
@@ -17,6 +17,7 @@ export type ConsoleListItem = {
|
|
|
17
17
|
dependedIssueUrls: string[];
|
|
18
18
|
labels: string[];
|
|
19
19
|
createdAt: string;
|
|
20
|
+
relatedOpenPullRequestUrls: string[];
|
|
20
21
|
};
|
|
21
22
|
export type ConsoleFieldOption = {
|
|
22
23
|
id: string;
|
|
@@ -62,7 +63,9 @@ export type GenerateConsoleListsInput = {
|
|
|
62
63
|
export declare class GenerateConsoleListsUseCase {
|
|
63
64
|
run: (input: GenerateConsoleListsInput) => ConsoleLists;
|
|
64
65
|
private isActionable;
|
|
66
|
+
private isOwnedByNobodyElse;
|
|
65
67
|
private workflowBlockerSelector;
|
|
68
|
+
private buildRelatedOpenPullRequestUrlsByIssueUrl;
|
|
66
69
|
private projectItem;
|
|
67
70
|
private buildFieldOptions;
|
|
68
71
|
private buildStoryColorsObject;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"GenerateConsoleListsUseCase.d.ts","sourceRoot":"","sources":["../../../../src/domain/usecases/console/GenerateConsoleListsUseCase.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,sBAAsB,CAAC;AAC7C,OAAO,EAAE,WAAW,EAAE,OAAO,EAAE,MAAM,wBAAwB,CAAC;AAQ9D,MAAM,MAAM,YAAY,GAAG,WAAW,CAAC,OAAO,CAAC,CAAC;AAEhD,MAAM,MAAM,eAAe,GAAG;IAC5B,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,aAAa,EAAE,MAAM,CAAC;IACtB,aAAa,EAAE,MAAM,CAAC;IACtB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,OAAO,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,iBAAiB,EAAE,MAAM,EAAE,CAAC;IAC5B,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"GenerateConsoleListsUseCase.d.ts","sourceRoot":"","sources":["../../../../src/domain/usecases/console/GenerateConsoleListsUseCase.ts"],"names":[],"mappings":"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"}
|