github-issue-tower-defence-management 1.190.0 → 1.192.0
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/README.md +2 -0
- package/bin/adapter/entry-points/cli/index.js +5 -0
- package/bin/adapter/entry-points/cli/index.js.map +1 -1
- package/bin/adapter/entry-points/cli/projectConfig.js +3 -0
- package/bin/adapter/entry-points/cli/projectConfig.js.map +1 -1
- package/bin/adapter/entry-points/console/ui-dist/assets/{index-B22eMy23.js → index-RaFqjB7u.js} +3 -3
- package/bin/adapter/entry-points/console/ui-dist/index.html +1 -1
- package/bin/domain/usecases/NotifyFinishedIssuePreparationUseCase.js +23 -0
- package/bin/domain/usecases/NotifyFinishedIssuePreparationUseCase.js.map +1 -1
- package/bin/domain/usecases/RevertOrphanedPreparationUseCase.js +19 -0
- package/bin/domain/usecases/RevertOrphanedPreparationUseCase.js.map +1 -1
- package/bin/domain/usecases/StartPreparationUseCase.js +9 -5
- package/bin/domain/usecases/StartPreparationUseCase.js.map +1 -1
- package/bin/domain/usecases/awaitingOwnerApprovalMessage.js +7 -0
- package/bin/domain/usecases/awaitingOwnerApprovalMessage.js.map +1 -0
- package/bin/domain/usecases/isWaitingForOwnerApproval.js +16 -0
- package/bin/domain/usecases/isWaitingForOwnerApproval.js.map +1 -0
- package/package.json +1 -1
- package/src/adapter/entry-points/cli/index.test.ts +2 -0
- package/src/adapter/entry-points/cli/index.ts +9 -0
- package/src/adapter/entry-points/cli/projectConfig.ts +8 -0
- package/src/adapter/entry-points/console/ui/e2e/consoleScenario.spec.ts +35 -1
- package/src/adapter/entry-points/console/ui/e2e/consoleTestHarness.ts +13 -1
- package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsoleCommentComposer.stories.tsx +7 -0
- package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsoleCommentComposer.test.tsx +82 -0
- package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsoleCommentComposer.tsx +23 -3
- package/src/adapter/entry-points/console/ui/src/features/console/logic/operations.ts +4 -3
- package/src/adapter/entry-points/console/ui/src/features/console/pages/ConsoleItemDetailContainer.test.tsx +79 -0
- package/src/adapter/entry-points/console/ui/src/features/console/pages/ConsoleItemDetailContainer.tsx +19 -3
- package/src/adapter/entry-points/console/ui-dist/assets/{index-B22eMy23.js → index-RaFqjB7u.js} +3 -3
- package/src/adapter/entry-points/console/ui-dist/index.html +1 -1
- package/src/domain/usecases/NotifyFinishedIssuePreparationUseCase.test.ts +86 -0
- package/src/domain/usecases/NotifyFinishedIssuePreparationUseCase.ts +52 -0
- package/src/domain/usecases/RevertOrphanedPreparationUseCase.test.ts +93 -0
- package/src/domain/usecases/RevertOrphanedPreparationUseCase.ts +47 -1
- package/src/domain/usecases/StartPreparationUseCase.test.ts +76 -0
- package/src/domain/usecases/StartPreparationUseCase.ts +9 -5
- package/src/domain/usecases/awaitingOwnerApprovalMessage.ts +5 -0
- package/src/domain/usecases/isWaitingForOwnerApproval.test.ts +91 -0
- package/src/domain/usecases/isWaitingForOwnerApproval.ts +17 -0
- package/types/adapter/entry-points/cli/index.d.ts.map +1 -1
- package/types/adapter/entry-points/cli/projectConfig.d.ts +1 -0
- package/types/adapter/entry-points/cli/projectConfig.d.ts.map +1 -1
- package/types/domain/usecases/NotifyFinishedIssuePreparationUseCase.d.ts +1 -0
- package/types/domain/usecases/NotifyFinishedIssuePreparationUseCase.d.ts.map +1 -1
- package/types/domain/usecases/RevertOrphanedPreparationUseCase.d.ts +1 -0
- package/types/domain/usecases/RevertOrphanedPreparationUseCase.d.ts.map +1 -1
- package/types/domain/usecases/StartPreparationUseCase.d.ts.map +1 -1
- package/types/domain/usecases/awaitingOwnerApprovalMessage.d.ts +3 -0
- package/types/domain/usecases/awaitingOwnerApprovalMessage.d.ts.map +1 -0
- package/types/domain/usecases/isWaitingForOwnerApproval.d.ts +2 -0
- package/types/domain/usecases/isWaitingForOwnerApproval.d.ts.map +1 -0
|
@@ -329,6 +329,88 @@ describe('ConsoleCommentComposer', () => {
|
|
|
329
329
|
});
|
|
330
330
|
});
|
|
331
331
|
|
|
332
|
+
it('renders only the Comment button when onSubmitAndMoveToAwaitingWorkspace is not provided', () => {
|
|
333
|
+
const { queryByText, getByText } = render(
|
|
334
|
+
<ConsoleCommentComposer initiallyOpen onSubmit={stubSubmit} />,
|
|
335
|
+
);
|
|
336
|
+
expect(getByText('Comment')).toBeInTheDocument();
|
|
337
|
+
expect(queryByText('Comment & Awaiting Workspace')).toBeNull();
|
|
338
|
+
});
|
|
339
|
+
|
|
340
|
+
it('renders both Comment and Comment & Awaiting Workspace buttons when onSubmitAndMoveToAwaitingWorkspace is provided', () => {
|
|
341
|
+
const { getByText } = render(
|
|
342
|
+
<ConsoleCommentComposer
|
|
343
|
+
initiallyOpen
|
|
344
|
+
onSubmit={stubSubmit}
|
|
345
|
+
onSubmitAndMoveToAwaitingWorkspace={stubSubmit}
|
|
346
|
+
/>,
|
|
347
|
+
);
|
|
348
|
+
expect(getByText('Comment')).toBeInTheDocument();
|
|
349
|
+
expect(getByText('Comment & Awaiting Workspace')).toBeInTheDocument();
|
|
350
|
+
});
|
|
351
|
+
|
|
352
|
+
it('calls onSubmit and clears draft when the left Comment button is clicked', async () => {
|
|
353
|
+
const onSubmit = jest.fn(stubSubmit);
|
|
354
|
+
const onSubmitAndMoveToAwaitingWorkspace = jest.fn(stubSubmit);
|
|
355
|
+
const { getByPlaceholderText, getByText } = render(
|
|
356
|
+
<ConsoleCommentComposer
|
|
357
|
+
initiallyOpen
|
|
358
|
+
onSubmit={onSubmit}
|
|
359
|
+
onSubmitAndMoveToAwaitingWorkspace={onSubmitAndMoveToAwaitingWorkspace}
|
|
360
|
+
/>,
|
|
361
|
+
);
|
|
362
|
+
fireEvent.change(getByPlaceholderText('Leave a comment…'), {
|
|
363
|
+
target: { value: 'Left button comment.' },
|
|
364
|
+
});
|
|
365
|
+
fireEvent.click(getByText('Comment'));
|
|
366
|
+
await waitFor(() => {
|
|
367
|
+
expect(getByPlaceholderText('Leave a comment…')).toHaveValue('');
|
|
368
|
+
});
|
|
369
|
+
expect(onSubmit).toHaveBeenCalledWith('Left button comment.');
|
|
370
|
+
expect(onSubmitAndMoveToAwaitingWorkspace).not.toHaveBeenCalled();
|
|
371
|
+
});
|
|
372
|
+
|
|
373
|
+
it('calls onSubmitAndMoveToAwaitingWorkspace and clears draft when the right button is clicked', async () => {
|
|
374
|
+
const onSubmit = jest.fn(stubSubmit);
|
|
375
|
+
const onSubmitAndMoveToAwaitingWorkspace = jest.fn(stubSubmit);
|
|
376
|
+
const { getByPlaceholderText, getByText } = render(
|
|
377
|
+
<ConsoleCommentComposer
|
|
378
|
+
initiallyOpen
|
|
379
|
+
onSubmit={onSubmit}
|
|
380
|
+
onSubmitAndMoveToAwaitingWorkspace={onSubmitAndMoveToAwaitingWorkspace}
|
|
381
|
+
/>,
|
|
382
|
+
);
|
|
383
|
+
fireEvent.change(getByPlaceholderText('Leave a comment…'), {
|
|
384
|
+
target: { value: 'Right button comment.' },
|
|
385
|
+
});
|
|
386
|
+
fireEvent.click(getByText('Comment & Awaiting Workspace'));
|
|
387
|
+
await waitFor(() => {
|
|
388
|
+
expect(getByPlaceholderText('Leave a comment…')).toHaveValue('');
|
|
389
|
+
});
|
|
390
|
+
expect(onSubmitAndMoveToAwaitingWorkspace).toHaveBeenCalledWith(
|
|
391
|
+
'Right button comment.',
|
|
392
|
+
);
|
|
393
|
+
expect(onSubmit).not.toHaveBeenCalled();
|
|
394
|
+
});
|
|
395
|
+
|
|
396
|
+
it('shows an error message when the right button click results in an error', async () => {
|
|
397
|
+
const { getByPlaceholderText, getByText, findByRole } = render(
|
|
398
|
+
<ConsoleCommentComposer
|
|
399
|
+
initiallyOpen
|
|
400
|
+
onSubmit={stubSubmit}
|
|
401
|
+
onSubmitAndMoveToAwaitingWorkspace={async () => {
|
|
402
|
+
throw new Error('move failed');
|
|
403
|
+
}}
|
|
404
|
+
/>,
|
|
405
|
+
);
|
|
406
|
+
fireEvent.change(getByPlaceholderText('Leave a comment…'), {
|
|
407
|
+
target: { value: 'Will fail to move.' },
|
|
408
|
+
});
|
|
409
|
+
fireEvent.click(getByText('Comment & Awaiting Workspace'));
|
|
410
|
+
const alert = await findByRole('alert');
|
|
411
|
+
expect(alert.textContent).toContain('move failed');
|
|
412
|
+
});
|
|
413
|
+
|
|
332
414
|
it('shows the upload failure reason when the upload rejects', async () => {
|
|
333
415
|
const { getByLabelText, findByRole } = render(
|
|
334
416
|
<ConsoleCommentComposer
|
|
@@ -6,6 +6,9 @@ export type ConsoleCommentComposerProps = {
|
|
|
6
6
|
initialDraft?: string;
|
|
7
7
|
onSubmit: (body: string) => Promise<ConsoleComment>;
|
|
8
8
|
onDraftChange?: (draft: string) => void;
|
|
9
|
+
onSubmitAndMoveToAwaitingWorkspace?: (
|
|
10
|
+
body: string,
|
|
11
|
+
) => Promise<ConsoleComment>;
|
|
9
12
|
onUploadFile?: (file: File) => Promise<string>;
|
|
10
13
|
};
|
|
11
14
|
|
|
@@ -78,6 +81,7 @@ export const ConsoleCommentComposer = ({
|
|
|
78
81
|
initialDraft,
|
|
79
82
|
onSubmit,
|
|
80
83
|
onDraftChange,
|
|
84
|
+
onSubmitAndMoveToAwaitingWorkspace,
|
|
81
85
|
onUploadFile,
|
|
82
86
|
}: ConsoleCommentComposerProps) => {
|
|
83
87
|
const [open, setOpen] = useState<boolean>(initiallyOpen);
|
|
@@ -91,14 +95,18 @@ export const ConsoleCommentComposer = ({
|
|
|
91
95
|
});
|
|
92
96
|
const fileInputRef = useRef<HTMLInputElement | null>(null);
|
|
93
97
|
|
|
94
|
-
const submit = async (): Promise<void> => {
|
|
98
|
+
const submit = async (withMove: boolean): Promise<void> => {
|
|
95
99
|
const body = draft.trim();
|
|
96
100
|
if (body.length === 0 || status.kind === 'posting') {
|
|
97
101
|
return;
|
|
98
102
|
}
|
|
99
103
|
setStatus({ kind: 'posting' });
|
|
100
104
|
try {
|
|
101
|
-
|
|
105
|
+
if (withMove && onSubmitAndMoveToAwaitingWorkspace !== undefined) {
|
|
106
|
+
await onSubmitAndMoveToAwaitingWorkspace(body);
|
|
107
|
+
} else {
|
|
108
|
+
await onSubmit(body);
|
|
109
|
+
}
|
|
102
110
|
setDraft('');
|
|
103
111
|
setStatus({ kind: 'idle' });
|
|
104
112
|
} catch (error) {
|
|
@@ -231,11 +239,23 @@ export const ConsoleCommentComposer = ({
|
|
|
231
239
|
className="console-composer-submit"
|
|
232
240
|
disabled={status.kind === 'posting'}
|
|
233
241
|
onClick={() => {
|
|
234
|
-
void submit();
|
|
242
|
+
void submit(false);
|
|
235
243
|
}}
|
|
236
244
|
>
|
|
237
245
|
Comment
|
|
238
246
|
</button>
|
|
247
|
+
{onSubmitAndMoveToAwaitingWorkspace !== undefined && (
|
|
248
|
+
<button
|
|
249
|
+
type="button"
|
|
250
|
+
className="console-composer-submit"
|
|
251
|
+
disabled={status.kind === 'posting'}
|
|
252
|
+
onClick={() => {
|
|
253
|
+
void submit(true);
|
|
254
|
+
}}
|
|
255
|
+
>
|
|
256
|
+
Comment & Awaiting Workspace
|
|
257
|
+
</button>
|
|
258
|
+
)}
|
|
239
259
|
</div>
|
|
240
260
|
</div>
|
|
241
261
|
)}
|
|
@@ -41,15 +41,16 @@ export type ConsoleOperationHandlers = {
|
|
|
41
41
|
onOkAndAwaitingWorkspace: (option: ConsoleFieldOption) => void;
|
|
42
42
|
};
|
|
43
43
|
|
|
44
|
+
export const IN_TMUX_BY_HUMAN_NAME = 'In Tmux by human';
|
|
45
|
+
export const AWAITING_WORKSPACE_NAME = 'Awaiting Workspace';
|
|
46
|
+
|
|
44
47
|
export const STATUS_BUTTON_NAMES: string[] = [
|
|
45
48
|
'In Tmux by agent',
|
|
46
49
|
'In Tmux by human',
|
|
47
50
|
'Todo by human',
|
|
48
51
|
'Todo by agent',
|
|
49
|
-
|
|
52
|
+
AWAITING_WORKSPACE_NAME,
|
|
50
53
|
];
|
|
51
54
|
|
|
52
|
-
export const IN_TMUX_BY_HUMAN_NAME = 'In Tmux by human';
|
|
53
|
-
|
|
54
55
|
export const isManualTriageTab = (tab: ConsoleTabName): boolean =>
|
|
55
56
|
tab === 'todo-by-human' || tab === 'todo-by-agent';
|
|
@@ -2,6 +2,7 @@ import { fireEvent, render, waitFor } from '@testing-library/react';
|
|
|
2
2
|
import type { ConsoleCaches } from '../hooks/useConsoleCaches';
|
|
3
3
|
import type { ConsoleOperationsApi } from '../hooks/useConsoleOperations';
|
|
4
4
|
import { ResourceCache } from '../lib/resourceCache';
|
|
5
|
+
import { AWAITING_WORKSPACE_NAME } from '../logic/operations';
|
|
5
6
|
import type {
|
|
6
7
|
ConsoleChangedFile,
|
|
7
8
|
ConsoleRelatedPullRequest,
|
|
@@ -405,6 +406,84 @@ describe('ConsoleItemDetailContainer', () => {
|
|
|
405
406
|
]);
|
|
406
407
|
});
|
|
407
408
|
|
|
409
|
+
it('passes onSubmitAndMoveToAwaitingWorkspace to the composer when statusOptions includes Awaiting Workspace', () => {
|
|
410
|
+
const { getByText } = render(
|
|
411
|
+
<ConsoleItemDetailContainer
|
|
412
|
+
tab="todo-by-human"
|
|
413
|
+
item={issueItem}
|
|
414
|
+
caches={buildCaches()}
|
|
415
|
+
operations={buildOperations()}
|
|
416
|
+
statusOptions={consoleStatusOptionsFixture}
|
|
417
|
+
storyOptions={consoleStoryOptionsFixture}
|
|
418
|
+
storyColors={consoleStoryColorsFixture}
|
|
419
|
+
storyName="TDPM Console port"
|
|
420
|
+
overlayStatus={null}
|
|
421
|
+
now={Date.parse('2026-06-19T12:00:00.000Z')}
|
|
422
|
+
onQueueAction={jest.fn()}
|
|
423
|
+
/>,
|
|
424
|
+
);
|
|
425
|
+
expect(getByText('Comment & Awaiting Workspace')).toBeInTheDocument();
|
|
426
|
+
});
|
|
427
|
+
|
|
428
|
+
it('does not pass onSubmitAndMoveToAwaitingWorkspace to the composer when statusOptions does not include Awaiting Workspace', () => {
|
|
429
|
+
const statusOptionsWithoutAwaitingWorkspace =
|
|
430
|
+
consoleStatusOptionsFixture.filter(
|
|
431
|
+
(o) => o.name !== AWAITING_WORKSPACE_NAME,
|
|
432
|
+
);
|
|
433
|
+
const { queryByText } = render(
|
|
434
|
+
<ConsoleItemDetailContainer
|
|
435
|
+
tab="todo-by-human"
|
|
436
|
+
item={issueItem}
|
|
437
|
+
caches={buildCaches()}
|
|
438
|
+
operations={buildOperations()}
|
|
439
|
+
statusOptions={statusOptionsWithoutAwaitingWorkspace}
|
|
440
|
+
storyOptions={consoleStoryOptionsFixture}
|
|
441
|
+
storyColors={consoleStoryColorsFixture}
|
|
442
|
+
storyName="TDPM Console port"
|
|
443
|
+
overlayStatus={null}
|
|
444
|
+
now={Date.parse('2026-06-19T12:00:00.000Z')}
|
|
445
|
+
onQueueAction={jest.fn()}
|
|
446
|
+
/>,
|
|
447
|
+
);
|
|
448
|
+
expect(queryByText('Comment & Awaiting Workspace')).toBeNull();
|
|
449
|
+
});
|
|
450
|
+
|
|
451
|
+
it('clicking Comment & Awaiting Workspace calls addComment and queues a set_status action for the Awaiting Workspace option', async () => {
|
|
452
|
+
const operations = buildOperations();
|
|
453
|
+
const onQueueAction = jest.fn();
|
|
454
|
+
const { getByPlaceholderText, getByText } = render(
|
|
455
|
+
<ConsoleItemDetailContainer
|
|
456
|
+
tab="todo-by-human"
|
|
457
|
+
item={issueItem}
|
|
458
|
+
caches={buildCaches()}
|
|
459
|
+
operations={operations}
|
|
460
|
+
statusOptions={consoleStatusOptionsFixture}
|
|
461
|
+
storyOptions={consoleStoryOptionsFixture}
|
|
462
|
+
storyColors={consoleStoryColorsFixture}
|
|
463
|
+
storyName="TDPM Console port"
|
|
464
|
+
overlayStatus={null}
|
|
465
|
+
now={Date.parse('2026-06-19T12:00:00.000Z')}
|
|
466
|
+
onQueueAction={onQueueAction}
|
|
467
|
+
/>,
|
|
468
|
+
);
|
|
469
|
+
fireEvent.change(getByPlaceholderText('Leave a comment…'), {
|
|
470
|
+
target: { value: 'test comment body' },
|
|
471
|
+
});
|
|
472
|
+
fireEvent.click(getByText('Comment & Awaiting Workspace'));
|
|
473
|
+
await waitFor(() => {
|
|
474
|
+
expect(operations.addComment).toHaveBeenCalledWith(
|
|
475
|
+
issueItem,
|
|
476
|
+
'test comment body',
|
|
477
|
+
);
|
|
478
|
+
});
|
|
479
|
+
expect(onQueueAction).toHaveBeenCalledWith(
|
|
480
|
+
expect.objectContaining({
|
|
481
|
+
kind: { type: 'set_status', optionName: AWAITING_WORKSPACE_NAME },
|
|
482
|
+
item: issueItem,
|
|
483
|
+
}),
|
|
484
|
+
);
|
|
485
|
+
});
|
|
486
|
+
|
|
408
487
|
it('collects an inline comment on an issue related pull request diff, enables Reject, and submits it as the request-changes review for that pull request url', async () => {
|
|
409
488
|
const operations = buildOperations();
|
|
410
489
|
const onQueueAction = jest.fn();
|
|
@@ -9,9 +9,10 @@ import type { ConsoleOperationsApi } from '../hooks/useConsoleOperations';
|
|
|
9
9
|
import { buildImageProxyUrl } from '../lib/imageProxy';
|
|
10
10
|
import type { ConsoleActionKind } from '../logic/actionToast';
|
|
11
11
|
import { resolveStoryColorEnum } from '../logic/grouping';
|
|
12
|
-
import
|
|
13
|
-
|
|
14
|
-
|
|
12
|
+
import {
|
|
13
|
+
AWAITING_WORKSPACE_NAME,
|
|
14
|
+
type ConsoleOperationHandlers,
|
|
15
|
+
type ConsolePendingReviewComment,
|
|
15
16
|
} from '../logic/operations';
|
|
16
17
|
import { mergePostedComments } from '../logic/postedComments';
|
|
17
18
|
import type {
|
|
@@ -163,6 +164,18 @@ export const ConsoleItemDetailContainer = ({
|
|
|
163
164
|
},
|
|
164
165
|
};
|
|
165
166
|
|
|
167
|
+
const awaitingWorkspaceOption =
|
|
168
|
+
statusOptions.find((o) => o.name === AWAITING_WORKSPACE_NAME) ?? null;
|
|
169
|
+
|
|
170
|
+
const addCommentAndMoveToAwaitingWorkspace =
|
|
171
|
+
awaitingWorkspaceOption !== null
|
|
172
|
+
? async (body: string): Promise<ConsoleComment> => {
|
|
173
|
+
const comment = await addComment(body);
|
|
174
|
+
handlers.onSetStatus(awaitingWorkspaceOption);
|
|
175
|
+
return comment;
|
|
176
|
+
}
|
|
177
|
+
: undefined;
|
|
178
|
+
|
|
166
179
|
const resolvedStoryName =
|
|
167
180
|
storyName ?? (item.story.trim() !== '' ? item.story : null);
|
|
168
181
|
const storyColorEnum: ConsoleColor | null =
|
|
@@ -205,6 +218,9 @@ export const ConsoleItemDetailContainer = ({
|
|
|
205
218
|
initialDraft={initialCommentDraft}
|
|
206
219
|
onSubmit={addComment}
|
|
207
220
|
onDraftChange={onCommentDraftChange}
|
|
221
|
+
onSubmitAndMoveToAwaitingWorkspace={
|
|
222
|
+
addCommentAndMoveToAwaitingWorkspace
|
|
223
|
+
}
|
|
208
224
|
onUploadFile={(file) => operations.uploadAttachment(item, file)}
|
|
209
225
|
/>
|
|
210
226
|
}
|