From e4eecd847c598a887dfdc8885a2fd896dabbfafa Mon Sep 17 00:00:00 2001 From: OrcaWin Date: Fri, 13 Nov 2026 01:31:41 -0800 Subject: [PATCH] test: remove low-value workspace wrapper and notice cases (#41938) Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com> --- .../scripts/ci/sharding/ci-shard-timings.json | 3 - .../appearance/notice-surface-tokens.test.ts | 47 +------ .../setup/FullDiskAccessSetupPrompt.test.ts | 1 - .../NewWorkspaceComposerNameSection.test.tsx | 104 --------------- .../ProjectCombobox.dialog-handoff.test.tsx | 120 ------------------ .../setup/DirectWorkItemSetupDialog.test.tsx | 36 ------ ...smart-workspace-field-availability.test.ts | 47 ------- 7 files changed, 1 insertion(+), 357 deletions(-) delete mode 100644 src/renderer/src/components/new-workspace/composer/NewWorkspaceComposerNameSection.test.tsx delete mode 100644 src/renderer/src/components/new-workspace/project-selection/ProjectCombobox.dialog-handoff.test.tsx delete mode 100644 src/renderer/src/components/new-workspace/setup/DirectWorkItemSetupDialog.test.tsx delete mode 100644 src/renderer/src/components/new-workspace/source-search/use-smart-workspace-field-availability.test.ts diff --git a/config/scripts/ci/sharding/ci-shard-timings.json b/config/scripts/ci/sharding/ci-shard-timings.json index 6e57540494d..a2b1f69743b 100644 --- a/config/scripts/ci/sharding/ci-shard-timings.json +++ b/config/scripts/ci/sharding/ci-shard-timings.json @@ -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, diff --git a/src/renderer/src/app-shell/appearance/notice-surface-tokens.test.ts b/src/renderer/src/app-shell/appearance/notice-surface-tokens.test.ts index 79a1923a60c..e9c4e7936e4 100644 --- a/src/renderer/src/app-shell/appearance/notice-surface-tokens.test.ts +++ b/src/renderer/src/app-shell/appearance/notice-surface-tokens.test.ts @@ -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): 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 => 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) - } - }) }) diff --git a/src/renderer/src/components/feature-wall/setup/FullDiskAccessSetupPrompt.test.ts b/src/renderer/src/components/feature-wall/setup/FullDiskAccessSetupPrompt.test.ts index cf98f6efee3..6c6b954d0f1 100644 --- a/src/renderer/src/components/feature-wall/setup/FullDiskAccessSetupPrompt.test.ts +++ b/src/renderer/src/components/feature-wall/setup/FullDiskAccessSetupPrompt.test.ts @@ -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() }) }) diff --git a/src/renderer/src/components/new-workspace/composer/NewWorkspaceComposerNameSection.test.tsx b/src/renderer/src/components/new-workspace/composer/NewWorkspaceComposerNameSection.test.tsx deleted file mode 100644 index c410c3ca40a..00000000000 --- a/src/renderer/src/components/new-workspace/composer/NewWorkspaceComposerNameSection.test.tsx +++ /dev/null @@ -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 } }) => ( -
- {selectedSource?.label} - -
- ) -})) - -function branchProps(): React.ComponentProps { - 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() - - 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() - 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( -
- - - ) - - 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() - }) -}) diff --git a/src/renderer/src/components/new-workspace/project-selection/ProjectCombobox.dialog-handoff.test.tsx b/src/renderer/src/components/new-workspace/project-selection/ProjectCombobox.dialog-handoff.test.tsx deleted file mode 100644 index c5238a5c8c7..00000000000 --- a/src/renderer/src/components/new-workspace/project-selection/ProjectCombobox.dialog-handoff.test.tsx +++ /dev/null @@ -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(null) - useEffect(() => browseRef.current?.focus(), []) - return - } - }) - }) -) - -function LateDialogComposer(): React.JSX.Element { - const [adding, setAdding] = useState(false) - return ( - - - New workspace - setAdding(true)} - /> - {adding ? ( - - - - Add a project - - - - - ) : null} - - - ) -} - -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('[role="combobox"][aria-label="Project"]')! -} - -function addOption(): HTMLElement { - return Array.from(document.querySelectorAll('[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( - - - - ) - ) - // 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') -}) diff --git a/src/renderer/src/components/new-workspace/setup/DirectWorkItemSetupDialog.test.tsx b/src/renderer/src/components/new-workspace/setup/DirectWorkItemSetupDialog.test.tsx deleted file mode 100644 index 074fd0e043b..00000000000 --- a/src/renderer/src/components/new-workspace/setup/DirectWorkItemSetupDialog.test.tsx +++ /dev/null @@ -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() - const result = requestDirectWorkItemSetup('Repo', { kind: 'unavailable' }, () => retry.promise) - const old = request() - render() - 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' }) -}) diff --git a/src/renderer/src/components/new-workspace/source-search/use-smart-workspace-field-availability.test.ts b/src/renderer/src/components/new-workspace/source-search/use-smart-workspace-field-availability.test.ts deleted file mode 100644 index cf54f8a7fd5..00000000000 --- a/src/renderer/src/components/new-workspace/source-search/use-smart-workspace-field-availability.test.ts +++ /dev/null @@ -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) - }) -})