github-issue-tower-defence-management 1.112.6 → 1.114.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 +1 -1
- package/bin/adapter/entry-points/cli/index.js +1 -0
- package/bin/adapter/entry-points/cli/index.js.map +1 -1
- package/bin/adapter/entry-points/console/consoleReadApi.js +58 -2
- package/bin/adapter/entry-points/console/consoleReadApi.js.map +1 -1
- package/bin/adapter/entry-points/console/ui-dist/assets/{index-DKFbU8lr.css → index-Blb_xqIx.css} +1 -1
- package/bin/adapter/entry-points/console/ui-dist/assets/index-DQyZ8KpG.js +101 -0
- package/bin/adapter/entry-points/console/ui-dist/index.html +2 -2
- package/bin/adapter/entry-points/console/webServer.js +6 -0
- package/bin/adapter/entry-points/console/webServer.js.map +1 -1
- package/package.json +1 -1
- package/src/adapter/entry-points/cli/index.ts +5 -1
- package/src/adapter/entry-points/console/consoleReadApi.test.ts +171 -0
- package/src/adapter/entry-points/console/consoleReadApi.ts +92 -1
- package/src/adapter/entry-points/console/ui/e2e/consoleScenario.spec.ts +44 -0
- package/src/adapter/entry-points/console/ui/e2e/consoleTestHarness.ts +31 -8
- package/src/adapter/entry-points/console/ui/src/features/console/components/content/ConsoleMarkdownContent.stories.tsx +16 -0
- package/src/adapter/entry-points/console/ui/src/features/console/components/content/ConsoleMarkdownContent.test.tsx +61 -0
- package/src/adapter/entry-points/console/ui/src/features/console/components/content/ConsoleMarkdownContent.tsx +59 -5
- package/src/adapter/entry-points/console/ui/src/features/console/components/content/ConsoleReferenceLink.stories.tsx +76 -0
- package/src/adapter/entry-points/console/ui/src/features/console/components/content/ConsoleReferenceLink.test.tsx +56 -0
- package/src/adapter/entry-points/console/ui/src/features/console/components/content/ConsoleReferenceLink.tsx +39 -0
- package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsoleCommentList.tsx +4 -0
- package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsoleItemDetail.stories.tsx +64 -3
- package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsoleItemDetail.test.tsx +68 -2
- package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsoleItemDetail.tsx +21 -0
- package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsolePullRequestDetail.test.tsx +46 -0
- package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsolePullRequestDetail.tsx +12 -0
- package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsolePullRequestStatusBadges.stories.tsx +41 -0
- package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsolePullRequestStatusBadges.test.tsx +72 -0
- package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsolePullRequestStatusBadges.tsx +71 -0
- package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsoleCaches.ts +5 -0
- package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsoleItemDetailData.test.ts +21 -0
- package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsoleItemDetailData.ts +18 -0
- package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsoleReferenceLink.test.ts +48 -0
- package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsoleReferenceLink.ts +39 -0
- package/src/adapter/entry-points/console/ui/src/features/console/lib/consoleApi.test.ts +66 -2
- package/src/adapter/entry-points/console/ui/src/features/console/lib/consoleApi.ts +38 -6
- package/src/adapter/entry-points/console/ui/src/features/console/logic/references.test.ts +80 -0
- package/src/adapter/entry-points/console/ui/src/features/console/logic/references.ts +44 -0
- package/src/adapter/entry-points/console/ui/src/features/console/logic/types.ts +10 -0
- package/src/adapter/entry-points/console/ui/src/features/console/pages/ConsoleItemDetailContainer.test.tsx +10 -0
- package/src/adapter/entry-points/console/ui/src/features/console/pages/ConsoleItemDetailContainer.tsx +13 -0
- package/src/adapter/entry-points/console/ui/src/features/console/pages/ConsoleReferenceLinkContainer.tsx +25 -0
- package/src/adapter/entry-points/console/ui/src/features/console/testing/fixtures.ts +40 -0
- package/src/adapter/entry-points/console/ui/src/index.css +35 -0
- package/src/adapter/entry-points/console/ui-dist/assets/{index-DKFbU8lr.css → index-Blb_xqIx.css} +1 -1
- package/src/adapter/entry-points/console/ui-dist/assets/index-DQyZ8KpG.js +101 -0
- package/src/adapter/entry-points/console/ui-dist/index.html +2 -2
- package/src/adapter/entry-points/console/webServer.test.ts +52 -1
- package/src/adapter/entry-points/console/webServer.ts +13 -0
- package/types/adapter/entry-points/cli/index.d.ts.map +1 -1
- package/types/adapter/entry-points/console/consoleReadApi.d.ts +21 -0
- package/types/adapter/entry-points/console/consoleReadApi.d.ts.map +1 -1
- package/types/adapter/entry-points/console/webServer.d.ts +2 -1
- package/types/adapter/entry-points/console/webServer.d.ts.map +1 -1
- package/bin/adapter/entry-points/console/ui-dist/assets/index-DUygHx5u.js +0 -101
- package/src/adapter/entry-points/console/ui-dist/assets/index-DUygHx5u.js +0 -101
|
@@ -38,6 +38,52 @@ describe('ConsolePullRequestDetail', () => {
|
|
|
38
38
|
expect(getByText('27 files')).toBeInTheDocument();
|
|
39
39
|
});
|
|
40
40
|
|
|
41
|
+
it('renders the CI status badge derived from the pull request fields', () => {
|
|
42
|
+
const { getByText } = render(
|
|
43
|
+
<ConsolePullRequestDetail
|
|
44
|
+
pullRequest={pullRequest}
|
|
45
|
+
body={pullRequest.summary?.body ?? ''}
|
|
46
|
+
bodyIsLoading={false}
|
|
47
|
+
files={consoleChangedFilesFixture}
|
|
48
|
+
filesAreLoading={false}
|
|
49
|
+
filesError={null}
|
|
50
|
+
commits={consoleCommitsFixture}
|
|
51
|
+
commitsAreLoading={false}
|
|
52
|
+
commitsError={null}
|
|
53
|
+
now={now}
|
|
54
|
+
/>,
|
|
55
|
+
);
|
|
56
|
+
expect(getByText('CI passing')).toBeInTheDocument();
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it('renders failing CI, conflict and out-of-date badges for an unhealthy pull request', () => {
|
|
60
|
+
const { getByText } = render(
|
|
61
|
+
<ConsolePullRequestDetail
|
|
62
|
+
pullRequest={{
|
|
63
|
+
...pullRequest,
|
|
64
|
+
isConflicted: true,
|
|
65
|
+
isPassedAllCiJob: false,
|
|
66
|
+
isCiStateSuccess: false,
|
|
67
|
+
isBranchOutOfDate: true,
|
|
68
|
+
missingRequiredCheckNames: ['build'],
|
|
69
|
+
}}
|
|
70
|
+
body={pullRequest.summary?.body ?? ''}
|
|
71
|
+
bodyIsLoading={false}
|
|
72
|
+
files={consoleChangedFilesFixture}
|
|
73
|
+
filesAreLoading={false}
|
|
74
|
+
filesError={null}
|
|
75
|
+
commits={consoleCommitsFixture}
|
|
76
|
+
commitsAreLoading={false}
|
|
77
|
+
commitsError={null}
|
|
78
|
+
now={now}
|
|
79
|
+
/>,
|
|
80
|
+
);
|
|
81
|
+
expect(getByText('CI failing')).toBeInTheDocument();
|
|
82
|
+
expect(getByText(/missing: build/)).toBeInTheDocument();
|
|
83
|
+
expect(getByText('Conflict')).toBeInTheDocument();
|
|
84
|
+
expect(getByText('Out of date')).toBeInTheDocument();
|
|
85
|
+
});
|
|
86
|
+
|
|
41
87
|
it('renders a copy URL button for the pull request url', () => {
|
|
42
88
|
const { getByRole } = render(
|
|
43
89
|
<ConsolePullRequestDetail
|
|
@@ -4,12 +4,14 @@ import type {
|
|
|
4
4
|
ConsoleCommit,
|
|
5
5
|
ConsoleRelatedPullRequest,
|
|
6
6
|
} from '../../logic/types';
|
|
7
|
+
import type { ConsoleReferenceLinkRenderer } from '../content/ConsoleMarkdownContent';
|
|
7
8
|
import { ConsoleMarkdownContent } from '../content/ConsoleMarkdownContent';
|
|
8
9
|
import { ConsolePanel } from '../layout/ConsolePanel';
|
|
9
10
|
import { ConsoleChangedFileList } from './ConsoleChangedFileList';
|
|
10
11
|
import { ConsoleCommitList } from './ConsoleCommitList';
|
|
11
12
|
import { ConsoleCopyUrlButton } from './ConsoleCopyUrlButton';
|
|
12
13
|
import type { ConsoleAddInlineComment } from './ConsoleFileDiff';
|
|
14
|
+
import { ConsolePullRequestStatusBadges } from './ConsolePullRequestStatusBadges';
|
|
13
15
|
|
|
14
16
|
export type ConsolePullRequestSectionProps = {
|
|
15
17
|
pullRequest: ConsoleRelatedPullRequest;
|
|
@@ -23,6 +25,7 @@ export type ConsolePullRequestSectionProps = {
|
|
|
23
25
|
commitsError: string | null;
|
|
24
26
|
now: number;
|
|
25
27
|
buildImageProxyUrl?: ImageProxyUrlBuilder;
|
|
28
|
+
renderReferenceLink?: ConsoleReferenceLinkRenderer;
|
|
26
29
|
onAddInlineComment?: ConsoleAddInlineComment;
|
|
27
30
|
};
|
|
28
31
|
|
|
@@ -38,6 +41,7 @@ export const ConsolePullRequestDetail = ({
|
|
|
38
41
|
commitsError,
|
|
39
42
|
now,
|
|
40
43
|
buildImageProxyUrl,
|
|
44
|
+
renderReferenceLink,
|
|
41
45
|
onAddInlineComment,
|
|
42
46
|
}: ConsolePullRequestSectionProps) => {
|
|
43
47
|
const summary = pullRequest.summary;
|
|
@@ -59,6 +63,13 @@ export const ConsolePullRequestDetail = ({
|
|
|
59
63
|
{pullRequest.isDraft && (
|
|
60
64
|
<span className="console-pr-section-state">draft</span>
|
|
61
65
|
)}
|
|
66
|
+
<ConsolePullRequestStatusBadges
|
|
67
|
+
isConflicted={pullRequest.isConflicted}
|
|
68
|
+
isPassedAllCiJob={pullRequest.isPassedAllCiJob}
|
|
69
|
+
isCiStateSuccess={pullRequest.isCiStateSuccess}
|
|
70
|
+
isBranchOutOfDate={pullRequest.isBranchOutOfDate}
|
|
71
|
+
missingRequiredCheckNames={pullRequest.missingRequiredCheckNames}
|
|
72
|
+
/>
|
|
62
73
|
<ConsoleCopyUrlButton url={pullRequest.url} label="Copy PR URL" />
|
|
63
74
|
<div className="console-pr-statbar">
|
|
64
75
|
{pullRequest.branchName !== null && (
|
|
@@ -82,6 +93,7 @@ export const ConsolePullRequestDetail = ({
|
|
|
82
93
|
<ConsoleMarkdownContent
|
|
83
94
|
body={summary?.body ?? body}
|
|
84
95
|
buildImageProxyUrl={buildImageProxyUrl}
|
|
96
|
+
renderReferenceLink={renderReferenceLink}
|
|
85
97
|
/>
|
|
86
98
|
)}
|
|
87
99
|
</ConsolePanel>
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react-vite';
|
|
2
|
+
import { ConsolePullRequestStatusBadges } from './ConsolePullRequestStatusBadges';
|
|
3
|
+
|
|
4
|
+
const meta: Meta<typeof ConsolePullRequestStatusBadges> = {
|
|
5
|
+
title: 'Console/ConsolePullRequestStatusBadges',
|
|
6
|
+
component: ConsolePullRequestStatusBadges,
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
export default meta;
|
|
10
|
+
|
|
11
|
+
type Story = StoryObj<typeof ConsolePullRequestStatusBadges>;
|
|
12
|
+
|
|
13
|
+
export const Passing: Story = {
|
|
14
|
+
args: {
|
|
15
|
+
isConflicted: false,
|
|
16
|
+
isPassedAllCiJob: true,
|
|
17
|
+
isCiStateSuccess: true,
|
|
18
|
+
isBranchOutOfDate: false,
|
|
19
|
+
missingRequiredCheckNames: [],
|
|
20
|
+
},
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export const FailingWithMissingChecks: Story = {
|
|
24
|
+
args: {
|
|
25
|
+
isConflicted: false,
|
|
26
|
+
isPassedAllCiJob: false,
|
|
27
|
+
isCiStateSuccess: false,
|
|
28
|
+
isBranchOutOfDate: false,
|
|
29
|
+
missingRequiredCheckNames: ['build', 'test'],
|
|
30
|
+
},
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
export const ConflictedAndOutOfDate: Story = {
|
|
34
|
+
args: {
|
|
35
|
+
isConflicted: true,
|
|
36
|
+
isPassedAllCiJob: false,
|
|
37
|
+
isCiStateSuccess: false,
|
|
38
|
+
isBranchOutOfDate: true,
|
|
39
|
+
missingRequiredCheckNames: ['build', 'test'],
|
|
40
|
+
},
|
|
41
|
+
};
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { render } from '@testing-library/react';
|
|
2
|
+
import { ConsolePullRequestStatusBadges } from './ConsolePullRequestStatusBadges';
|
|
3
|
+
|
|
4
|
+
describe('ConsolePullRequestStatusBadges', () => {
|
|
5
|
+
it('renders a passing CI badge when all CI jobs passed and the CI state is success', () => {
|
|
6
|
+
const { getByText, queryByText } = render(
|
|
7
|
+
<ConsolePullRequestStatusBadges
|
|
8
|
+
isConflicted={false}
|
|
9
|
+
isPassedAllCiJob={true}
|
|
10
|
+
isCiStateSuccess={true}
|
|
11
|
+
isBranchOutOfDate={false}
|
|
12
|
+
missingRequiredCheckNames={[]}
|
|
13
|
+
/>,
|
|
14
|
+
);
|
|
15
|
+
expect(getByText('CI passing')).toBeInTheDocument();
|
|
16
|
+
expect(queryByText('Conflict')).toBeNull();
|
|
17
|
+
expect(queryByText('Out of date')).toBeNull();
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
it('renders a failing CI badge with missing required checks', () => {
|
|
21
|
+
const { getByText } = render(
|
|
22
|
+
<ConsolePullRequestStatusBadges
|
|
23
|
+
isConflicted={false}
|
|
24
|
+
isPassedAllCiJob={false}
|
|
25
|
+
isCiStateSuccess={true}
|
|
26
|
+
isBranchOutOfDate={false}
|
|
27
|
+
missingRequiredCheckNames={['build', 'test']}
|
|
28
|
+
/>,
|
|
29
|
+
);
|
|
30
|
+
expect(getByText('CI failing')).toBeInTheDocument();
|
|
31
|
+
expect(getByText(/missing: build, test/)).toBeInTheDocument();
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it('renders the conflict badge when the pull request is conflicted', () => {
|
|
35
|
+
const { getByText } = render(
|
|
36
|
+
<ConsolePullRequestStatusBadges
|
|
37
|
+
isConflicted={true}
|
|
38
|
+
isPassedAllCiJob={true}
|
|
39
|
+
isCiStateSuccess={true}
|
|
40
|
+
isBranchOutOfDate={false}
|
|
41
|
+
missingRequiredCheckNames={[]}
|
|
42
|
+
/>,
|
|
43
|
+
);
|
|
44
|
+
expect(getByText('Conflict')).toBeInTheDocument();
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it('renders the out-of-date badge when the branch is behind the base branch', () => {
|
|
48
|
+
const { getByText } = render(
|
|
49
|
+
<ConsolePullRequestStatusBadges
|
|
50
|
+
isConflicted={false}
|
|
51
|
+
isPassedAllCiJob={true}
|
|
52
|
+
isCiStateSuccess={true}
|
|
53
|
+
isBranchOutOfDate={true}
|
|
54
|
+
missingRequiredCheckNames={[]}
|
|
55
|
+
/>,
|
|
56
|
+
);
|
|
57
|
+
expect(getByText('Out of date')).toBeInTheDocument();
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
it('treats a successful CI state but unfinished jobs as failing', () => {
|
|
61
|
+
const { getByText } = render(
|
|
62
|
+
<ConsolePullRequestStatusBadges
|
|
63
|
+
isConflicted={false}
|
|
64
|
+
isPassedAllCiJob={false}
|
|
65
|
+
isCiStateSuccess={true}
|
|
66
|
+
isBranchOutOfDate={false}
|
|
67
|
+
missingRequiredCheckNames={[]}
|
|
68
|
+
/>,
|
|
69
|
+
);
|
|
70
|
+
expect(getByText('CI failing')).toBeInTheDocument();
|
|
71
|
+
});
|
|
72
|
+
});
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { colorFromEnum } from '../../logic/colors';
|
|
2
|
+
import type { ConsoleColor } from '../../logic/types';
|
|
3
|
+
|
|
4
|
+
export type ConsolePullRequestStatusBadgesProps = {
|
|
5
|
+
isConflicted: boolean;
|
|
6
|
+
isPassedAllCiJob: boolean;
|
|
7
|
+
isCiStateSuccess: boolean;
|
|
8
|
+
isBranchOutOfDate: boolean;
|
|
9
|
+
missingRequiredCheckNames: string[];
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
const badgeStyle = (color: ConsoleColor) => {
|
|
13
|
+
const palette = colorFromEnum(color);
|
|
14
|
+
return {
|
|
15
|
+
color: palette.fg,
|
|
16
|
+
borderColor: palette.border,
|
|
17
|
+
backgroundColor: palette.bg,
|
|
18
|
+
};
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export const ConsolePullRequestStatusBadges = ({
|
|
22
|
+
isConflicted,
|
|
23
|
+
isPassedAllCiJob,
|
|
24
|
+
isCiStateSuccess,
|
|
25
|
+
isBranchOutOfDate,
|
|
26
|
+
missingRequiredCheckNames,
|
|
27
|
+
}: ConsolePullRequestStatusBadgesProps) => {
|
|
28
|
+
const ciPassing = isPassedAllCiJob && isCiStateSuccess;
|
|
29
|
+
const ciColor: ConsoleColor = ciPassing ? 'GREEN' : 'RED';
|
|
30
|
+
const ciLabel = ciPassing ? 'CI passing' : 'CI failing';
|
|
31
|
+
const missingChecksLabel =
|
|
32
|
+
!ciPassing && missingRequiredCheckNames.length > 0
|
|
33
|
+
? `missing: ${missingRequiredCheckNames.join(', ')}`
|
|
34
|
+
: null;
|
|
35
|
+
|
|
36
|
+
return (
|
|
37
|
+
<span className="console-detail-pr-status">
|
|
38
|
+
<span
|
|
39
|
+
className="console-detail-status-chip console-detail-ci-chip"
|
|
40
|
+
style={badgeStyle(ciColor)}
|
|
41
|
+
title={missingChecksLabel ?? ciLabel}
|
|
42
|
+
>
|
|
43
|
+
{ciLabel}
|
|
44
|
+
{missingChecksLabel !== null && (
|
|
45
|
+
<span className="console-detail-ci-missing">
|
|
46
|
+
{' '}
|
|
47
|
+
({missingChecksLabel})
|
|
48
|
+
</span>
|
|
49
|
+
)}
|
|
50
|
+
</span>
|
|
51
|
+
{isConflicted && (
|
|
52
|
+
<span
|
|
53
|
+
className="console-detail-status-chip console-detail-conflict-chip"
|
|
54
|
+
style={badgeStyle('RED')}
|
|
55
|
+
title="This pull request has merge conflicts"
|
|
56
|
+
>
|
|
57
|
+
Conflict
|
|
58
|
+
</span>
|
|
59
|
+
)}
|
|
60
|
+
{isBranchOutOfDate && (
|
|
61
|
+
<span
|
|
62
|
+
className="console-detail-status-chip console-detail-outofdate-chip"
|
|
63
|
+
style={badgeStyle('YELLOW')}
|
|
64
|
+
title="This branch is out of date with the base branch"
|
|
65
|
+
>
|
|
66
|
+
Out of date
|
|
67
|
+
</span>
|
|
68
|
+
)}
|
|
69
|
+
</span>
|
|
70
|
+
);
|
|
71
|
+
};
|
|
@@ -9,6 +9,7 @@ import type {
|
|
|
9
9
|
ConsoleComment,
|
|
10
10
|
ConsoleCommit,
|
|
11
11
|
ConsoleIssueState,
|
|
12
|
+
ConsolePullRequestStatus,
|
|
12
13
|
ConsoleRelatedPullRequest,
|
|
13
14
|
} from '../logic/types';
|
|
14
15
|
import { useConsoleToken } from './useConsoleToken';
|
|
@@ -20,6 +21,7 @@ export type ConsoleCaches = {
|
|
|
20
21
|
commits: ResourceCache<ConsoleCommit[]>;
|
|
21
22
|
relatedPrs: ResourceCache<ConsoleRelatedPullRequest[]>;
|
|
22
23
|
state: ResourceCache<ConsoleIssueState>;
|
|
24
|
+
prStatus: ResourceCache<ConsolePullRequestStatus>;
|
|
23
25
|
client: ConsoleApiClient;
|
|
24
26
|
};
|
|
25
27
|
|
|
@@ -37,6 +39,9 @@ export const useConsoleCaches = (): ConsoleCaches => {
|
|
|
37
39
|
client.fetchRelatedPrs,
|
|
38
40
|
),
|
|
39
41
|
state: new ResourceCache<ConsoleIssueState>(client.fetchIssueState),
|
|
42
|
+
prStatus: new ResourceCache<ConsolePullRequestStatus>(
|
|
43
|
+
client.fetchPullRequestStatus,
|
|
44
|
+
),
|
|
40
45
|
};
|
|
41
46
|
}, [appendToken]);
|
|
42
47
|
};
|
|
@@ -6,6 +6,7 @@ import type {
|
|
|
6
6
|
ConsoleCommit,
|
|
7
7
|
ConsoleIssueState,
|
|
8
8
|
ConsoleListItem,
|
|
9
|
+
ConsolePullRequestStatus,
|
|
9
10
|
ConsoleRelatedPullRequest,
|
|
10
11
|
} from '../logic/types';
|
|
11
12
|
import type { ConsoleCaches } from './useConsoleCaches';
|
|
@@ -62,6 +63,15 @@ const buildCaches = (related: ConsoleRelatedPullRequest[]): ConsoleCaches => {
|
|
|
62
63
|
state: 'open',
|
|
63
64
|
merged: false,
|
|
64
65
|
isPullRequest: true,
|
|
66
|
+
title: 'Console item detail fixture title',
|
|
67
|
+
}),
|
|
68
|
+
fetchPullRequestStatus: async (): Promise<ConsolePullRequestStatus> => ({
|
|
69
|
+
found: true,
|
|
70
|
+
isConflicted: true,
|
|
71
|
+
isPassedAllCiJob: false,
|
|
72
|
+
isCiStateSuccess: false,
|
|
73
|
+
isBranchOutOfDate: true,
|
|
74
|
+
missingRequiredCheckNames: ['build'],
|
|
65
75
|
}),
|
|
66
76
|
};
|
|
67
77
|
return {
|
|
@@ -72,6 +82,7 @@ const buildCaches = (related: ConsoleRelatedPullRequest[]): ConsoleCaches => {
|
|
|
72
82
|
commits: new ResourceCache(client.fetchPrCommits),
|
|
73
83
|
relatedPrs: new ResourceCache(client.fetchRelatedPrs),
|
|
74
84
|
state: new ResourceCache(client.fetchIssueState),
|
|
85
|
+
prStatus: new ResourceCache(client.fetchPullRequestStatus),
|
|
75
86
|
};
|
|
76
87
|
};
|
|
77
88
|
|
|
@@ -85,9 +96,19 @@ describe('useConsoleItemDetailData', () => {
|
|
|
85
96
|
expect(result.current.body).toBe('body');
|
|
86
97
|
expect(result.current.files.length).toBe(1);
|
|
87
98
|
expect(result.current.commits.length).toBe(1);
|
|
99
|
+
expect(result.current.pullRequestStatus?.found).toBe(true);
|
|
100
|
+
expect(result.current.pullRequestStatus?.isConflicted).toBe(true);
|
|
88
101
|
});
|
|
89
102
|
});
|
|
90
103
|
|
|
104
|
+
it('does not expose pull request status for an issue item', () => {
|
|
105
|
+
const caches = buildCaches([]);
|
|
106
|
+
const { result } = renderHook(() =>
|
|
107
|
+
useConsoleItemDetailData(caches, issueItem),
|
|
108
|
+
);
|
|
109
|
+
expect(result.current.pullRequestStatus).toBeNull();
|
|
110
|
+
});
|
|
111
|
+
|
|
91
112
|
it('loads related pull request views for an issue item', async () => {
|
|
92
113
|
const related: ConsoleRelatedPullRequest[] = [
|
|
93
114
|
{
|
package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsoleItemDetailData.ts
CHANGED
|
@@ -6,6 +6,7 @@ import type {
|
|
|
6
6
|
ConsoleCommit,
|
|
7
7
|
ConsoleIssueState,
|
|
8
8
|
ConsoleListItem,
|
|
9
|
+
ConsolePullRequestStatus,
|
|
9
10
|
ConsoleRelatedPullRequest,
|
|
10
11
|
} from '../logic/types';
|
|
11
12
|
import type { ConsoleCaches } from './useConsoleCaches';
|
|
@@ -19,6 +20,15 @@ const DEFAULT_STATE: ConsoleIssueState = {
|
|
|
19
20
|
state: 'open',
|
|
20
21
|
merged: false,
|
|
21
22
|
isPullRequest: false,
|
|
23
|
+
title: '',
|
|
24
|
+
};
|
|
25
|
+
const DEFAULT_PR_STATUS: ConsolePullRequestStatus = {
|
|
26
|
+
found: false,
|
|
27
|
+
isConflicted: false,
|
|
28
|
+
isPassedAllCiJob: false,
|
|
29
|
+
isCiStateSuccess: false,
|
|
30
|
+
isBranchOutOfDate: false,
|
|
31
|
+
missingRequiredCheckNames: [],
|
|
22
32
|
};
|
|
23
33
|
|
|
24
34
|
export type ConsoleItemDetailData = {
|
|
@@ -35,6 +45,7 @@ export type ConsoleItemDetailData = {
|
|
|
35
45
|
commits: ConsoleCommit[];
|
|
36
46
|
commitsAreLoading: boolean;
|
|
37
47
|
commitsError: string | null;
|
|
48
|
+
pullRequestStatus: ConsolePullRequestStatus | null;
|
|
38
49
|
relatedPullRequests: ConsoleRelatedPullRequestView[];
|
|
39
50
|
};
|
|
40
51
|
|
|
@@ -66,6 +77,12 @@ export const useConsoleItemDetailData = (
|
|
|
66
77
|
isPr ? url : null,
|
|
67
78
|
EMPTY_COMMITS,
|
|
68
79
|
);
|
|
80
|
+
const prStatus = useConsoleResource(
|
|
81
|
+
caches.prStatus,
|
|
82
|
+
isPr ? key : null,
|
|
83
|
+
isPr ? url : null,
|
|
84
|
+
DEFAULT_PR_STATUS,
|
|
85
|
+
);
|
|
69
86
|
const relatedPrs = useConsoleResource(
|
|
70
87
|
caches.relatedPrs,
|
|
71
88
|
!isPr ? key : null,
|
|
@@ -162,6 +179,7 @@ export const useConsoleItemDetailData = (
|
|
|
162
179
|
commits: commits.data,
|
|
163
180
|
commitsAreLoading: commits.isLoading,
|
|
164
181
|
commitsError: commits.error,
|
|
182
|
+
pullRequestStatus: isPr ? prStatus.data : null,
|
|
165
183
|
relatedPullRequests: relatedViews,
|
|
166
184
|
};
|
|
167
185
|
};
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { renderHook, waitFor } from '@testing-library/react';
|
|
2
|
+
import { ResourceCache } from '../lib/resourceCache';
|
|
3
|
+
import type { ConsoleIssueState } from '../logic/types';
|
|
4
|
+
import { useConsoleReferenceLink } from './useConsoleReferenceLink';
|
|
5
|
+
|
|
6
|
+
const buildState = (title: string): ConsoleIssueState => ({
|
|
7
|
+
state: 'open',
|
|
8
|
+
merged: false,
|
|
9
|
+
isPullRequest: true,
|
|
10
|
+
title,
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
describe('useConsoleReferenceLink', () => {
|
|
14
|
+
const url = 'https://github.com/octo/repo/pull/7';
|
|
15
|
+
|
|
16
|
+
it('returns null then the resolved state', async () => {
|
|
17
|
+
const cache = new ResourceCache<ConsoleIssueState>(async () =>
|
|
18
|
+
buildState('Decorate links'),
|
|
19
|
+
);
|
|
20
|
+
const { result } = renderHook(() => useConsoleReferenceLink(cache, url));
|
|
21
|
+
expect(result.current).toBeNull();
|
|
22
|
+
await waitFor(() => {
|
|
23
|
+
expect(result.current?.title).toBe('Decorate links');
|
|
24
|
+
});
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
it('returns null when the fetch fails', async () => {
|
|
28
|
+
const cache = new ResourceCache<ConsoleIssueState>(async () => {
|
|
29
|
+
throw new Error('boom');
|
|
30
|
+
});
|
|
31
|
+
const { result } = renderHook(() => useConsoleReferenceLink(cache, url));
|
|
32
|
+
await waitFor(() => {
|
|
33
|
+
expect(cache.has(url)).toBe(false);
|
|
34
|
+
});
|
|
35
|
+
expect(result.current).toBeNull();
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it('reuses an already cached value without refetching', () => {
|
|
39
|
+
const fetcher = jest.fn(async () => buildState('cached'));
|
|
40
|
+
const cache = new ResourceCache<ConsoleIssueState>(fetcher);
|
|
41
|
+
cache.load(url, url);
|
|
42
|
+
return cache.load(url, url).then(() => {
|
|
43
|
+
const { result } = renderHook(() => useConsoleReferenceLink(cache, url));
|
|
44
|
+
expect(result.current?.title).toBe('cached');
|
|
45
|
+
expect(fetcher).toHaveBeenCalledTimes(1);
|
|
46
|
+
});
|
|
47
|
+
});
|
|
48
|
+
});
|
package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsoleReferenceLink.ts
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { useEffect, useState } from 'react';
|
|
2
|
+
import type { ResourceCache } from '../lib/resourceCache';
|
|
3
|
+
import type { ConsoleIssueState } from '../logic/types';
|
|
4
|
+
|
|
5
|
+
export const useConsoleReferenceLink = (
|
|
6
|
+
cache: ResourceCache<ConsoleIssueState>,
|
|
7
|
+
url: string,
|
|
8
|
+
): ConsoleIssueState | null => {
|
|
9
|
+
const [state, setState] = useState<ConsoleIssueState | null>(
|
|
10
|
+
() => cache.peek(url) ?? null,
|
|
11
|
+
);
|
|
12
|
+
|
|
13
|
+
useEffect(() => {
|
|
14
|
+
const cached = cache.peek(url);
|
|
15
|
+
if (cached !== undefined) {
|
|
16
|
+
setState(cached);
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
let cancelled = false;
|
|
20
|
+
setState(null);
|
|
21
|
+
cache
|
|
22
|
+
.load(url, url)
|
|
23
|
+
.then((value) => {
|
|
24
|
+
if (!cancelled) {
|
|
25
|
+
setState(value);
|
|
26
|
+
}
|
|
27
|
+
})
|
|
28
|
+
.catch(() => {
|
|
29
|
+
if (!cancelled) {
|
|
30
|
+
setState(null);
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
return () => {
|
|
34
|
+
cancelled = true;
|
|
35
|
+
};
|
|
36
|
+
}, [cache, url]);
|
|
37
|
+
|
|
38
|
+
return state;
|
|
39
|
+
};
|
|
@@ -48,6 +48,7 @@ describe('createConsoleApiClient', () => {
|
|
|
48
48
|
readers.push((url) => client.fetchPrCommits(url));
|
|
49
49
|
readers.push((url) => client.fetchRelatedPrs(url));
|
|
50
50
|
readers.push((url) => client.fetchIssueState(url));
|
|
51
|
+
readers.push((url) => client.fetchPullRequestStatus(url));
|
|
51
52
|
const expectedPaths = [
|
|
52
53
|
'/api/itembody',
|
|
53
54
|
'/api/comments',
|
|
@@ -55,6 +56,7 @@ describe('createConsoleApiClient', () => {
|
|
|
55
56
|
'/api/prcommits',
|
|
56
57
|
'/api/relatedprs',
|
|
57
58
|
'/api/issuetitle',
|
|
59
|
+
'/api/pullrequeststatus',
|
|
58
60
|
];
|
|
59
61
|
for (let index = 0; index < readers.length; index += 1) {
|
|
60
62
|
const fetchMock = mockFetchOnce({});
|
|
@@ -103,11 +105,34 @@ describe('createConsoleApiClient', () => {
|
|
|
103
105
|
});
|
|
104
106
|
|
|
105
107
|
it('parses the issue state', async () => {
|
|
106
|
-
mockFetchOnce({
|
|
108
|
+
mockFetchOnce({
|
|
109
|
+
state: 'closed',
|
|
110
|
+
merged: true,
|
|
111
|
+
isPullRequest: true,
|
|
112
|
+
title: 'Decorate PR links',
|
|
113
|
+
});
|
|
107
114
|
const client = createConsoleApiClient(appendToken);
|
|
108
115
|
expect(
|
|
109
116
|
await client.fetchIssueState('https://github.com/o/r/pull/1'),
|
|
110
|
-
).toEqual({
|
|
117
|
+
).toEqual({
|
|
118
|
+
state: 'closed',
|
|
119
|
+
merged: true,
|
|
120
|
+
isPullRequest: true,
|
|
121
|
+
title: 'Decorate PR links',
|
|
122
|
+
});
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
it('defaults the title to an empty string when absent', async () => {
|
|
126
|
+
mockFetchOnce({ state: 'open', merged: false, isPullRequest: false });
|
|
127
|
+
const client = createConsoleApiClient(appendToken);
|
|
128
|
+
expect(
|
|
129
|
+
await client.fetchIssueState('https://github.com/o/r/issues/1'),
|
|
130
|
+
).toEqual({
|
|
131
|
+
state: 'open',
|
|
132
|
+
merged: false,
|
|
133
|
+
isPullRequest: false,
|
|
134
|
+
title: '',
|
|
135
|
+
});
|
|
111
136
|
});
|
|
112
137
|
|
|
113
138
|
it('parses pull request commits', async () => {
|
|
@@ -161,6 +186,45 @@ describe('createConsoleApiClient', () => {
|
|
|
161
186
|
expect(related[0].missingRequiredCheckNames).toEqual(['build']);
|
|
162
187
|
});
|
|
163
188
|
|
|
189
|
+
it('parses the pull request status when found', async () => {
|
|
190
|
+
mockFetchOnce({
|
|
191
|
+
found: true,
|
|
192
|
+
status: {
|
|
193
|
+
isConflicted: true,
|
|
194
|
+
isPassedAllCiJob: false,
|
|
195
|
+
isCiStateSuccess: false,
|
|
196
|
+
isBranchOutOfDate: true,
|
|
197
|
+
missingRequiredCheckNames: ['build', 'test'],
|
|
198
|
+
},
|
|
199
|
+
});
|
|
200
|
+
const client = createConsoleApiClient(appendToken);
|
|
201
|
+
expect(
|
|
202
|
+
await client.fetchPullRequestStatus('https://github.com/o/r/pull/1'),
|
|
203
|
+
).toEqual({
|
|
204
|
+
found: true,
|
|
205
|
+
isConflicted: true,
|
|
206
|
+
isPassedAllCiJob: false,
|
|
207
|
+
isCiStateSuccess: false,
|
|
208
|
+
isBranchOutOfDate: true,
|
|
209
|
+
missingRequiredCheckNames: ['build', 'test'],
|
|
210
|
+
});
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
it('returns a not-found pull request status when the server reports none', async () => {
|
|
214
|
+
mockFetchOnce({ found: false, status: null });
|
|
215
|
+
const client = createConsoleApiClient(appendToken);
|
|
216
|
+
expect(
|
|
217
|
+
await client.fetchPullRequestStatus('https://github.com/o/r/pull/1'),
|
|
218
|
+
).toEqual({
|
|
219
|
+
found: false,
|
|
220
|
+
isConflicted: false,
|
|
221
|
+
isPassedAllCiJob: false,
|
|
222
|
+
isCiStateSuccess: false,
|
|
223
|
+
isBranchOutOfDate: false,
|
|
224
|
+
missingRequiredCheckNames: [],
|
|
225
|
+
});
|
|
226
|
+
});
|
|
227
|
+
|
|
164
228
|
it('throws on a non-ok response', async () => {
|
|
165
229
|
mockFetchOnce({}, false);
|
|
166
230
|
const client = createConsoleApiClient(appendToken);
|