mirror of
https://github.com/stablyai/orca.git
synced 2026-10-31 00:57:45 +00:00
test: remove 32 low-value cases and five suites (#34346)
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
This commit is contained in:
@@ -7394,7 +7394,6 @@
|
||||
"src/renderer/src/components/diff-comments/use-visible-worktree-diff-comments.test.ts": 198,
|
||||
"src/renderer/src/components/diff-comments/useDiffCommentDecorator.lifecycle.test.tsx": 1231,
|
||||
"src/renderer/src/components/diff-comments/useDiffCommentDecorator.range-selection.test.tsx": 3422,
|
||||
"src/renderer/src/components/editor/ChangesModeView.test.tsx": 385,
|
||||
"src/renderer/src/components/editor/CheckRunCopyButton.test.tsx": 343,
|
||||
"src/renderer/src/components/editor/CheckRunDetailsPanel.captured-failure.test.tsx": 1706,
|
||||
"src/renderer/src/components/editor/CheckRunDetailsPanel.copy.test.tsx": 1825,
|
||||
@@ -7411,7 +7410,6 @@
|
||||
"src/renderer/src/components/editor/EditorFileLoadErrorView.test.tsx": 442,
|
||||
"src/renderer/src/components/editor/EditorFileLocationMenuItems.test.tsx": 468,
|
||||
"src/renderer/src/components/editor/EditorPanel.markdown-classification-memoization.test.tsx": 1989,
|
||||
"src/renderer/src/components/editor/EditorPanel.markdown-review-tools-setting.test.tsx": 1495,
|
||||
"src/renderer/src/components/editor/EditorPanelActionsMenu.test.tsx": 102,
|
||||
"src/renderer/src/components/editor/EditorPanelHeader.test.tsx": 1416,
|
||||
"src/renderer/src/components/editor/EditorPanelHeaderPath.test.tsx": 1703,
|
||||
@@ -8359,7 +8357,6 @@
|
||||
"src/renderer/src/components/right-sidebar/AiVaultSessionVirtualList.measurement.test.tsx": 2554,
|
||||
"src/renderer/src/components/right-sidebar/AiVaultShowMoreSessionsRow.test.tsx": 369,
|
||||
"src/renderer/src/components/right-sidebar/AiVaultVirtualRow.test.tsx": 2253,
|
||||
"src/renderer/src/components/right-sidebar/ChecksPanel.review-header.test.tsx": 575,
|
||||
"src/renderer/src/components/right-sidebar/ChecksPanel.updated-at-metadata.test.tsx": 243,
|
||||
"src/renderer/src/components/right-sidebar/CommitArea.chevron-spinner.test.tsx": 1564,
|
||||
"src/renderer/src/components/right-sidebar/CommitArea.generate.test.tsx": 1781,
|
||||
@@ -8387,7 +8384,6 @@
|
||||
"src/renderer/src/components/right-sidebar/SourceControl.commit-failure-recovery.test.ts": 68,
|
||||
"src/renderer/src/components/right-sidebar/SourceControl.commit-generation-records.test.ts": 30,
|
||||
"src/renderer/src/components/right-sidebar/SourceControl.compare-summary.test.ts": 122,
|
||||
"src/renderer/src/components/right-sidebar/SourceControl.hosted-review-header-link.test.tsx": 78,
|
||||
"src/renderer/src/components/right-sidebar/SourceControl.open-file-highlight.test.tsx": 2978,
|
||||
"src/renderer/src/components/right-sidebar/SourceControl.pr-generation-records.test.ts": 125,
|
||||
"src/renderer/src/components/right-sidebar/SourceControl.preview-open.test.tsx": 3358,
|
||||
@@ -8700,7 +8696,6 @@
|
||||
"src/renderer/src/components/settings/HostAccountSignInPrompt.test.tsx": 158,
|
||||
"src/renderer/src/components/settings/HostAgentLaunchSettings.test.tsx": 4184,
|
||||
"src/renderer/src/components/settings/KagiSessionLinkForm.test.ts": 193,
|
||||
"src/renderer/src/components/settings/LinearAgentSkillGuide.test.tsx": 220,
|
||||
"src/renderer/src/components/settings/LinearAgentSkillPane.test.tsx": 675,
|
||||
"src/renderer/src/components/settings/LocalNetworkConnectionTest.test.tsx": 556,
|
||||
"src/renderer/src/components/settings/MachineNameField.test.tsx": 481,
|
||||
|
||||
@@ -48,30 +48,6 @@ afterEach(() => {
|
||||
})
|
||||
|
||||
describe('useAgentBucketCounts', () => {
|
||||
it('includes folder workspaces in the count snapshot inputs', () => {
|
||||
mocks.buildDashboardBucketCounts.mockImplementation(
|
||||
(state: { folderWorkspaces?: unknown[] }) => ({
|
||||
attention: 0,
|
||||
working: state.folderWorkspaces?.length ? 1 : 0,
|
||||
done: 0,
|
||||
idle: 0
|
||||
})
|
||||
)
|
||||
|
||||
const { result } = renderHook(() => useAgentBucketCounts())
|
||||
|
||||
expect(result.current).toEqual({ attention: 0, working: 1, done: 0, idle: 0 })
|
||||
expect(mocks.buildDashboardBucketCounts).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
folderWorkspaces: mocks.state.folderWorkspaces,
|
||||
unifiedTabsByWorktree: mocks.state.unifiedTabsByWorktree
|
||||
}),
|
||||
expect.any(Number),
|
||||
expect.objectContaining({ byWorktree: expect.any(Map) }),
|
||||
expect.anything()
|
||||
)
|
||||
})
|
||||
|
||||
it('moves acknowledged completions to idle without recomputing for unrelated writes', () => {
|
||||
mocks.buildDashboardBucketCounts.mockImplementation(
|
||||
(state: { acknowledgedAgentsByPaneKey?: Record<string, number> }) => ({
|
||||
|
||||
@@ -1,101 +0,0 @@
|
||||
// @vitest-environment happy-dom
|
||||
import { act, Suspense } from 'react'
|
||||
import { createRoot, type Root } from 'react-dom/client'
|
||||
import { afterEach, describe, expect, it, vi } from 'vitest'
|
||||
import type { OpenFile } from '@/store/slices/editor'
|
||||
import type { DiffViewerProps } from './diff-viewer-props'
|
||||
import {
|
||||
MAX_RENDERED_DIFF_COMBINED_CHARACTERS,
|
||||
MAX_RENDERED_DIFF_LINES_PER_SIDE,
|
||||
type LargeDiffRenderLimit
|
||||
} from './large-diff-render-limit'
|
||||
|
||||
const diffViewerMock = vi.hoisted(() => ({
|
||||
latestProps: null as DiffViewerProps | null
|
||||
}))
|
||||
|
||||
vi.mock('./DiffViewer', () => ({
|
||||
default: (props: DiffViewerProps) => {
|
||||
diffViewerMock.latestProps = props
|
||||
return <div data-testid="diff-viewer-probe" />
|
||||
}
|
||||
}))
|
||||
|
||||
import { ChangesModeView } from './ChangesModeView'
|
||||
|
||||
function createOpenFile(): OpenFile {
|
||||
return {
|
||||
id: 'file-1',
|
||||
filePath: '/repo/large.txt',
|
||||
relativePath: 'large.txt',
|
||||
worktreeId: 'repo::/repo',
|
||||
language: 'plaintext',
|
||||
isDirty: false,
|
||||
mode: 'edit'
|
||||
} as OpenFile
|
||||
}
|
||||
|
||||
function createLargeDiffRenderLimit(): LargeDiffRenderLimit {
|
||||
return {
|
||||
limited: true,
|
||||
reason: 'character-count',
|
||||
lineCounts: null,
|
||||
characterCount: MAX_RENDERED_DIFF_COMBINED_CHARACTERS + 1,
|
||||
limits: {
|
||||
maxLinesPerSide: MAX_RENDERED_DIFF_LINES_PER_SIDE,
|
||||
maxCombinedCharacters: MAX_RENDERED_DIFF_COMBINED_CHARACTERS
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
describe('ChangesModeView', () => {
|
||||
let container: HTMLDivElement | null = null
|
||||
let root: Root | null = null
|
||||
|
||||
afterEach(() => {
|
||||
if (root) {
|
||||
act(() => root?.unmount())
|
||||
}
|
||||
container?.remove()
|
||||
container = null
|
||||
root = null
|
||||
diffViewerMock.latestProps = null
|
||||
})
|
||||
|
||||
it('passes pruned diff limits through and suppresses the identical-content banner', async () => {
|
||||
container = document.createElement('div')
|
||||
document.body.appendChild(container)
|
||||
root = createRoot(container)
|
||||
const largeDiffRenderLimit = createLargeDiffRenderLimit()
|
||||
|
||||
await act(async () => {
|
||||
root?.render(
|
||||
<Suspense fallback={null}>
|
||||
<ChangesModeView
|
||||
activeFile={createOpenFile()}
|
||||
dc={{
|
||||
kind: 'text',
|
||||
originalContent: '',
|
||||
modifiedContent: '',
|
||||
originalIsBinary: false,
|
||||
modifiedIsBinary: false,
|
||||
largeDiffRenderLimit
|
||||
}}
|
||||
modifiedContent=""
|
||||
activeConflictEntry={null}
|
||||
resolvedLanguage="plaintext"
|
||||
sideBySide={false}
|
||||
viewStateScopeId="file-1"
|
||||
diffViewStateKey="file-1:changes"
|
||||
onContentChange={vi.fn()}
|
||||
onSave={vi.fn()}
|
||||
/>
|
||||
</Suspense>
|
||||
)
|
||||
})
|
||||
|
||||
await vi.waitFor(() => expect(diffViewerMock.latestProps).not.toBeNull())
|
||||
expect(diffViewerMock.latestProps?.largeDiffRenderLimit).toBe(largeDiffRenderLimit)
|
||||
expect(container.textContent).not.toContain('No uncommitted changes.')
|
||||
})
|
||||
})
|
||||
@@ -1,93 +0,0 @@
|
||||
// @vitest-environment happy-dom
|
||||
import { act } from 'react'
|
||||
import { createRoot, type Root } from 'react-dom/client'
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
import { useAppStore } from '@/store'
|
||||
import type { OpenFile } from '@/store/slices/editor'
|
||||
import { getDefaultSettings } from '../../../../shared/constants'
|
||||
|
||||
const probe = vi.hoisted(() => {
|
||||
const state: { markdownAnnotationsEnabled: boolean | null } = {
|
||||
markdownAnnotationsEnabled: null
|
||||
}
|
||||
return state
|
||||
})
|
||||
|
||||
vi.mock('./EditorPanelShell', () => ({
|
||||
EditorPanelShell: (props: { markdownAnnotationsEnabled: boolean }) => {
|
||||
probe.markdownAnnotationsEnabled = props.markdownAnnotationsEnabled
|
||||
return null
|
||||
}
|
||||
}))
|
||||
|
||||
vi.mock('./useEditorPanelContentState', () => ({
|
||||
useEditorPanelContentState: () => ({
|
||||
fileContents: { '/repo/notes.md': { content: '# Notes\n', isBinary: false } },
|
||||
diffContents: {},
|
||||
reloadContent: () => {}
|
||||
})
|
||||
}))
|
||||
|
||||
import EditorPanel from './EditorPanel'
|
||||
|
||||
const file: OpenFile = {
|
||||
id: '/repo/notes.md',
|
||||
filePath: '/repo/notes.md',
|
||||
relativePath: 'notes.md',
|
||||
worktreeId: 'wt-1',
|
||||
language: 'markdown',
|
||||
mode: 'edit',
|
||||
isDirty: false
|
||||
}
|
||||
|
||||
const initialAppState = useAppStore.getInitialState()
|
||||
let container: HTMLDivElement
|
||||
let root: Root
|
||||
|
||||
async function renderWithSetting(
|
||||
markdownReviewToolsEnabled: boolean,
|
||||
props: { markdownAnnotationsEnabled?: boolean } = {}
|
||||
): Promise<void> {
|
||||
useAppStore.setState({
|
||||
settings: { ...getDefaultSettings('/tmp'), markdownReviewToolsEnabled }
|
||||
})
|
||||
await act(async () => root.render(<EditorPanel {...props} />))
|
||||
}
|
||||
|
||||
describe('EditorPanel markdown review tools setting', () => {
|
||||
beforeEach(() => {
|
||||
globalThis.IS_REACT_ACT_ENVIRONMENT = true
|
||||
probe.markdownAnnotationsEnabled = null
|
||||
useAppStore.setState(initialAppState, true)
|
||||
useAppStore.setState({
|
||||
openFiles: [file],
|
||||
activeFileId: file.id,
|
||||
markdownViewMode: { [file.id]: 'preview' },
|
||||
gitStatusByWorktree: { 'wt-1': [] }
|
||||
})
|
||||
container = document.createElement('div')
|
||||
document.body.appendChild(container)
|
||||
root = createRoot(container)
|
||||
})
|
||||
|
||||
afterEach(async () => {
|
||||
await act(async () => root.unmount())
|
||||
document.body.replaceChildren()
|
||||
useAppStore.setState(initialAppState, true)
|
||||
})
|
||||
|
||||
it('enables review tools when the setting is on', async () => {
|
||||
await renderWithSetting(true)
|
||||
expect(probe.markdownAnnotationsEnabled).toBe(true)
|
||||
})
|
||||
|
||||
it('disables review tools when the setting is off', async () => {
|
||||
await renderWithSetting(false)
|
||||
expect(probe.markdownAnnotationsEnabled).toBe(false)
|
||||
})
|
||||
|
||||
it('keeps a caller opt-out even when the setting is on', async () => {
|
||||
await renderWithSetting(true, { markdownAnnotationsEnabled: false })
|
||||
expect(probe.markdownAnnotationsEnabled).toBe(false)
|
||||
})
|
||||
})
|
||||
@@ -1,8 +1,4 @@
|
||||
import { renderToStaticMarkup } from 'react-dom/server'
|
||||
import remarkCjkFriendly from 'remark-cjk-friendly/parseOnly'
|
||||
import remarkGfm from 'remark-gfm'
|
||||
import remarkParse from 'remark-parse'
|
||||
import { unified } from 'unified'
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { MarkdownPreviewBody } from './MarkdownPreviewBody'
|
||||
|
||||
@@ -34,26 +30,6 @@ const cjkEmphasisExamples = [
|
||||
}
|
||||
]
|
||||
|
||||
const unchangedMarkdownExamples = [
|
||||
'# Heading',
|
||||
'**bold** text and *italic* text',
|
||||
'foo_bar_baz',
|
||||
'`**literal**`',
|
||||
'[link](https://example.com)',
|
||||
'- [x] completed task',
|
||||
'| left | right |\n| --- | --- |\n| a | b |',
|
||||
'~~deleted~~ text',
|
||||
'~~"삭제"~~문장'
|
||||
]
|
||||
|
||||
function parseMarkdown(markdown: string) {
|
||||
return unified().use(remarkParse).use(remarkGfm).parse(markdown)
|
||||
}
|
||||
|
||||
function parseCjkFriendlyMarkdown(markdown: string) {
|
||||
return unified().use(remarkParse).use(remarkGfm).use(remarkCjkFriendly).parse(markdown)
|
||||
}
|
||||
|
||||
describe('MarkdownPreviewBody', () => {
|
||||
it('renders forward reference links in research prose and tables without exposing definitions', () => {
|
||||
const url = 'https://github.com/example/app/blob/revision/src/app/(mobile)/language.tsx#L95'
|
||||
@@ -89,8 +65,4 @@ describe('MarkdownPreviewBody', () => {
|
||||
|
||||
expect(html).toContain(expectedHtml)
|
||||
})
|
||||
|
||||
it.each(unchangedMarkdownExamples)('keeps existing parsing for %s', (markdown) => {
|
||||
expect(parseCjkFriendlyMarkdown(markdown)).toEqual(parseMarkdown(markdown))
|
||||
})
|
||||
})
|
||||
|
||||
@@ -2,8 +2,6 @@ import { beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
import type { OpenFile } from '@/store/slices/editor'
|
||||
import {
|
||||
createCurrentEditorArtifactRequest,
|
||||
createEditorArtifactRequest,
|
||||
editorArtifactContentType,
|
||||
editorArtifactSourceKey
|
||||
} from './editor-artifact-upload'
|
||||
|
||||
@@ -38,16 +36,6 @@ function openFile(overrides: Partial<OpenFile> = {}): OpenFile {
|
||||
}
|
||||
|
||||
describe('Editor artifact upload', () => {
|
||||
it('uses the ordinary file path for local and folder workspaces', () => {
|
||||
expect(editorArtifactSourceKey(openFile())).toBe('/repo/notes.md')
|
||||
expect(createEditorArtifactRequest(openFile(), '# Draft', 'text/markdown')).toEqual({
|
||||
sourceKey: '/repo/notes.md',
|
||||
content: '# Draft',
|
||||
contentType: 'text/markdown',
|
||||
fileName: 'notes.md'
|
||||
})
|
||||
})
|
||||
|
||||
it('isolates source identity by runtime owner', () => {
|
||||
const file = openFile({
|
||||
runtimeEnvironmentId: 'server-1',
|
||||
@@ -94,12 +82,4 @@ describe('Editor artifact upload', () => {
|
||||
).toBe('# Latest edit')
|
||||
expect(mocks.flush).toHaveBeenCalledWith(expect.anything(), '/repo/notes.md')
|
||||
})
|
||||
|
||||
it('shares Markdown and HTML documents only', () => {
|
||||
expect(editorArtifactContentType('/repo/notes.md', true)).toBe('text/markdown')
|
||||
expect(editorArtifactContentType('/repo/report.html', false)).toBe('text/html')
|
||||
expect(editorArtifactContentType('/repo/REPORT.HTM', false)).toBe('text/html')
|
||||
expect(editorArtifactContentType('/repo/index.jsp', false)).toBeNull()
|
||||
expect(editorArtifactContentType('/repo/main.ts', false)).toBeNull()
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,138 +0,0 @@
|
||||
import type { ReactNode } from 'react'
|
||||
import { renderToStaticMarkup } from 'react-dom/server'
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
import { ChecksPanelReviewHeader } from './checks-panel/review-header'
|
||||
import type { ChecksPanelHostedReviewModifierDestination } from './checks-panel-hosted-review-click-routing'
|
||||
|
||||
vi.mock('@/components/ui/dropdown-menu', () => ({
|
||||
DropdownMenu: ({ children }: { children: ReactNode }) => <div>{children}</div>,
|
||||
DropdownMenuTrigger: ({ children }: { children: ReactNode; asChild?: boolean }) => (
|
||||
<>{children}</>
|
||||
),
|
||||
DropdownMenuContent: ({ children }: { children: ReactNode }) => <div>{children}</div>,
|
||||
DropdownMenuItem: ({
|
||||
children,
|
||||
disabled
|
||||
}: {
|
||||
children: ReactNode
|
||||
disabled?: boolean
|
||||
onSelect?: () => void
|
||||
}) => <div data-disabled={disabled ? 'true' : undefined}>{children}</div>
|
||||
}))
|
||||
|
||||
vi.mock('@/components/ui/tooltip', () => ({
|
||||
Tooltip: ({ children }: { children: ReactNode }) => <>{children}</>,
|
||||
TooltipContent: ({ children }: { children: ReactNode }) => <>{children}</>,
|
||||
TooltipTrigger: ({ children }: { children: ReactNode }) => <>{children}</>
|
||||
}))
|
||||
|
||||
beforeEach(() => {
|
||||
vi.stubGlobal('navigator', { userAgent: 'Macintosh' })
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
vi.unstubAllGlobals()
|
||||
})
|
||||
|
||||
function renderHeader({
|
||||
canUnlinkReview = true,
|
||||
provider = 'github',
|
||||
modifierHintDestination = 'system-browser'
|
||||
}: {
|
||||
canUnlinkReview?: boolean
|
||||
provider?: 'github' | 'gitlab'
|
||||
modifierHintDestination?: ChecksPanelHostedReviewModifierDestination
|
||||
} = {}): string {
|
||||
const isGitLab = provider === 'gitlab'
|
||||
return renderToStaticMarkup(
|
||||
<ChecksPanelReviewHeader
|
||||
review={{
|
||||
provider,
|
||||
number: isGitLab ? 31 : 2964,
|
||||
title: isGitLab ? 'Fix GitLab MR creation' : 'fix: pr-bug-scan validated finding',
|
||||
state: 'open',
|
||||
url: isGitLab
|
||||
? 'https://gitlab.com/acme/orca/-/merge_requests/31'
|
||||
: 'https://github.com/stablyai/orca/pull/2964',
|
||||
status: 'pending',
|
||||
updatedAt: '2026-05-31T22:58:01Z',
|
||||
mergeable: 'UNKNOWN'
|
||||
}}
|
||||
isRefreshing={false}
|
||||
canUnlinkReview={canUnlinkReview}
|
||||
modifierHintDestination={modifierHintDestination}
|
||||
onRefresh={vi.fn()}
|
||||
onOpenReview={vi.fn()}
|
||||
onUnlinkReview={vi.fn()}
|
||||
onLinkAnotherReview={vi.fn()}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
describe('ChecksPanelReviewHeader', () => {
|
||||
it('opens the PR from the number and puts link management behind the menu', () => {
|
||||
const markup = renderHeader()
|
||||
|
||||
expect(markup).toContain('Open on GitHub')
|
||||
expect(markup).toContain('system browser')
|
||||
expect(markup).toContain('⇧⌘+click')
|
||||
expect(markup).not.toContain('⌘+click to open')
|
||||
expect(markup).toContain('#2964')
|
||||
expect(markup).toContain('underline decoration-border underline-offset-2')
|
||||
expect(markup).toContain('More PR actions')
|
||||
expect(markup).toContain('Unlink PR from workspace')
|
||||
expect(markup).toContain(
|
||||
'Orca will hide PR #2964 details for this workspace. The PR and branch on GitHub won’t be changed.'
|
||||
)
|
||||
expect(markup).toContain('Link another PR')
|
||||
expect(markup).toContain('lucide-ellipsis')
|
||||
expect(markup).not.toContain('lucide-external-link')
|
||||
})
|
||||
|
||||
it('omits the modifier hint when it lands where a plain click already does', () => {
|
||||
const markup = renderHeader({ modifierHintDestination: null })
|
||||
|
||||
expect(markup).toContain('Open on GitHub')
|
||||
expect(markup).not.toContain('system browser')
|
||||
expect(markup).not.toContain('⇧⌘+click')
|
||||
})
|
||||
|
||||
// With inversion on and Link Routing off, the modifier goes to Orca; label that destination.
|
||||
it('names Orca when the modifier inverts toward the built-in browser', () => {
|
||||
expect(renderHeader({ modifierHintDestination: 'orca' })).toContain('⇧⌘+click to open in Orca')
|
||||
|
||||
vi.stubGlobal('navigator', { userAgent: 'Windows' })
|
||||
expect(renderHeader({ modifierHintDestination: 'orca' })).toContain(
|
||||
'Shift+Ctrl+click to open in Orca'
|
||||
)
|
||||
})
|
||||
|
||||
it('shows the Ctrl system-browser hint off macOS', () => {
|
||||
vi.stubGlobal('navigator', { userAgent: 'Windows' })
|
||||
|
||||
const markup = renderHeader()
|
||||
|
||||
expect(markup).toContain('Shift+Ctrl+click for system browser')
|
||||
expect(markup).not.toContain('Ctrl+click to open')
|
||||
})
|
||||
|
||||
it('enables unlinking for a displayed auto-detected PR', () => {
|
||||
const markup = renderHeader({ canUnlinkReview: true })
|
||||
|
||||
expect(markup).not.toContain('data-disabled="true"')
|
||||
expect(markup).toContain('Unlink PR from workspace')
|
||||
})
|
||||
|
||||
it('shows GitLab MR identity with provider-appropriate link management actions', () => {
|
||||
const markup = renderHeader({ provider: 'gitlab' })
|
||||
|
||||
expect(markup).toContain('Open on GitLab')
|
||||
expect(markup).toContain('!31')
|
||||
expect(markup).toContain('More MR actions')
|
||||
expect(markup).toContain('Unlink MR from workspace')
|
||||
expect(markup).toContain(
|
||||
'Orca will hide MR !31 details for this workspace. The MR and branch on GitLab won’t be changed.'
|
||||
)
|
||||
expect(markup).toContain('Link another MR')
|
||||
})
|
||||
})
|
||||
-114
@@ -1,114 +0,0 @@
|
||||
import React from 'react'
|
||||
import { renderToStaticMarkup } from 'react-dom/server'
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
import type { HostedReviewInfo } from '../../../../shared/hosted-review'
|
||||
import { HostedReviewHeaderLink } from './source-control/review/hosted-review-header-chrome'
|
||||
|
||||
const { openHttpLinkMock } = vi.hoisted(() => ({ openHttpLinkMock: vi.fn() }))
|
||||
|
||||
vi.mock('@/lib/http-link-routing', () => ({
|
||||
openHttpLink: openHttpLinkMock,
|
||||
registerHttpLinkStoreAccessor: vi.fn(),
|
||||
registerWorkspaceHttpLinkBrowserOpener: vi.fn()
|
||||
}))
|
||||
|
||||
function makeReview(overrides: Partial<HostedReviewInfo> = {}): HostedReviewInfo {
|
||||
return {
|
||||
provider: 'github',
|
||||
number: 2192,
|
||||
title: 'Open PR in checks',
|
||||
state: 'open',
|
||||
url: 'https://github.com/stablyai/orca/pull/2192',
|
||||
status: 'pending',
|
||||
updatedAt: '2026-05-17T00:00:00Z',
|
||||
mergeable: 'UNKNOWN',
|
||||
...overrides
|
||||
}
|
||||
}
|
||||
|
||||
type MinimalClickEvent = Pick<
|
||||
React.MouseEvent<HTMLButtonElement>,
|
||||
'nativeEvent' | 'stopPropagation'
|
||||
>
|
||||
type ClickModifiers = Partial<Pick<MouseEvent, 'metaKey' | 'ctrlKey' | 'shiftKey'>>
|
||||
|
||||
function clickEvent(modifiers: ClickModifiers = {}): MinimalClickEvent {
|
||||
return {
|
||||
nativeEvent: {
|
||||
metaKey: modifiers.metaKey ?? false,
|
||||
ctrlKey: modifiers.ctrlKey ?? false,
|
||||
shiftKey: modifiers.shiftKey ?? false
|
||||
} as MouseEvent,
|
||||
stopPropagation: vi.fn()
|
||||
}
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
openHttpLinkMock.mockReset()
|
||||
})
|
||||
|
||||
describe('HostedReviewHeaderLink', () => {
|
||||
it('opens GitHub PRs in the Checks tab instead of rendering an external link', () => {
|
||||
const onOpenHostedReviewInChecks = vi.fn()
|
||||
const element = HostedReviewHeaderLink({
|
||||
review: makeReview(),
|
||||
onOpenHostedReviewInChecks
|
||||
})
|
||||
const markup = renderToStaticMarkup(element)
|
||||
|
||||
expect(markup).toContain('<button')
|
||||
expect(markup).toContain('PR #2192')
|
||||
expect(markup).toContain('underline decoration-border underline-offset-2')
|
||||
expect(markup).not.toContain('href=')
|
||||
expect(markup).not.toContain('target="_blank"')
|
||||
expect(markup).not.toContain('system browser')
|
||||
expect(markup).not.toContain('⌘+click')
|
||||
|
||||
const event = clickEvent()
|
||||
;(element.props.onClick as (event: MinimalClickEvent) => void)(event)
|
||||
expect(event.stopPropagation).toHaveBeenCalledTimes(1)
|
||||
expect(onOpenHostedReviewInChecks).toHaveBeenCalledTimes(1)
|
||||
expect(openHttpLinkMock).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('opens GitLab MRs in the Checks tab instead of rendering an external link', () => {
|
||||
const onOpenHostedReviewInChecks = vi.fn()
|
||||
const element = HostedReviewHeaderLink({
|
||||
review: makeReview({
|
||||
provider: 'gitlab',
|
||||
number: 31,
|
||||
url: 'https://gitlab.com/acme/widgets/-/merge_requests/31'
|
||||
}),
|
||||
onOpenHostedReviewInChecks
|
||||
})
|
||||
const markup = renderToStaticMarkup(element)
|
||||
|
||||
expect(markup).toContain('<button')
|
||||
expect(markup).not.toContain('href=')
|
||||
expect(markup).toContain('MR #31')
|
||||
|
||||
const event = clickEvent()
|
||||
;(element.props.onClick as (event: MinimalClickEvent) => void)(event)
|
||||
expect(event.stopPropagation).toHaveBeenCalledTimes(1)
|
||||
expect(onOpenHostedReviewInChecks).toHaveBeenCalledTimes(1)
|
||||
expect(openHttpLinkMock).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('keeps other provider reviews as external hosted-review links', () => {
|
||||
const markup = renderToStaticMarkup(
|
||||
<HostedReviewHeaderLink
|
||||
review={makeReview({
|
||||
provider: 'bitbucket',
|
||||
number: 31,
|
||||
url: 'https://bitbucket.org/acme/widgets/pull-requests/31'
|
||||
})}
|
||||
onOpenHostedReviewInChecks={vi.fn()}
|
||||
/>
|
||||
)
|
||||
|
||||
expect(markup).toContain('<a')
|
||||
expect(markup).toContain('href="https://bitbucket.org/acme/widgets/pull-requests/31"')
|
||||
expect(markup).toContain('target="_blank"')
|
||||
expect(markup).toContain('PR #31')
|
||||
})
|
||||
})
|
||||
@@ -1,97 +0,0 @@
|
||||
import { renderToStaticMarkup } from 'react-dom/server'
|
||||
import { describe, expect, it, vi } from 'vitest'
|
||||
import { LinearAgentSkillGuide, type LinearSetupReadiness } from './LinearAgentSkillGuide'
|
||||
|
||||
const baseReadiness: LinearSetupReadiness = {
|
||||
connected: true,
|
||||
checking: false,
|
||||
skillInstalled: false,
|
||||
skillChecking: false,
|
||||
skillUnverifiable: false,
|
||||
visible: true
|
||||
}
|
||||
|
||||
function renderGuide(readiness: Partial<LinearSetupReadiness>): string {
|
||||
return renderToStaticMarkup(
|
||||
<LinearAgentSkillGuide
|
||||
readiness={{ ...baseReadiness, ...readiness }}
|
||||
onOpenTaskSources={vi.fn()}
|
||||
onManageLinearAccess={vi.fn()}
|
||||
skillPanel={<div data-testid="skill-panel">Skill install panel</div>}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
describe('LinearAgentSkillGuide', () => {
|
||||
it('renders the setup checklist with an inlined skill panel', () => {
|
||||
const markup = renderGuide({})
|
||||
|
||||
expect(markup).toContain('Setup checklist')
|
||||
expect(markup).toContain('2 of 3 ready')
|
||||
expect(markup).toContain('Task Sources')
|
||||
expect(markup).toContain('Skill install panel')
|
||||
expect(markup).not.toContain('Ready below')
|
||||
expect(markup).not.toContain('Install below')
|
||||
expect(markup).not.toContain('Agent skill')
|
||||
expect(markup).not.toContain('Open Task Sources setup')
|
||||
})
|
||||
|
||||
it('marks the checklist complete when every step is done', () => {
|
||||
expect(renderGuide({ skillInstalled: true })).toContain('All set')
|
||||
})
|
||||
|
||||
it('keeps durable progress while a skill recheck is in flight', () => {
|
||||
const markup = renderGuide({ skillInstalled: true, skillChecking: true })
|
||||
|
||||
expect(markup).toContain('Checking…')
|
||||
expect(markup).not.toContain('2 of 3 ready')
|
||||
expect(markup).not.toContain('All set')
|
||||
})
|
||||
|
||||
it('keeps durable progress while a connection check is in flight', () => {
|
||||
const markup = renderGuide({ skillInstalled: true, checking: true })
|
||||
|
||||
expect(markup).toContain('Checking…')
|
||||
expect(markup).not.toContain('2 of 3 ready')
|
||||
})
|
||||
|
||||
it('reports an unverifiable skill scan as unknown instead of an unfinished step', () => {
|
||||
const markup = renderGuide({ skillUnverifiable: true })
|
||||
|
||||
expect(markup).toContain('Cannot verify')
|
||||
expect(markup).toContain('2/3')
|
||||
expect(markup).toContain('bg-amber-500')
|
||||
expect(markup).not.toContain('2 of 3 ready')
|
||||
expect(markup).not.toContain('All set')
|
||||
})
|
||||
|
||||
it('still claims nothing while a rescan of an unverifiable step runs', () => {
|
||||
const markup = renderGuide({ skillUnverifiable: true, skillChecking: true })
|
||||
|
||||
expect(markup).toContain('Checking…')
|
||||
expect(markup).not.toContain('Cannot verify')
|
||||
})
|
||||
|
||||
it('lets a found skill outrank a stale unverifiable flag', () => {
|
||||
const markup = renderGuide({ skillInstalled: true, skillUnverifiable: true })
|
||||
|
||||
expect(markup).toContain('All set')
|
||||
expect(markup).not.toContain('Cannot verify')
|
||||
})
|
||||
|
||||
it('keeps the confirmed count when the unfinished step is the connection', () => {
|
||||
const markup = renderGuide({ connected: false, skillUnverifiable: true })
|
||||
|
||||
expect(markup).toContain('1 of 3 ready')
|
||||
expect(markup).not.toContain('Cannot verify')
|
||||
})
|
||||
|
||||
it('does not headline an unknown skill over an unfinished visibility step', () => {
|
||||
const markup = renderGuide({ visible: false, skillUnverifiable: true })
|
||||
|
||||
expect(markup).toContain('1 of 3 ready')
|
||||
expect(markup).not.toContain('Cannot verify')
|
||||
// Hiding Linear is deliberate, so the shared table keeps this pill neutral.
|
||||
expect(markup).not.toContain('bg-amber-500')
|
||||
})
|
||||
})
|
||||
@@ -36,23 +36,6 @@ function buildReadiness(
|
||||
}
|
||||
|
||||
describe('task-source-setup-state', () => {
|
||||
it('counts Linear readiness as three steps', () => {
|
||||
expect(
|
||||
getTaskProviderCompletedSteps({
|
||||
connected: true,
|
||||
checking: false,
|
||||
skillInstalled: false,
|
||||
visible: true
|
||||
})
|
||||
).toEqual({ completed: 2, total: 3 })
|
||||
})
|
||||
|
||||
it('counts code-host readiness as two steps', () => {
|
||||
expect(
|
||||
getTaskProviderCompletedSteps({ connected: true, checking: false, visible: true })
|
||||
).toEqual({ completed: 2, total: 2 })
|
||||
})
|
||||
|
||||
it('marks Linear ready only when connected, skill installed, and visible', () => {
|
||||
expect(
|
||||
isTaskProviderReady({
|
||||
@@ -256,66 +239,4 @@ describe('task-source-setup-state', () => {
|
||||
})
|
||||
).toBe('linear')
|
||||
})
|
||||
|
||||
it('does not switch to a later incomplete provider during the previous provider recheck', () => {
|
||||
const whileChecking = buildReadiness({
|
||||
linear: { skillInstalled: false, skillChecking: true },
|
||||
jira: { connected: false }
|
||||
})
|
||||
|
||||
expect(getAutoExpandedTaskProvider(ORDER, whileChecking)).toBe('jira')
|
||||
expect(
|
||||
resolveStickyAutoExpandedTaskProvider({
|
||||
providers: ORDER,
|
||||
readinessByProvider: whileChecking,
|
||||
previousAutoExpanded: 'linear'
|
||||
})
|
||||
).toBe('linear')
|
||||
})
|
||||
|
||||
it('does not hand the expansion to a provider whose check landed later', () => {
|
||||
// Model slower gh/glab preflight completing after Linear claimed expansion.
|
||||
const afterPreflight = buildReadiness({
|
||||
github: { connected: false },
|
||||
gitlab: { connected: false },
|
||||
linear: { skillInstalled: false }
|
||||
})
|
||||
|
||||
expect(getAutoExpandedTaskProvider(ORDER, afterPreflight)).toBe('github')
|
||||
expect(
|
||||
resolveStickyAutoExpandedTaskProvider({
|
||||
providers: ORDER,
|
||||
readinessByProvider: afterPreflight,
|
||||
previousAutoExpanded: 'linear'
|
||||
})
|
||||
).toBe('linear')
|
||||
})
|
||||
|
||||
it('keeps the auto-expand slot after the chosen provider finishes', () => {
|
||||
const afterLinearCompletes = buildReadiness({ github: { connected: false } })
|
||||
|
||||
expect(getAutoExpandedTaskProvider(ORDER, afterLinearCompletes)).toBe('github')
|
||||
expect(
|
||||
resolveStickyAutoExpandedTaskProvider({
|
||||
providers: ORDER,
|
||||
readinessByProvider: afterLinearCompletes,
|
||||
previousAutoExpanded: 'linear'
|
||||
})
|
||||
).toBe('linear')
|
||||
})
|
||||
|
||||
it('keeps the auto-expand slot when the chosen provider is hidden', () => {
|
||||
const afterHidingLinear = buildReadiness({
|
||||
github: { connected: false },
|
||||
linear: { skillInstalled: false, visible: false }
|
||||
})
|
||||
|
||||
expect(
|
||||
resolveStickyAutoExpandedTaskProvider({
|
||||
providers: ORDER,
|
||||
readinessByProvider: afterHidingLinear,
|
||||
previousAutoExpanded: 'linear'
|
||||
})
|
||||
).toBe('linear')
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user