From 831c394797e1b6fb0d290b08758c3bdae0496742 Mon Sep 17 00:00:00 2001 From: Mark Xian Date: Sat, 24 Oct 2026 10:20:05 +0800 Subject: [PATCH] fix(gitlab): label hosted reviews per provider in the space-manager panel (#7655) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The workspace space-manager rendered every hosted review as "PR #N", so a GitLab merge request showed as "PR #5" instead of "MR !5". The cached review already carries its provider (HostedReviewInfo.provider) — the panel's local input type just narrowed it away. Thread provider through and format via the same localizedHostedReviewCopy / '!' vs '#' helpers the rest of the app uses. Extract the label formatting into hosted-review-label.ts (a pure module, away from the panel's heavy React/store imports) so it can be unit-tested directly, and add hosted-review-label.test.ts covering github / gitlab / azure-devops / gitea and the status suffix. linkedPR stays "PR #N" — it is the GitHub-specific workspace field, distinct from linkedGitLabMR. Co-authored-by: Claude Opus 4.8 (1M context) Co-authored-by: Neil <4138956+nwparker@users.noreply.github.com> Co-authored-by: Neil --- .../status-bar/hosted-review-label.test.ts | 39 +++++++++++++++++++ .../status-bar/hosted-review-label.ts | 23 +++++++++++ .../workspace-space-decision-details.ts | 9 +---- .../workspace-space-presentation.test.ts | 2 +- 4 files changed, 65 insertions(+), 8 deletions(-) create mode 100644 src/renderer/src/components/status-bar/hosted-review-label.test.ts create mode 100644 src/renderer/src/components/status-bar/hosted-review-label.ts diff --git a/src/renderer/src/components/status-bar/hosted-review-label.test.ts b/src/renderer/src/components/status-bar/hosted-review-label.test.ts new file mode 100644 index 00000000000..558245b330f --- /dev/null +++ b/src/renderer/src/components/status-bar/hosted-review-label.test.ts @@ -0,0 +1,39 @@ +import { describe, expect, it } from 'vitest' +import { formatHostedReviewLabel } from './hosted-review-label' + +describe('formatHostedReviewLabel', () => { + it('labels a GitLab review as "MR !N" — not "PR #N"', () => { + expect( + formatHostedReviewLabel({ provider: 'gitlab', number: 5, state: 'open', status: 'none' }) + ).toBe('MR !5 Open') + }) + + it.each([ + ['github' as const, 'PR #7 Open'], + ['azure-devops' as const, 'PR #7 Open'], + ['gitea' as const, 'PR #7 Open'], + ['bitbucket' as const, 'PR #7 Open'] + ])('uses the "PR #" form and correct copy for %s', (provider, expected) => { + expect(formatHostedReviewLabel({ provider, number: 7, state: 'open', status: 'none' })).toBe( + expected + ) + }) + + it('keeps the GitHub label for older cached reviews without a provider', () => { + expect(formatHostedReviewLabel({ number: 42, state: 'open', status: 'none' })).toBe( + 'PR #42 Open' + ) + }) + + it('appends a non-"none" status suffix', () => { + expect( + formatHostedReviewLabel({ provider: 'gitlab', number: 5, state: 'open', status: 'draft' }) + ).toBe('MR !5 Open, draft') + }) + + it('omits the suffix when status is "none" or empty', () => { + expect( + formatHostedReviewLabel({ provider: 'github', number: 5, state: 'closed', status: '' }) + ).toBe('PR #5 Closed') + }) +}) diff --git a/src/renderer/src/components/status-bar/hosted-review-label.ts b/src/renderer/src/components/status-bar/hosted-review-label.ts new file mode 100644 index 00000000000..90ee7dd7726 --- /dev/null +++ b/src/renderer/src/components/status-bar/hosted-review-label.ts @@ -0,0 +1,23 @@ +import type { HostedReviewProvider } from '../../../../shared/hosted-review' +import { + localizedHostedReviewCopy, + resolveSupportedHostedReviewCopyProvider +} from '@/i18n/hosted-review-localized-copy' + +function formatReviewState(state: string): string { + return state.charAt(0).toUpperCase() + state.slice(1) +} + +export function formatHostedReviewLabel(review: { + provider?: HostedReviewProvider + number: number + state: string + status: string +}): string { + const { shortLabel } = localizedHostedReviewCopy( + resolveSupportedHostedReviewCopyProvider(review.provider) + ) + const symbol = review.provider === 'gitlab' ? '!' : '#' + const statusSuffix = review.status && review.status !== 'none' ? `, ${review.status}` : '' + return `${shortLabel} ${symbol}${review.number} ${formatReviewState(review.state)}${statusSuffix}` +} diff --git a/src/renderer/src/components/status-bar/workspace-space-decision-details.ts b/src/renderer/src/components/status-bar/workspace-space-decision-details.ts index b85df924934..1a4bac8b572 100644 --- a/src/renderer/src/components/status-bar/workspace-space-decision-details.ts +++ b/src/renderer/src/components/status-bar/workspace-space-decision-details.ts @@ -16,6 +16,7 @@ import { issueCacheKey as getIssueCacheKey } from '../../store/github/cache-iden import { countWorkspaceSpaceActiveAgents } from './workspace-space-presentation' import { getWorkspaceSpaceWorktreeIdentity } from './workspace-space-delete-selection' import { getWorkspaceSpaceBranchLabel } from './workspace-space-format' +import { formatHostedReviewLabel } from './hosted-review-label' export type WorkspaceDecisionDetails = { isActive: boolean @@ -78,10 +79,6 @@ export function pluralize(count: number, singular: string, plural = `${singular} return `${count} ${count === 1 ? singular : plural}` } -function formatReviewState(state: string): string { - return state.charAt(0).toUpperCase() + state.slice(1) -} - function countLiveTerminals( tabs: readonly TerminalTab[], ptyIdsByTabId: Record @@ -156,9 +153,7 @@ export function getWorkspaceDecisionDetails( const linkedPR = workspaceRecord?.linkedPR ?? null const reviewLabel = hostedReview !== undefined && hostedReview !== null - ? `PR #${hostedReview.number} ${formatReviewState(hostedReview.state)}${ - hostedReview.status && hostedReview.status !== 'none' ? `, ${hostedReview.status}` : '' - }` + ? formatHostedReviewLabel(hostedReview) : linkedPR ? `PR #${linkedPR}` : null diff --git a/src/renderer/src/components/status-bar/workspace-space-presentation.test.ts b/src/renderer/src/components/status-bar/workspace-space-presentation.test.ts index c9b212a2320..8e5b7df9d21 100644 --- a/src/renderer/src/components/status-bar/workspace-space-presentation.test.ts +++ b/src/renderer/src/components/status-bar/workspace-space-presentation.test.ts @@ -564,7 +564,7 @@ describe('workspace space presentation helpers', () => { ) expect(explicit.reviewLabel).toBe('PR #12 Open, success') - expect(gitLab.reviewLabel).toBe('PR #12 Open, success') + expect(gitLab.reviewLabel).toBe('MR !12 Open, success') }) it('counts migration-unsupported agent entries by worktree id', () => {