mirror of
https://github.com/stablyai/orca.git
synced 2026-09-22 16:02:32 +00:00
fix(github): complete PR reactions for CodeRabbit reviews (#13456)
* feat(github): add PR comment reaction controls * feat(github): add full PR comment reaction picker * fix(github): cover all reactable PR comment paths * fix(github): reconcile comment reactions with main * fix(github): preserve focus on failed reaction removal
This commit is contained in:
@@ -4044,6 +4044,60 @@ describe('GitHub GraphQL rate-limit guard', () => {
|
||||
expect(noteRateLimitSpendMock).not.toHaveBeenCalledWith('graphql')
|
||||
})
|
||||
|
||||
it('maps review summary reaction subjects from GraphQL', async () => {
|
||||
getOwnerRepoMock.mockResolvedValueOnce({ owner: 'acme', repo: 'widgets' })
|
||||
ghExecFileAsyncMock
|
||||
.mockResolvedValueOnce({
|
||||
stdout: JSON.stringify({
|
||||
data: {
|
||||
repository: {
|
||||
pullRequest: {
|
||||
reviewThreads: { nodes: [] },
|
||||
comments: { nodes: [] },
|
||||
reviews: {
|
||||
nodes: [
|
||||
{
|
||||
id: 'PRR_44',
|
||||
databaseId: 44,
|
||||
author: {
|
||||
__typename: 'Bot',
|
||||
login: 'coderabbitai',
|
||||
avatarUrl: 'https://avatar'
|
||||
},
|
||||
body: 'Automated review summary',
|
||||
createdAt: '2026-04-01T00:00:00Z',
|
||||
url: 'https://github.com/acme/widgets/pull/7#pullrequestreview-44',
|
||||
reactionGroups: [
|
||||
{
|
||||
content: 'ROCKET',
|
||||
viewerHasReacted: true,
|
||||
reactors: { totalCount: 2 }
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
.mockResolvedValueOnce({ stdout: '[]' })
|
||||
.mockResolvedValueOnce({ stdout: '[]' })
|
||||
|
||||
await expect(getPRComments('/repo-root', 7)).resolves.toEqual([
|
||||
expect.objectContaining({
|
||||
id: 44,
|
||||
reactionSubjectId: 'PRR_44',
|
||||
isBot: true,
|
||||
reactions: [{ content: 'rocket', count: 2, viewerHasReacted: true }]
|
||||
})
|
||||
])
|
||||
expect(ghExecFileAsyncMock.mock.calls[0]?.[0]).toEqual(
|
||||
expect.arrayContaining([expect.stringContaining('reviews(first: 100)')])
|
||||
)
|
||||
})
|
||||
|
||||
it('uses explicit PR repo for comments when a fork PR is discovered', async () => {
|
||||
rateLimitGuardMock.mockImplementation(((bucket: string) =>
|
||||
bucket === 'graphql'
|
||||
|
||||
@@ -4157,6 +4157,23 @@ query($owner: String!, $repo: String!, $pr: Int!) {
|
||||
}
|
||||
}
|
||||
}
|
||||
reviews(first: 100) {
|
||||
nodes {
|
||||
id
|
||||
databaseId
|
||||
author { __typename login avatarUrl(size: 48) }
|
||||
body
|
||||
createdAt
|
||||
url
|
||||
reactionGroups {
|
||||
content
|
||||
viewerHasReacted
|
||||
reactors {
|
||||
totalCount
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}`
|
||||
@@ -4284,6 +4301,7 @@ export async function getPRComments(
|
||||
url: string
|
||||
reactionGroups?: GitHubGraphQLReactionGroup[] | null
|
||||
}
|
||||
let graphQLReviewSummaries: PRComment[] | undefined
|
||||
const reviewComments: PRComment[] = []
|
||||
if (threadsResult.status === 'fulfilled' && threadsResult.value) {
|
||||
const threadsData = JSON.parse(threadsResult.value.stdout) as {
|
||||
@@ -4292,6 +4310,7 @@ export async function getPRComments(
|
||||
pullRequest: {
|
||||
reviewThreads: { nodes: GQLThread[] }
|
||||
comments?: { nodes: GQLIssueComment[] }
|
||||
reviews?: { nodes: GQLIssueComment[] }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4313,6 +4332,21 @@ export async function getPRComments(
|
||||
if (graphQLIssueComments.length > 0) {
|
||||
issueComments = graphQLIssueComments
|
||||
}
|
||||
graphQLReviewSummaries = (pullRequest.reviews?.nodes ?? [])
|
||||
.filter((review) => review.body?.trim())
|
||||
.map(
|
||||
(review): PRComment => ({
|
||||
id: review.databaseId,
|
||||
author: review.author?.login ?? 'ghost',
|
||||
authorAvatarUrl: review.author?.avatarUrl ?? '',
|
||||
body: review.body,
|
||||
createdAt: review.createdAt,
|
||||
url: review.url,
|
||||
isBot: review.author?.__typename === 'Bot',
|
||||
reactionSubjectId: review.id,
|
||||
reactions: mapGraphQLReactionGroups(review.reactionGroups)
|
||||
})
|
||||
)
|
||||
|
||||
const threads = pullRequest.reviewThreads.nodes
|
||||
for (const thread of threads) {
|
||||
@@ -4353,7 +4387,9 @@ export async function getPRComments(
|
||||
html_url: string
|
||||
}
|
||||
let reviewSummaries: PRComment[] = []
|
||||
if (reviewsResult.status === 'fulfilled') {
|
||||
if (graphQLReviewSummaries) {
|
||||
reviewSummaries = graphQLReviewSummaries
|
||||
} else if (reviewsResult.status === 'fulfilled') {
|
||||
reviewSummaries = (JSON.parse(reviewsResult.value.stdout) as RESTReview[])
|
||||
.filter((r) => r.body?.trim())
|
||||
.map(
|
||||
|
||||
Reference in New Issue
Block a user