github-issue-tower-defence-management 2.17.0 → 2.19.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/bin/adapter/entry-points/console/consoleOperationApi.js +12 -1
- package/bin/adapter/entry-points/console/consoleOperationApi.js.map +1 -1
- package/bin/adapter/entry-points/console/ui-dist/assets/index-BYDbK1Fj.js +88 -0
- package/bin/adapter/entry-points/console/ui-dist/assets/{index-DshIymWu.css → index-KyQISRuQ.css} +1 -1
- package/bin/adapter/entry-points/console/ui-dist/index.html +2 -2
- package/bin/adapter/entry-points/console/webServer.js +2 -0
- package/bin/adapter/entry-points/console/webServer.js.map +1 -1
- package/bin/adapter/repositories/issue/ApiV3CheerioRestIssueRepository.js +41 -0
- package/bin/adapter/repositories/issue/ApiV3CheerioRestIssueRepository.js.map +1 -1
- package/package.json +1 -1
- package/src/adapter/entry-points/console/consoleOperationApi.test.ts +30 -0
- package/src/adapter/entry-points/console/consoleOperationApi.ts +14 -0
- package/src/adapter/entry-points/console/ui/e2e/consoleScenario.spec.ts +65 -0
- package/src/adapter/entry-points/console/ui/e2e/consoleTestHarness.ts +12 -0
- package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsoleCommentComposer.stories.tsx +8 -0
- package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsoleCommentComposer.test.tsx +81 -2
- package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsoleCommentComposer.tsx +12 -0
- package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsoleItemDetail.stories.tsx +1 -0
- package/src/adapter/entry-points/console/ui/src/features/console/components/operations/ConsoleDangerousActions.stories.tsx +14 -0
- package/src/adapter/entry-points/console/ui/src/features/console/components/operations/ConsoleDangerousActions.test.tsx +40 -0
- package/src/adapter/entry-points/console/ui/src/features/console/components/operations/ConsoleDangerousActions.tsx +37 -0
- package/src/adapter/entry-points/console/ui/src/features/console/components/operations/ConsoleOperationMenu.stories.tsx +1 -0
- package/src/adapter/entry-points/console/ui/src/features/console/components/operations/ConsoleOperationMenu.test.tsx +15 -0
- package/src/adapter/entry-points/console/ui/src/features/console/components/operations/ConsoleOperationMenu.tsx +3 -4
- package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsoleOperations.ts +11 -0
- package/src/adapter/entry-points/console/ui/src/features/console/lib/consoleApi.ts +19 -0
- package/src/adapter/entry-points/console/ui/src/features/console/logic/actionToast.ts +9 -1
- package/src/adapter/entry-points/console/ui/src/features/console/logic/operations.ts +1 -0
- package/src/adapter/entry-points/console/ui/src/features/console/pages/ConsoleItemDetailContainer.test.tsx +67 -0
- package/src/adapter/entry-points/console/ui/src/features/console/pages/ConsoleItemDetailContainer.tsx +12 -0
- package/src/adapter/entry-points/console/ui/src/index.css +6 -0
- package/src/adapter/entry-points/console/ui-dist/assets/index-BYDbK1Fj.js +88 -0
- package/src/adapter/entry-points/console/ui-dist/assets/{index-DshIymWu.css → index-KyQISRuQ.css} +1 -1
- package/src/adapter/entry-points/console/ui-dist/index.html +2 -2
- package/src/adapter/entry-points/console/webServer.ts +3 -0
- package/src/adapter/repositories/issue/ApiV3CheerioRestIssueRepository.test.ts +150 -0
- package/src/adapter/repositories/issue/ApiV3CheerioRestIssueRepository.ts +70 -0
- package/src/domain/usecases/adapter-interfaces/IssueRepository.ts +1 -0
- package/types/adapter/entry-points/console/consoleOperationApi.d.ts +1 -0
- package/types/adapter/entry-points/console/consoleOperationApi.d.ts.map +1 -1
- package/types/adapter/entry-points/console/webServer.d.ts.map +1 -1
- package/types/adapter/repositories/issue/ApiV3CheerioRestIssueRepository.d.ts +1 -0
- package/types/adapter/repositories/issue/ApiV3CheerioRestIssueRepository.d.ts.map +1 -1
- package/types/domain/usecases/adapter-interfaces/IssueRepository.d.ts +1 -0
- package/types/domain/usecases/adapter-interfaces/IssueRepository.d.ts.map +1 -1
- package/bin/adapter/entry-points/console/ui-dist/assets/index-DKxao68k.js +0 -88
- package/src/adapter/entry-points/console/ui/src/features/console/components/operations/ConsoleOkAndAwaitingWorkspaceActions.stories.tsx +0 -25
- package/src/adapter/entry-points/console/ui/src/features/console/components/operations/ConsoleOkAndAwaitingWorkspaceActions.test.tsx +0 -62
- package/src/adapter/entry-points/console/ui/src/features/console/components/operations/ConsoleOkAndAwaitingWorkspaceActions.tsx +0 -33
- package/src/adapter/entry-points/console/ui-dist/assets/index-DKxao68k.js +0 -88
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { fireEvent, render, waitFor } from '@testing-library/react';
|
|
1
|
+
import { act, fireEvent, render, waitFor } from '@testing-library/react';
|
|
2
2
|
import type { ConsoleComment } from '../../logic/types';
|
|
3
3
|
import {
|
|
4
4
|
appendAttachmentMarkdown,
|
|
@@ -329,14 +329,93 @@ describe('ConsoleCommentComposer', () => {
|
|
|
329
329
|
});
|
|
330
330
|
});
|
|
331
331
|
|
|
332
|
-
it('renders only the Comment button when onSubmitAndMoveToAwaitingWorkspace is
|
|
332
|
+
it('renders only the Comment button when neither onOkAndAwaitingWorkspace nor onSubmitAndMoveToAwaitingWorkspace is provided', () => {
|
|
333
333
|
const { queryByText, getByText } = render(
|
|
334
334
|
<ConsoleCommentComposer initiallyOpen onSubmit={stubSubmit} />,
|
|
335
335
|
);
|
|
336
336
|
expect(getByText('Comment')).toBeInTheDocument();
|
|
337
|
+
expect(queryByText('ok & Awaiting Workspace')).toBeNull();
|
|
337
338
|
expect(queryByText('Comment & Awaiting Workspace')).toBeNull();
|
|
338
339
|
});
|
|
339
340
|
|
|
341
|
+
it('renders ok & Awaiting Workspace between Comment and Comment & Awaiting Workspace when both callbacks are provided', () => {
|
|
342
|
+
const { getAllByRole } = render(
|
|
343
|
+
<ConsoleCommentComposer
|
|
344
|
+
initiallyOpen
|
|
345
|
+
onSubmit={stubSubmit}
|
|
346
|
+
onOkAndAwaitingWorkspace={() => {}}
|
|
347
|
+
onSubmitAndMoveToAwaitingWorkspace={stubSubmit}
|
|
348
|
+
/>,
|
|
349
|
+
);
|
|
350
|
+
const buttons = getAllByRole('button').filter((b) =>
|
|
351
|
+
[
|
|
352
|
+
'Comment',
|
|
353
|
+
'ok & Awaiting Workspace',
|
|
354
|
+
'Comment & Awaiting Workspace',
|
|
355
|
+
].includes(b.textContent ?? ''),
|
|
356
|
+
);
|
|
357
|
+
expect(buttons.map((b) => b.textContent)).toEqual([
|
|
358
|
+
'Comment',
|
|
359
|
+
'ok & Awaiting Workspace',
|
|
360
|
+
'Comment & Awaiting Workspace',
|
|
361
|
+
]);
|
|
362
|
+
});
|
|
363
|
+
|
|
364
|
+
it('calls onOkAndAwaitingWorkspace when the ok & Awaiting Workspace button is clicked', () => {
|
|
365
|
+
const onOkAndAwaitingWorkspace = jest.fn();
|
|
366
|
+
const { getByText } = render(
|
|
367
|
+
<ConsoleCommentComposer
|
|
368
|
+
initiallyOpen
|
|
369
|
+
onSubmit={stubSubmit}
|
|
370
|
+
onOkAndAwaitingWorkspace={onOkAndAwaitingWorkspace}
|
|
371
|
+
/>,
|
|
372
|
+
);
|
|
373
|
+
fireEvent.click(getByText('ok & Awaiting Workspace'));
|
|
374
|
+
expect(onOkAndAwaitingWorkspace).toHaveBeenCalledTimes(1);
|
|
375
|
+
});
|
|
376
|
+
|
|
377
|
+
it('disables the ok & Awaiting Workspace button while a comment is being posted', async () => {
|
|
378
|
+
let resolvePost!: () => void;
|
|
379
|
+
const slowSubmit = async (_body: string): Promise<ConsoleComment> =>
|
|
380
|
+
new Promise((resolve) => {
|
|
381
|
+
resolvePost = () =>
|
|
382
|
+
resolve({
|
|
383
|
+
author: 'HiromiShikata',
|
|
384
|
+
body: _body,
|
|
385
|
+
createdAt: '2026-06-19T11:58:00.000Z',
|
|
386
|
+
});
|
|
387
|
+
});
|
|
388
|
+
const { getByPlaceholderText, getByText } = render(
|
|
389
|
+
<ConsoleCommentComposer
|
|
390
|
+
initiallyOpen
|
|
391
|
+
onSubmit={slowSubmit}
|
|
392
|
+
onOkAndAwaitingWorkspace={() => {}}
|
|
393
|
+
onSubmitAndMoveToAwaitingWorkspace={stubSubmit}
|
|
394
|
+
/>,
|
|
395
|
+
);
|
|
396
|
+
fireEvent.change(getByPlaceholderText('Leave a comment…'), {
|
|
397
|
+
target: { value: 'in-flight comment' },
|
|
398
|
+
});
|
|
399
|
+
fireEvent.click(getByText('Comment'));
|
|
400
|
+
await waitFor(() => {
|
|
401
|
+
expect(getByText('ok & Awaiting Workspace')).toBeDisabled();
|
|
402
|
+
});
|
|
403
|
+
await act(async () => {
|
|
404
|
+
resolvePost();
|
|
405
|
+
});
|
|
406
|
+
});
|
|
407
|
+
|
|
408
|
+
it('does not render ok & Awaiting Workspace when onOkAndAwaitingWorkspace is not provided', () => {
|
|
409
|
+
const { queryByText } = render(
|
|
410
|
+
<ConsoleCommentComposer
|
|
411
|
+
initiallyOpen
|
|
412
|
+
onSubmit={stubSubmit}
|
|
413
|
+
onSubmitAndMoveToAwaitingWorkspace={stubSubmit}
|
|
414
|
+
/>,
|
|
415
|
+
);
|
|
416
|
+
expect(queryByText('ok & Awaiting Workspace')).toBeNull();
|
|
417
|
+
});
|
|
418
|
+
|
|
340
419
|
it('renders both Comment and Comment & Awaiting Workspace buttons when onSubmitAndMoveToAwaitingWorkspace is provided', () => {
|
|
341
420
|
const { getByText } = render(
|
|
342
421
|
<ConsoleCommentComposer
|
|
@@ -9,6 +9,7 @@ export type ConsoleCommentComposerProps = {
|
|
|
9
9
|
onSubmitAndMoveToAwaitingWorkspace?: (
|
|
10
10
|
body: string,
|
|
11
11
|
) => Promise<ConsoleComment>;
|
|
12
|
+
onOkAndAwaitingWorkspace?: () => void;
|
|
12
13
|
onUploadFile?: (file: File) => Promise<string>;
|
|
13
14
|
};
|
|
14
15
|
|
|
@@ -82,6 +83,7 @@ export const ConsoleCommentComposer = ({
|
|
|
82
83
|
onSubmit,
|
|
83
84
|
onDraftChange,
|
|
84
85
|
onSubmitAndMoveToAwaitingWorkspace,
|
|
86
|
+
onOkAndAwaitingWorkspace,
|
|
85
87
|
onUploadFile,
|
|
86
88
|
}: ConsoleCommentComposerProps) => {
|
|
87
89
|
const [open, setOpen] = useState<boolean>(initiallyOpen);
|
|
@@ -244,6 +246,16 @@ export const ConsoleCommentComposer = ({
|
|
|
244
246
|
>
|
|
245
247
|
Comment
|
|
246
248
|
</button>
|
|
249
|
+
{onOkAndAwaitingWorkspace !== undefined && (
|
|
250
|
+
<button
|
|
251
|
+
type="button"
|
|
252
|
+
className="console-composer-submit"
|
|
253
|
+
disabled={status.kind === 'posting'}
|
|
254
|
+
onClick={onOkAndAwaitingWorkspace}
|
|
255
|
+
>
|
|
256
|
+
ok & Awaiting Workspace
|
|
257
|
+
</button>
|
|
258
|
+
)}
|
|
247
259
|
{onSubmitAndMoveToAwaitingWorkspace !== undefined && (
|
|
248
260
|
<button
|
|
249
261
|
type="button"
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react-vite';
|
|
2
|
+
import { ConsoleDangerousActions } from './ConsoleDangerousActions';
|
|
3
|
+
|
|
4
|
+
const meta: Meta<typeof ConsoleDangerousActions> = {
|
|
5
|
+
title: 'Console/ConsoleDangerousActions',
|
|
6
|
+
component: ConsoleDangerousActions,
|
|
7
|
+
args: { onDeleteAllComments: () => {} },
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export default meta;
|
|
11
|
+
|
|
12
|
+
type Story = StoryObj<typeof ConsoleDangerousActions>;
|
|
13
|
+
|
|
14
|
+
export const Default: Story = {};
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { fireEvent, render } from '@testing-library/react';
|
|
2
|
+
import { ConsoleDangerousActions } from './ConsoleDangerousActions';
|
|
3
|
+
|
|
4
|
+
describe('ConsoleDangerousActions', () => {
|
|
5
|
+
it('renders only the toggle button in the collapsed state', () => {
|
|
6
|
+
const { getByText, queryByText } = render(
|
|
7
|
+
<ConsoleDangerousActions onDeleteAllComments={() => {}} />,
|
|
8
|
+
);
|
|
9
|
+
expect(getByText('⚠')).toBeInTheDocument();
|
|
10
|
+
expect(queryByText('Delete All Comments')).toBeNull();
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
it('reveals the Delete All Comments button after the toggle is clicked', () => {
|
|
14
|
+
const { getByText } = render(
|
|
15
|
+
<ConsoleDangerousActions onDeleteAllComments={() => {}} />,
|
|
16
|
+
);
|
|
17
|
+
fireEvent.click(getByText('⚠'));
|
|
18
|
+
expect(getByText('Delete All Comments')).toBeInTheDocument();
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
it('collapses back when the toggle is clicked a second time', () => {
|
|
22
|
+
const { getByText, queryByText } = render(
|
|
23
|
+
<ConsoleDangerousActions onDeleteAllComments={() => {}} />,
|
|
24
|
+
);
|
|
25
|
+
fireEvent.click(getByText('⚠'));
|
|
26
|
+
fireEvent.click(getByText('⚠'));
|
|
27
|
+
expect(queryByText('Delete All Comments')).toBeNull();
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
it('calls onDeleteAllComments and collapses when Delete All Comments is clicked', () => {
|
|
31
|
+
const onDeleteAllComments = jest.fn();
|
|
32
|
+
const { getByText, queryByText } = render(
|
|
33
|
+
<ConsoleDangerousActions onDeleteAllComments={onDeleteAllComments} />,
|
|
34
|
+
);
|
|
35
|
+
fireEvent.click(getByText('⚠'));
|
|
36
|
+
fireEvent.click(getByText('Delete All Comments'));
|
|
37
|
+
expect(onDeleteAllComments).toHaveBeenCalledTimes(1);
|
|
38
|
+
expect(queryByText('Delete All Comments')).toBeNull();
|
|
39
|
+
});
|
|
40
|
+
});
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { useState } from 'react';
|
|
2
|
+
|
|
3
|
+
export type ConsoleDangerousActionsProps = {
|
|
4
|
+
onDeleteAllComments: () => void;
|
|
5
|
+
};
|
|
6
|
+
|
|
7
|
+
export const ConsoleDangerousActions = ({
|
|
8
|
+
onDeleteAllComments,
|
|
9
|
+
}: ConsoleDangerousActionsProps) => {
|
|
10
|
+
const [expanded, setExpanded] = useState(false);
|
|
11
|
+
|
|
12
|
+
const handleDeleteAllComments = () => {
|
|
13
|
+
onDeleteAllComments();
|
|
14
|
+
setExpanded(false);
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
return (
|
|
18
|
+
<div className="console-op-group">
|
|
19
|
+
<button
|
|
20
|
+
type="button"
|
|
21
|
+
className="console-op-button console-op-button-danger"
|
|
22
|
+
onClick={() => setExpanded((prev) => !prev)}
|
|
23
|
+
>
|
|
24
|
+
⚠
|
|
25
|
+
</button>
|
|
26
|
+
{expanded && (
|
|
27
|
+
<button
|
|
28
|
+
type="button"
|
|
29
|
+
className="console-op-button console-op-button-danger"
|
|
30
|
+
onClick={handleDeleteAllComments}
|
|
31
|
+
>
|
|
32
|
+
Delete All Comments
|
|
33
|
+
</button>
|
|
34
|
+
)}
|
|
35
|
+
</div>
|
|
36
|
+
);
|
|
37
|
+
};
|
|
@@ -14,6 +14,7 @@ const handlers: ConsoleOperationHandlers = {
|
|
|
14
14
|
onSetInTmuxByHuman: jest.fn(),
|
|
15
15
|
onClose: jest.fn(),
|
|
16
16
|
onOkAndAwaitingWorkspace: jest.fn(),
|
|
17
|
+
onDeleteAllComments: jest.fn(),
|
|
17
18
|
};
|
|
18
19
|
|
|
19
20
|
const prItem = consoleListItemsFixture[0];
|
|
@@ -141,4 +142,18 @@ describe('ConsoleOperationMenu', () => {
|
|
|
141
142
|
);
|
|
142
143
|
expect(getByText('Reject')).not.toBeDisabled();
|
|
143
144
|
});
|
|
145
|
+
|
|
146
|
+
it('renders the dangerous actions toggle on all tabs', () => {
|
|
147
|
+
const { getByText } = render(
|
|
148
|
+
<ConsoleOperationMenu
|
|
149
|
+
tab="prs"
|
|
150
|
+
item={prItem}
|
|
151
|
+
hasPullRequest
|
|
152
|
+
rejectEnabled={false}
|
|
153
|
+
statusOptions={consoleStatusOptionsFixture}
|
|
154
|
+
handlers={handlers}
|
|
155
|
+
/>,
|
|
156
|
+
);
|
|
157
|
+
expect(getByText('⚠')).toBeInTheDocument();
|
|
158
|
+
});
|
|
144
159
|
});
|
|
@@ -8,8 +8,8 @@ import type {
|
|
|
8
8
|
ConsoleTabName,
|
|
9
9
|
} from '../../logic/types';
|
|
10
10
|
import { ConsoleCloseActions } from './ConsoleCloseActions';
|
|
11
|
+
import { ConsoleDangerousActions } from './ConsoleDangerousActions';
|
|
11
12
|
import { ConsoleNextActionDateActions } from './ConsoleNextActionDateActions';
|
|
12
|
-
import { ConsoleOkAndAwaitingWorkspaceActions } from './ConsoleOkAndAwaitingWorkspaceActions';
|
|
13
13
|
import { ConsolePullRequestReviewActions } from './ConsolePullRequestReviewActions';
|
|
14
14
|
import { ConsoleStatusActions } from './ConsoleStatusActions';
|
|
15
15
|
|
|
@@ -31,9 +31,8 @@ export const ConsoleOperationMenu = ({
|
|
|
31
31
|
}: ConsoleOperationBarProps) => {
|
|
32
32
|
return (
|
|
33
33
|
<div className="console-operation-bar">
|
|
34
|
-
<
|
|
35
|
-
|
|
36
|
-
onOkAndAwaitingWorkspace={handlers.onOkAndAwaitingWorkspace}
|
|
34
|
+
<ConsoleDangerousActions
|
|
35
|
+
onDeleteAllComments={handlers.onDeleteAllComments}
|
|
37
36
|
/>
|
|
38
37
|
{hasPullRequest && (
|
|
39
38
|
<ConsolePullRequestReviewActions
|
package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsoleOperations.ts
CHANGED
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
encodeAttachmentContent,
|
|
8
8
|
postConsoleAttachment,
|
|
9
9
|
postConsoleComment,
|
|
10
|
+
postConsoleDeleteAllComments,
|
|
10
11
|
postConsoleOperation,
|
|
11
12
|
postConsoleReviewComment,
|
|
12
13
|
} from '../lib/consoleApi';
|
|
@@ -74,6 +75,7 @@ export type ConsoleOperationsApi = {
|
|
|
74
75
|
side: ConsoleReviewCommentSide,
|
|
75
76
|
body: string,
|
|
76
77
|
) => Promise<void>;
|
|
78
|
+
deleteAllComments: (item: ConsoleListItem) => Promise<void>;
|
|
77
79
|
};
|
|
78
80
|
|
|
79
81
|
export const reviewRequest = (
|
|
@@ -391,6 +393,14 @@ export const useConsoleOperations = (
|
|
|
391
393
|
[pjcode],
|
|
392
394
|
);
|
|
393
395
|
|
|
396
|
+
const deleteAllComments = useCallback(
|
|
397
|
+
async (item: ConsoleListItem) => {
|
|
398
|
+
await postConsoleDeleteAllComments({ issueUrl: item.url });
|
|
399
|
+
invalidateItemContent(item);
|
|
400
|
+
},
|
|
401
|
+
[invalidateItemContent],
|
|
402
|
+
);
|
|
403
|
+
|
|
394
404
|
return {
|
|
395
405
|
reviewPullRequest,
|
|
396
406
|
setNextActionDate,
|
|
@@ -402,5 +412,6 @@ export const useConsoleOperations = (
|
|
|
402
412
|
addComment,
|
|
403
413
|
uploadAttachment,
|
|
404
414
|
addInlineReviewComment,
|
|
415
|
+
deleteAllComments,
|
|
405
416
|
};
|
|
406
417
|
};
|
|
@@ -482,6 +482,25 @@ export const postConsoleReorderStory = async (
|
|
|
482
482
|
}
|
|
483
483
|
};
|
|
484
484
|
|
|
485
|
+
export const DELETE_ALL_COMMENTS_OPERATION_PATH = '/api/deleteallcomments';
|
|
486
|
+
|
|
487
|
+
export type ConsoleDeleteAllCommentsRequest = {
|
|
488
|
+
issueUrl: string;
|
|
489
|
+
};
|
|
490
|
+
|
|
491
|
+
export const postConsoleDeleteAllComments = async (
|
|
492
|
+
request: ConsoleDeleteAllCommentsRequest,
|
|
493
|
+
): Promise<void> => {
|
|
494
|
+
const response = await fetch(DELETE_ALL_COMMENTS_OPERATION_PATH, {
|
|
495
|
+
method: 'POST',
|
|
496
|
+
headers: { 'Content-Type': 'application/json' },
|
|
497
|
+
body: JSON.stringify(request),
|
|
498
|
+
});
|
|
499
|
+
if (!response.ok) {
|
|
500
|
+
throw new Error(await readOperationErrorReason(response));
|
|
501
|
+
}
|
|
502
|
+
};
|
|
503
|
+
|
|
485
504
|
export type ProjectListResponse = {
|
|
486
505
|
pjcodes: string[];
|
|
487
506
|
workflowImprovementIssueUrl: string | null;
|
|
@@ -21,7 +21,8 @@ export type ConsoleActionKind =
|
|
|
21
21
|
| { type: 'set_status'; optionName: string }
|
|
22
22
|
| { type: 'set_in_tmux_by_human'; optionName: string }
|
|
23
23
|
| { type: 'close'; action: ConsoleCloseAction }
|
|
24
|
-
| { type: 'ok_and_awaiting_workspace' }
|
|
24
|
+
| { type: 'ok_and_awaiting_workspace' }
|
|
25
|
+
| { type: 'delete_all_comments' };
|
|
25
26
|
|
|
26
27
|
export const ACTION_TOAST_DELAY_MS = 5000;
|
|
27
28
|
|
|
@@ -67,6 +68,8 @@ export const actionToastMessage = (
|
|
|
67
68
|
return 'ok → Awaiting Workspace';
|
|
68
69
|
case 'close':
|
|
69
70
|
return kind.action === 'close' ? 'Closed' : 'Closed as not planned';
|
|
71
|
+
case 'delete_all_comments':
|
|
72
|
+
return 'Deleted all comments';
|
|
70
73
|
}
|
|
71
74
|
};
|
|
72
75
|
|
|
@@ -94,6 +97,8 @@ export const actionToastColor = (
|
|
|
94
97
|
return 'blue';
|
|
95
98
|
case 'close':
|
|
96
99
|
return 'red';
|
|
100
|
+
case 'delete_all_comments':
|
|
101
|
+
return 'red';
|
|
97
102
|
}
|
|
98
103
|
};
|
|
99
104
|
|
|
@@ -104,6 +109,9 @@ export const actionAdvances = (
|
|
|
104
109
|
if (kind.type === 'next_action_date') {
|
|
105
110
|
return isManualTriageTab(tab);
|
|
106
111
|
}
|
|
112
|
+
if (kind.type === 'delete_all_comments') {
|
|
113
|
+
return false;
|
|
114
|
+
}
|
|
107
115
|
return true;
|
|
108
116
|
};
|
|
109
117
|
|
|
@@ -43,6 +43,7 @@ export type ConsoleOperationHandlers = {
|
|
|
43
43
|
onSetInTmuxByHuman: (option: ConsoleFieldOption) => void;
|
|
44
44
|
onClose: (action: ConsoleCloseAction) => void;
|
|
45
45
|
onOkAndAwaitingWorkspace: (option: ConsoleFieldOption) => void;
|
|
46
|
+
onDeleteAllComments: () => void;
|
|
46
47
|
};
|
|
47
48
|
|
|
48
49
|
export const IN_TMUX_BY_HUMAN_NAME = 'In Tmux by human';
|
|
@@ -82,6 +82,7 @@ const buildOperations = (): ConsoleOperationsApi => ({
|
|
|
82
82
|
})),
|
|
83
83
|
uploadAttachment: jest.fn(async () => ''),
|
|
84
84
|
addInlineReviewComment: jest.fn(async () => {}),
|
|
85
|
+
deleteAllComments: jest.fn(async () => {}),
|
|
85
86
|
});
|
|
86
87
|
|
|
87
88
|
const findCommentsPanelToggle = (container: HTMLElement): HTMLElement => {
|
|
@@ -559,4 +560,70 @@ describe('ConsoleItemDetailContainer', () => {
|
|
|
559
560
|
]);
|
|
560
561
|
expect(reviewCall?.[3][0].body).not.toBe('');
|
|
561
562
|
});
|
|
563
|
+
|
|
564
|
+
it('routes delete-all-comments through onQueueAction with the correct kind and commit', async () => {
|
|
565
|
+
const operations = buildOperations();
|
|
566
|
+
const onQueueAction = jest.fn();
|
|
567
|
+
const { getByText } = render(
|
|
568
|
+
<ConsoleItemDetailContainer
|
|
569
|
+
tab="todo-by-agent"
|
|
570
|
+
item={issueItem}
|
|
571
|
+
caches={buildCaches()}
|
|
572
|
+
operations={operations}
|
|
573
|
+
statusOptions={consoleStatusOptionsFixture}
|
|
574
|
+
storyColors={consoleStoryColorsFixture}
|
|
575
|
+
storyName="TDPM Console port"
|
|
576
|
+
overlayStatus={null}
|
|
577
|
+
now={Date.parse('2026-06-19T12:00:00.000Z')}
|
|
578
|
+
onQueueAction={onQueueAction}
|
|
579
|
+
/>,
|
|
580
|
+
);
|
|
581
|
+
|
|
582
|
+
await waitFor(() => {
|
|
583
|
+
expect(getByText('⚠')).toBeInTheDocument();
|
|
584
|
+
});
|
|
585
|
+
|
|
586
|
+
fireEvent.click(getByText('⚠'));
|
|
587
|
+
fireEvent.click(getByText('Delete All Comments'));
|
|
588
|
+
|
|
589
|
+
expect(onQueueAction).toHaveBeenCalledTimes(1);
|
|
590
|
+
const input = onQueueAction.mock.calls[0][0];
|
|
591
|
+
expect(input.kind).toEqual({ type: 'delete_all_comments' });
|
|
592
|
+
expect(input.item).toBe(issueItem);
|
|
593
|
+
expect(operations.deleteAllComments).not.toHaveBeenCalled();
|
|
594
|
+
|
|
595
|
+
await input.commit();
|
|
596
|
+
expect(operations.deleteAllComments).toHaveBeenCalledWith(issueItem);
|
|
597
|
+
});
|
|
598
|
+
|
|
599
|
+
it('surfaces errors from deleteAllComments via the queue rather than swallowing them', async () => {
|
|
600
|
+
const operations = buildOperations();
|
|
601
|
+
const error = new Error('GitHub API failure');
|
|
602
|
+
(operations.deleteAllComments as jest.Mock).mockRejectedValue(error);
|
|
603
|
+
const onQueueAction = jest.fn();
|
|
604
|
+
const { getByText } = render(
|
|
605
|
+
<ConsoleItemDetailContainer
|
|
606
|
+
tab="todo-by-agent"
|
|
607
|
+
item={issueItem}
|
|
608
|
+
caches={buildCaches()}
|
|
609
|
+
operations={operations}
|
|
610
|
+
statusOptions={consoleStatusOptionsFixture}
|
|
611
|
+
storyColors={consoleStoryColorsFixture}
|
|
612
|
+
storyName="TDPM Console port"
|
|
613
|
+
overlayStatus={null}
|
|
614
|
+
now={Date.parse('2026-06-19T12:00:00.000Z')}
|
|
615
|
+
onQueueAction={onQueueAction}
|
|
616
|
+
/>,
|
|
617
|
+
);
|
|
618
|
+
|
|
619
|
+
await waitFor(() => {
|
|
620
|
+
expect(getByText('⚠')).toBeInTheDocument();
|
|
621
|
+
});
|
|
622
|
+
|
|
623
|
+
fireEvent.click(getByText('⚠'));
|
|
624
|
+
fireEvent.click(getByText('Delete All Comments'));
|
|
625
|
+
|
|
626
|
+
const input = onQueueAction.mock.calls[0][0];
|
|
627
|
+
await expect(input.commit()).rejects.toThrow('GitHub API failure');
|
|
628
|
+
});
|
|
562
629
|
});
|
|
@@ -262,6 +262,13 @@ export const ConsoleItemDetailContainer = ({
|
|
|
262
262
|
commit: () => operations.okAndMoveToAwaitingWorkspace(item, option),
|
|
263
263
|
});
|
|
264
264
|
},
|
|
265
|
+
onDeleteAllComments: () => {
|
|
266
|
+
onQueueAction({
|
|
267
|
+
kind: { type: 'delete_all_comments' },
|
|
268
|
+
item,
|
|
269
|
+
commit: () => operations.deleteAllComments(item),
|
|
270
|
+
});
|
|
271
|
+
},
|
|
265
272
|
};
|
|
266
273
|
|
|
267
274
|
const awaitingWorkspaceOption =
|
|
@@ -318,6 +325,11 @@ export const ConsoleItemDetailContainer = ({
|
|
|
318
325
|
initialDraft={initialCommentDraft}
|
|
319
326
|
onSubmit={addComment}
|
|
320
327
|
onDraftChange={onCommentDraftChange}
|
|
328
|
+
onOkAndAwaitingWorkspace={
|
|
329
|
+
awaitingWorkspaceOption !== null
|
|
330
|
+
? () => handlers.onOkAndAwaitingWorkspace(awaitingWorkspaceOption)
|
|
331
|
+
: undefined
|
|
332
|
+
}
|
|
321
333
|
onSubmitAndMoveToAwaitingWorkspace={
|
|
322
334
|
addCommentAndMoveToAwaitingWorkspace
|
|
323
335
|
}
|
|
@@ -1133,6 +1133,12 @@ body {
|
|
|
1133
1133
|
color: #79c0ff;
|
|
1134
1134
|
}
|
|
1135
1135
|
|
|
1136
|
+
.console-op-button-danger {
|
|
1137
|
+
background: #3a1518;
|
|
1138
|
+
border-color: #f85149;
|
|
1139
|
+
color: #f85149;
|
|
1140
|
+
}
|
|
1141
|
+
|
|
1136
1142
|
.console-pr-section {
|
|
1137
1143
|
border: 1px solid #30363d;
|
|
1138
1144
|
border-radius: 8px;
|