mirror of
https://github.com/stablyai/orca.git
synced 2026-11-13 17:37:40 +00:00
test: remove low-value workspace wrapper and notice cases (#41938)
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
This commit is contained in:
@@ -4755,9 +4755,6 @@
|
||||
"src/renderer/src/components/native-chat/pane/actions/use-structured-native-chat-submit-reveal.test.ts": 2304,
|
||||
"src/renderer/src/components/network/CustomAddressDialog.test.tsx": 568,
|
||||
"src/renderer/src/components/new-workspace/branch-selection/ComposerParentWorktreePicker.test.tsx": 870,
|
||||
"src/renderer/src/components/new-workspace/setup/DirectWorkItemSetupDialog.test.tsx": 5296,
|
||||
"src/renderer/src/components/new-workspace/composer/NewWorkspaceComposerNameSection.test.tsx": 2493,
|
||||
"src/renderer/src/components/new-workspace/project-selection/ProjectCombobox.dialog-handoff.test.tsx": 1043,
|
||||
"src/renderer/src/components/new-workspace/RunTargetCombobox.host-identity.test.tsx": 856,
|
||||
"src/renderer/src/components/new-workspace/SetProjectLocationDialog.host-identity.test.tsx": 1094,
|
||||
"src/renderer/src/components/new-workspace/project-location/SetProjectLocationDialog.test.tsx": 1387,
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
import { readFileSync } from 'node:fs'
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { hexToRgb, rgbaToHex, type Rgba } from '../../../../shared/color/rgba-contrast'
|
||||
import { hexToRgb, type Rgba } from '../../../../shared/color/rgba-contrast'
|
||||
import { noticeSurfaceTokens } from './notice-surface-tokens'
|
||||
import { safePluginThemeTokens } from './plugin-theme-tokens'
|
||||
|
||||
const DARK_BUILT_INS = {
|
||||
'--foreground': '#f0f0f0',
|
||||
@@ -71,16 +69,6 @@ function expectReadable(themeTokens: Record<string, string>): void {
|
||||
}
|
||||
|
||||
describe('noticeSurfaceTokens', () => {
|
||||
it("paints Orca's dark notice colors for the built-in palette", () => {
|
||||
expect(noticeSurfaceTokens({}, 'dark', DARK_BUILT_INS, probe)).toEqual({
|
||||
'--notice-surface': '#262626',
|
||||
'--notice-secondary': '#3a3a3a',
|
||||
'--notice-secondary-edge': 'transparent',
|
||||
'--notice-muted': '#3a3a3a',
|
||||
'--notice-muted-foreground': '#acacac'
|
||||
})
|
||||
})
|
||||
|
||||
// Popovers the theme validator accepts that once painted notice text under 4.5:1: #5a5a5a
|
||||
// through Chromium's color-mix rounding and an over-lifted 80% description, #383838 through hex
|
||||
// rounding, white-on-dark-base through dark muted panels, the rest unlifted.
|
||||
@@ -94,18 +82,6 @@ describe('noticeSurfaceTokens', () => {
|
||||
expectReadable(themeTokens)
|
||||
})
|
||||
|
||||
it('keeps notice text 4.5:1 for every gray popover the validator accepts', () => {
|
||||
const accepted = Array.from({ length: 256 }, (_, gray) => ({
|
||||
'--popover': rgbaToHex([gray, gray, gray, 1])
|
||||
})).filter(
|
||||
(tokens) => safePluginThemeTokens(tokens, 'dark', DARK_BUILT_INS, probe)['--popover']
|
||||
)
|
||||
expect(accepted.length).toBeGreaterThan(50)
|
||||
for (const tokens of accepted) {
|
||||
expectReadable(tokens)
|
||||
}
|
||||
})
|
||||
|
||||
it('outlines secondary buttons only when their fill is gone', () => {
|
||||
const tokensFor = (popoverForeground: string): Record<string, string> =>
|
||||
noticeSurfaceTokens(
|
||||
@@ -121,25 +97,4 @@ describe('noticeSurfaceTokens', () => {
|
||||
expect(filled['--notice-secondary']).not.toBe(filled['--notice-surface'])
|
||||
expect(filled['--notice-secondary-edge']).toBe('transparent')
|
||||
})
|
||||
|
||||
it('remaps the surface paired with every foreground the notice scope remaps', () => {
|
||||
const css = readFileSync(new URL('../../assets/main.css', import.meta.url), 'utf8')
|
||||
const scope = css.match(
|
||||
/\[data-sonner-toaster\],\s*\.notification-card-stack\s*\{([^}]*)\}/
|
||||
)?.[1]
|
||||
expect(scope).toBeDefined()
|
||||
const remapped = new Set([...(scope ?? '').matchAll(/(--[\w-]+):/g)].map(([, name]) => name))
|
||||
// Why: a foreground remapped onto the popover's polarity over an unremapped surface (outline
|
||||
// buttons: text-foreground on bg-background) paints light-on-light or dark-on-dark.
|
||||
const pairs = [
|
||||
['--foreground', '--background'],
|
||||
['--card-foreground', '--card'],
|
||||
['--secondary-foreground', '--secondary'],
|
||||
['--muted-foreground', '--muted']
|
||||
]
|
||||
for (const [foreground, surface] of pairs) {
|
||||
expect(remapped.has(foreground), foreground).toBe(true)
|
||||
expect(remapped.has(surface), `${surface} pairs with ${foreground}`).toBe(true)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
@@ -107,7 +107,6 @@ describe('FullDiskAccessSetupPrompt state helpers', () => {
|
||||
|
||||
expect(container.textContent).not.toContain('Full Disk Access')
|
||||
expect(getStatus).not.toHaveBeenCalled()
|
||||
expect(window.api.developerPermissions.request).not.toHaveBeenCalled()
|
||||
root.unmount()
|
||||
})
|
||||
})
|
||||
|
||||
-104
@@ -1,104 +0,0 @@
|
||||
// @vitest-environment happy-dom
|
||||
|
||||
import React from 'react'
|
||||
import { cleanup, render, screen, waitFor } from '@testing-library/react'
|
||||
import userEvent from '@testing-library/user-event'
|
||||
import { afterEach, describe, expect, it, vi } from 'vitest'
|
||||
import { NewWorkspaceComposerNameSection } from './NewWorkspaceComposerNameSection'
|
||||
|
||||
vi.mock('@/components/new-workspace/SmartWorkspaceNameField', () => ({
|
||||
default: ({ value, selectedSource }: { value: string; selectedSource?: { label: string } }) => (
|
||||
<div>
|
||||
<span>{selectedSource?.label}</span>
|
||||
<input aria-label="Workspace name" value={value} readOnly />
|
||||
</div>
|
||||
)
|
||||
}))
|
||||
|
||||
function branchProps(): React.ComponentProps<typeof NewWorkspaceComposerNameSection> {
|
||||
return {
|
||||
eligibleRepos: [],
|
||||
repoId: 'repo',
|
||||
onRepoChange: vi.fn(),
|
||||
name: 'Payments review',
|
||||
onNameValueChange: vi.fn(),
|
||||
onSmartGitHubItemSelect: vi.fn(),
|
||||
onSmartGitLabItemSelect: vi.fn(),
|
||||
onSmartBranchSelect: vi.fn(),
|
||||
onSmartLinearIssueSelect: vi.fn(),
|
||||
smartNameSelection: { kind: 'branch', label: 'origin/feature/payments' },
|
||||
onClearSmartNameSelection: vi.fn(),
|
||||
selectedRepoRequiresConnection: false,
|
||||
selectedRepoIsGit: true,
|
||||
branchesEnabled: true,
|
||||
forkPushWarning: null,
|
||||
canReuseSelectedBranch: true,
|
||||
reuseEligibleBranch: 'feature/payments',
|
||||
branchNameOverride: undefined,
|
||||
onBranchNameOverrideChange: vi.fn(),
|
||||
reuseSelectedBranch: true,
|
||||
onReuseSelectedBranchChange: vi.fn(),
|
||||
baseBranch: 'refs/remotes/origin/feature/payments',
|
||||
onNamePlainEnter: vi.fn()
|
||||
}
|
||||
}
|
||||
|
||||
afterEach(cleanup)
|
||||
|
||||
describe('NewWorkspaceComposerNameSection branch reuse', () => {
|
||||
it('chooses a new branch by pointer and the existing branch by keyboard without changing the source', async () => {
|
||||
const user = userEvent.setup()
|
||||
const props = branchProps()
|
||||
const { rerender } = render(<NewWorkspaceComposerNameSection {...props} />)
|
||||
|
||||
expect(screen.getByText('origin/feature/payments')).toBeTruthy()
|
||||
expect(screen.getByText('feature/payments')).toBeTruthy()
|
||||
expect(screen.queryByRole('textbox', { name: 'New branch name' })).toBeNull()
|
||||
expect(
|
||||
screen.getByRole('radio', { name: 'Existing branch' }).getAttribute('aria-checked')
|
||||
).toBe('true')
|
||||
await user.click(screen.getByRole('radio', { name: 'New branch' }))
|
||||
expect(props.onReuseSelectedBranchChange).toHaveBeenLastCalledWith(false)
|
||||
|
||||
rerender(<NewWorkspaceComposerNameSection {...props} reuseSelectedBranch={false} />)
|
||||
expect(
|
||||
screen.getByRole('radio', { name: 'Existing branch' }).getAttribute('aria-checked')
|
||||
).toBe('false')
|
||||
expect(document.activeElement).toBe(screen.getByRole('textbox', { name: 'New branch name' }))
|
||||
await user.paste('review/payments')
|
||||
expect(props.onBranchNameOverrideChange).toHaveBeenLastCalledWith('review/payments')
|
||||
await user.click(screen.getByRole('radio', { name: 'New branch' }))
|
||||
expect(document.activeElement).toBe(screen.getByRole('textbox', { name: 'New branch name' }))
|
||||
screen.getByRole('radio', { name: 'New branch' }).focus()
|
||||
await user.keyboard('{ArrowUp>}')
|
||||
await waitFor(() => expect(props.onReuseSelectedBranchChange).toHaveBeenLastCalledWith(true))
|
||||
await user.keyboard('{/ArrowUp}')
|
||||
expect(props.onNameValueChange).not.toHaveBeenCalled()
|
||||
expect(props.onClearSmartNameSelection).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('opens the existing workspace with the keyboard without submitting or changing branch intent', async () => {
|
||||
const user = userEvent.setup()
|
||||
const props = branchProps()
|
||||
const openWorkspace = vi.fn()
|
||||
const submit = vi.fn((event: React.FormEvent) => event.preventDefault())
|
||||
render(
|
||||
<form onSubmit={submit}>
|
||||
<NewWorkspaceComposerNameSection {...props} onOpenSelectedBranchWorkspace={openWorkspace} />
|
||||
</form>
|
||||
)
|
||||
|
||||
const openButton = screen.getByRole('button', { name: 'Open existing workspace' })
|
||||
await user.tab()
|
||||
await user.tab()
|
||||
await user.tab()
|
||||
expect(document.activeElement).toBe(openButton)
|
||||
await user.keyboard('{Enter}')
|
||||
|
||||
expect(openWorkspace).toHaveBeenCalledOnce()
|
||||
expect(submit).not.toHaveBeenCalled()
|
||||
expect(props.onReuseSelectedBranchChange).not.toHaveBeenCalled()
|
||||
expect(props.onNameValueChange).not.toHaveBeenCalled()
|
||||
expect(props.onClearSmartNameSelection).not.toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
-120
@@ -1,120 +0,0 @@
|
||||
// @vitest-environment happy-dom
|
||||
|
||||
import React, { act, Suspense, useEffect, useRef, useState } from 'react'
|
||||
import { createRoot, type Root } from 'react-dom/client'
|
||||
import { afterEach, beforeEach, expect, it, vi } from 'vitest'
|
||||
import { Dialog, DialogContent, DialogTitle } from '@/components/ui/dialog'
|
||||
import ProjectCombobox from './ProjectCombobox'
|
||||
import { TooltipProvider } from '@/components/ui/tooltip'
|
||||
|
||||
vi.mock('./use-recent-project-ids', () => ({ useRecentProjectIds: () => [] }))
|
||||
|
||||
// Mirrors a lazily loaded creation dialog that focuses its primary action from a mount effect.
|
||||
let resolveLateDialog: () => void = () => {}
|
||||
const LateAddDialogBody = React.lazy(
|
||||
() =>
|
||||
new Promise<{ default: () => React.JSX.Element }>((resolve) => {
|
||||
resolveLateDialog = () =>
|
||||
resolve({
|
||||
default: function Body(): React.JSX.Element {
|
||||
const browseRef = useRef<HTMLButtonElement>(null)
|
||||
useEffect(() => browseRef.current?.focus(), [])
|
||||
return <button ref={browseRef}>Browse</button>
|
||||
}
|
||||
})
|
||||
})
|
||||
)
|
||||
|
||||
function LateDialogComposer(): React.JSX.Element {
|
||||
const [adding, setAdding] = useState(false)
|
||||
return (
|
||||
<Dialog open>
|
||||
<DialogContent aria-describedby={undefined}>
|
||||
<DialogTitle>New workspace</DialogTitle>
|
||||
<ProjectCombobox
|
||||
options={[]}
|
||||
value={null}
|
||||
onValueChange={vi.fn()}
|
||||
onAddProject={() => setAdding(true)}
|
||||
/>
|
||||
{adding ? (
|
||||
<Suspense fallback={null}>
|
||||
<Dialog open onOpenChange={setAdding}>
|
||||
<DialogContent aria-describedby={undefined}>
|
||||
<DialogTitle>Add a project</DialogTitle>
|
||||
<LateAddDialogBody />
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
</Suspense>
|
||||
) : null}
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
)
|
||||
}
|
||||
|
||||
let container: HTMLDivElement
|
||||
let root: Root
|
||||
|
||||
beforeEach(() => {
|
||||
// Radix Presence retains closing content when the production exit animation runs.
|
||||
const getStyle = window.getComputedStyle.bind(window)
|
||||
vi.spyOn(window, 'getComputedStyle').mockImplementation((element, ...args) => {
|
||||
const style = getStyle(element, ...args)
|
||||
if (element.getAttribute('data-slot') === 'popover-content') {
|
||||
return new Proxy(style, {
|
||||
get: (target, property) =>
|
||||
property === 'animationName'
|
||||
? element.getAttribute('data-state') === 'closed'
|
||||
? 'exit'
|
||||
: 'enter'
|
||||
: // oxlint-disable-next-line anti-slop/no-reflect-get -- Proxy `get` trap: raw string|symbol pass-through; the receiver stays the target on purpose.
|
||||
Reflect.get(target, property)
|
||||
})
|
||||
}
|
||||
return style
|
||||
})
|
||||
container = document.createElement('div')
|
||||
document.body.appendChild(container)
|
||||
root = createRoot(container)
|
||||
})
|
||||
|
||||
afterEach(async () => {
|
||||
await act(async () => root.unmount())
|
||||
container.remove()
|
||||
vi.restoreAllMocks()
|
||||
})
|
||||
|
||||
function projectField(): HTMLInputElement {
|
||||
return document.querySelector<HTMLInputElement>('[role="combobox"][aria-label="Project"]')!
|
||||
}
|
||||
|
||||
function addOption(): HTMLElement {
|
||||
return Array.from(document.querySelectorAll<HTMLElement>('[role="option"]')).find((option) =>
|
||||
option.textContent?.includes('Add project')
|
||||
)!
|
||||
}
|
||||
|
||||
it('keeps the selector closed when a late creation dialog focuses before its trap mounts', async () => {
|
||||
await act(async () =>
|
||||
root.render(
|
||||
<TooltipProvider>
|
||||
<LateDialogComposer />
|
||||
</TooltipProvider>
|
||||
)
|
||||
)
|
||||
// Dialog autofocus lands on the field, so the composer trap records it as last focused.
|
||||
expect(document.activeElement).toBe(projectField())
|
||||
expect(document.querySelector('[role="listbox"]')).not.toBeNull()
|
||||
|
||||
await act(async () => {
|
||||
addOption().dispatchEvent(new MouseEvent('mousedown', { bubbles: true, cancelable: true }))
|
||||
addOption().click()
|
||||
})
|
||||
expect(document.querySelector('[role="listbox"]')).toBeNull()
|
||||
// Let the closed selector release its focus scope before the lazy dialog arrives.
|
||||
await act(async () => new Promise((resolve) => setTimeout(resolve, 0)))
|
||||
await act(async () => resolveLateDialog())
|
||||
|
||||
expect(document.querySelector('[role="listbox"]')).toBeNull()
|
||||
expect(projectField().getAttribute('aria-expanded')).toBe('false')
|
||||
})
|
||||
@@ -1,36 +0,0 @@
|
||||
// @vitest-environment happy-dom
|
||||
import { readDirectSetupRequest } from '@/lib/workspace/creation/work-item/direct-work-item-setup-test-fixture'
|
||||
import { act, cleanup, fireEvent, render, screen } from '@testing-library/react'
|
||||
import { afterEach, expect, it } from 'vitest'
|
||||
import { i18n } from '@/i18n/i18n'
|
||||
import { useAppStore } from '@/store'
|
||||
import {
|
||||
requestDirectWorkItemSetup,
|
||||
type DirectWorkItemSetupRequest
|
||||
} from '@/lib/workspace/creation/work-item/direct-work-item-setup'
|
||||
import { DirectWorkItemSetupDialog } from './DirectWorkItemSetupDialog'
|
||||
import type { WorkspaceSetupInspection as DirectSetupInspection } from '@/lib/workspace/creation/setup/workspace-setup-preparation'
|
||||
|
||||
const initial = useAppStore.getState()
|
||||
afterEach(async () => {
|
||||
cleanup()
|
||||
await i18n.changeLanguage('en')
|
||||
useAppStore.getState().closeModal()
|
||||
useAppStore.setState(initial, true)
|
||||
})
|
||||
function request(): DirectWorkItemSetupRequest {
|
||||
return readDirectSetupRequest(useAppStore.getState().modalData.directSetup)
|
||||
}
|
||||
|
||||
it('ignores recovery after cancellation without closing the replacement modal', async () => {
|
||||
const retry = Promise.withResolvers<DirectSetupInspection>()
|
||||
const result = requestDirectWorkItemSetup('Repo', { kind: 'unavailable' }, () => retry.promise)
|
||||
const old = request()
|
||||
render(<DirectWorkItemSetupDialog request={old} />)
|
||||
fireEvent.click(screen.getByRole('button', { name: 'Retry' }))
|
||||
useAppStore.getState().openModal('quick-open', { section: 'appearance' })
|
||||
await expect(result).resolves.toBe('cancelled')
|
||||
await act(async () => retry.resolve({ kind: 'decided', decision: 'run' }))
|
||||
expect(old.isPending()).toBe(false)
|
||||
expect(useAppStore.getState().modalData).toEqual({ section: 'appearance' })
|
||||
})
|
||||
-47
@@ -1,47 +0,0 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { resolveLocalGitlabAvailable } from './use-smart-workspace-field-availability'
|
||||
|
||||
describe('resolveLocalGitlabAvailable', () => {
|
||||
const lastKnown = new Map([['host', true]])
|
||||
|
||||
it('uses the current preflight answer when it is for this context', () => {
|
||||
expect(
|
||||
resolveLocalGitlabAvailable({
|
||||
preflightStatusCurrent: true,
|
||||
glabInstalled: false,
|
||||
contextKey: 'host',
|
||||
lastKnown
|
||||
})
|
||||
).toBe(false)
|
||||
expect(
|
||||
resolveLocalGitlabAvailable({
|
||||
preflightStatusCurrent: true,
|
||||
glabInstalled: true,
|
||||
contextKey: 'wsl:Ubuntu',
|
||||
lastKnown
|
||||
})
|
||||
).toBe(true)
|
||||
})
|
||||
|
||||
it('keeps the last known answer while this context is being re-checked', () => {
|
||||
expect(
|
||||
resolveLocalGitlabAvailable({
|
||||
preflightStatusCurrent: false,
|
||||
glabInstalled: undefined,
|
||||
contextKey: 'host',
|
||||
lastKnown
|
||||
})
|
||||
).toBe(true)
|
||||
})
|
||||
|
||||
it('stays hidden for a context that was never checked', () => {
|
||||
expect(
|
||||
resolveLocalGitlabAvailable({
|
||||
preflightStatusCurrent: false,
|
||||
glabInstalled: undefined,
|
||||
contextKey: 'wsl:Debian',
|
||||
lastKnown
|
||||
})
|
||||
).toBe(false)
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user