From 0f76063ce62eb1d523260f0e2bfbd7c6927eaed0 Mon Sep 17 00:00:00 2001 From: OrcaWin Date: Tue, 27 Oct 2026 04:58:01 -0700 Subject: [PATCH] test: remove 70 low-value cases and four suites (#34400) Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com> --- config/scripts/ci-shard-timings.json | 4 - .../bundled-skill-setup-labels.test.tsx | 219 ----------------- ...-source-control-integration-cards.test.tsx | 129 ---------- .../linear-agent-skill-install-cta.test.tsx | 74 ------ .../RepositorySourceControlAiSection.test.ts | 103 +------- ...sitory-source-control-ai-global-ux.test.ts | 13 - ...ry-source-control-ai-persist-queue.test.ts | 24 -- .../settings/settings-host-scope.test.ts | 30 --- .../settings/shortcut-groups.test.ts | 36 --- .../skill-setup-blocked-plan.test.tsx | 30 --- ...source-control-action-card-layout.test.tsx | 41 ---- ...linear-agent-skill-setup-reminders.test.ts | 10 - .../skills/skill-delete-copy.test.ts | 141 +---------- .../skill-package-checklist-items.test.ts | 114 --------- .../skills/skill-package-install-risk.test.ts | 74 ------ .../workspace-space-presentation.test.ts | 145 +---------- ...rkspace-cleanup-candidate-row-data.test.ts | 110 +-------- .../workspace-cleanup-candidate-row.test.tsx | 213 ---------------- .../workspace-cleanup-presentation.test.ts | 231 +----------------- .../workspace-cleanup-sort-header.test.tsx | 59 ----- 20 files changed, 7 insertions(+), 1793 deletions(-) delete mode 100644 src/renderer/src/components/settings/bundled-skill-setup-labels.test.tsx delete mode 100644 src/renderer/src/components/settings/cli-source-control-integration-cards.test.tsx delete mode 100644 src/renderer/src/components/skills/skill-package-checklist-items.test.ts delete mode 100644 src/renderer/src/components/skills/skill-package-install-risk.test.ts diff --git a/config/scripts/ci-shard-timings.json b/config/scripts/ci-shard-timings.json index ceb5f9b3a7b..b3f13546cde 100644 --- a/config/scripts/ci-shard-timings.json +++ b/config/scripts/ci-shard-timings.json @@ -8772,12 +8772,10 @@ "src/renderer/src/components/settings/agent-permission-conversion-persistence.test.tsx": 8787, "src/renderer/src/components/settings/bitbucket-integration-card.test.tsx": 421, "src/renderer/src/components/settings/browser-session-host-selection.test.ts": 43, - "src/renderer/src/components/settings/bundled-skill-setup-labels.test.tsx": 3388, "src/renderer/src/components/settings/bundled-skill-setup-result-copy.test.ts": 304, "src/renderer/src/components/settings/chat-naming-persistence.test.tsx": 3654, "src/renderer/src/components/settings/chat-settings-unsaved-guard.test.tsx": 1560, "src/renderer/src/components/settings/cli-install-failure.test.ts": 34, - "src/renderer/src/components/settings/cli-source-control-integration-cards.test.tsx": 695, "src/renderer/src/components/settings/codex-config-sync-warning.test.ts": 34, "src/renderer/src/components/settings/codex-session-source-home-control.test.tsx": 154, "src/renderer/src/components/settings/developer-permissions-search.test.ts": 178, @@ -9241,8 +9239,6 @@ "src/renderer/src/components/skills/skill-install-progress-state.test.tsx": 258, "src/renderer/src/components/skills/skill-install-provider-groups.test.ts": 32, "src/renderer/src/components/skills/skill-install-workspace-choices.test.ts": 55, - "src/renderer/src/components/skills/skill-package-checklist-items.test.ts": 123, - "src/renderer/src/components/skills/skill-package-install-risk.test.ts": 73, "src/renderer/src/components/skills/skill-share-link.test.ts": 38, "src/renderer/src/components/skills/skill-share-package-selection.test.ts": 48, "src/renderer/src/components/skills/skill-share-preview-summary.test.ts": 149, diff --git a/src/renderer/src/components/settings/bundled-skill-setup-labels.test.tsx b/src/renderer/src/components/settings/bundled-skill-setup-labels.test.tsx deleted file mode 100644 index 4cc636175b0..00000000000 --- a/src/renderer/src/components/settings/bundled-skill-setup-labels.test.tsx +++ /dev/null @@ -1,219 +0,0 @@ -// @vitest-environment happy-dom - -import { cleanup, fireEvent, render, screen, waitFor } from '@testing-library/react' -import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' -import type { BundledSkillOp } from '../../../../shared/bundled-skill-plan' -import { - doneResult, - planEntry, - planOf, - planOp -} from '../skills/bundled-skill-plan-fixtures.test-support' -import { AgentSkillSetupPanel } from './AgentSkillSetupPanel' -import { LinearAgentSkillInstallCta } from './linear-agent-skill-install-cta' - -const mocks = vi.hoisted(() => ({ plan: vi.fn(), execute: vi.fn(), refresh: vi.fn() })) -const TARGET = { kind: 'environment', environmentId: 'agents-host' } as const - -vi.mock('@/hooks/useActiveProjectSkillRuntime', () => ({ - useActiveProjectSkillRuntime: () => ({ bundledSkillTarget: TARGET }) -})) -vi.mock('@/hooks/installed-agent-skill-discovery', async (importOriginal) => ({ - ...(await importOriginal()), - notifyInstalledAgentSkillsChanged: vi.fn() -})) -vi.mock('@/hooks/useInstalledAgentSkills', async (importOriginal) => ({ - ...(await importOriginal()), - GLOBAL_AGENT_SKILL_SOURCE_KINDS: ['home'], - notifyInstalledAgentSkillsRefreshed: vi.fn(), - useInstalledAgentSkillNames: () => ({ - installed: true, - loading: false, - error: null, - skills: [], - refresh: mocks.refresh - }) -})) -vi.mock('@/hooks/useSkillFreshness', () => ({ refreshSkillFreshness: vi.fn() })) -vi.mock('@/runtime/runtime-bundled-skills-client', () => ({ - planBundledSkillsOnTarget: mocks.plan, - executeBundledSkillsOnTarget: mocks.execute -})) - -function operations( - name: string, - update: boolean, - agents: string[], - edited = false -): BundledSkillOp[] { - return [ - planOp(update ? 'update' : 'keep', `/home/u/.agents/skills/${name}`), - planOp('keep', `/home/u/.claude/skills/${name}`, { - observed: { kind: 'link', target: `/home/u/.agents/skills/${name}` } - }), - ...agents.map((agent) => - planOp('link', `/home/u/.${agent}/skills/${name}`, { - agents: agent === 'cursor' ? ['cursor'] : ['gemini'] - }) - ), - ...(edited - ? [ - planOp('leave', `/home/u/.factory/skills/${name}`, { - reason: 'edited', - agents: ['droid'], - replaceable: true - }) - ] - : []) - ] -} - -beforeEach(() => { - mocks.plan.mockReset() - mocks.execute.mockReset() - mocks.refresh.mockReset() -}) -afterEach(cleanup) - -for (const surface of ['panel', 'linear'] as const) { - describe(`${surface} setup plan labels`, () => { - it.each([ - { - update: false, - agents: ['cursor'], - button: 'Install for Cursor', - result: 'Installed for Cursor' - }, - { - update: false, - agents: ['cursor', 'gemini'], - button: 'Install for 2 more agents', - result: 'Installed for 2 more agents' - }, - { update: true, agents: [], button: 'Update', result: 'Updated' }, - { - update: true, - agents: ['cursor'], - button: 'Update', - result: 'Updated · Installed for Cursor' - }, - { - update: true, - agents: ['cursor', 'gemini'], - button: 'Update', - result: 'Updated · Installed for 2 more agents' - } - ])('labels $button and reports $result', async ({ update, agents, button, result }) => { - const name = surface === 'panel' ? 'orca-cli' : 'orca-linear' - const plan = planOf([planEntry(name, operations(name, update, agents))]) - mocks.plan.mockResolvedValue({ kind: 'plan', plan }) - mocks.execute.mockResolvedValue(doneResult(plan)) - render( - surface === 'panel' ? ( - - ) : ( - - ) - ) - fireEvent.click(await screen.findByRole('button', { name: button })) - expect(await screen.findByText(result, { exact: true })).toBeDefined() - expect(mocks.plan).toHaveBeenCalledWith(TARGET, { names: [name] }) - expect(mocks.execute).toHaveBeenCalledWith(TARGET, plan.planDigest, { names: [name] }) - }) - - it('shows the current verdict without an install button when every copy is current', async () => { - const name = surface === 'panel' ? 'orca-cli' : 'orca-linear' - mocks.plan.mockResolvedValue({ - kind: 'plan', - plan: planOf([planEntry(name, operations(name, false, []))]) - }) - render( - surface === 'panel' ? ( - - ) : ( - - ) - ) - await waitFor(() => - expect(screen.queryByRole('button', { name: /^(Install|Update)/ })).toBeNull() - ) - expect( - await screen.findByText(surface === 'panel' ? 'Up to date' : 'Installed', { exact: true }) - ).toBeDefined() - expect(mocks.execute).not.toHaveBeenCalled() - }) - }) -} - -it('keeps the edited-copy suffix after the mixed result', async () => { - const plan = planOf([planEntry('orca-cli', operations('orca-cli', true, ['cursor'], true))]) - mocks.plan.mockResolvedValue({ kind: 'plan', plan }) - mocks.execute.mockResolvedValue(doneResult(plan)) - render( - - ) - fireEvent.click(await screen.findByRole('button', { name: 'Update' })) - expect( - await screen.findByText( - 'Updated · Installed for Cursor · Update skipped in Droid: Skill edited outside Orca' - ) - ).toBeDefined() -}) - -it.each([ - { agents: ['cursor'], result: 'Installed for Cursor' }, - { agents: ['cursor', 'gemini'], result: 'Installed for 2 agents' } -])('uses first-install wording for $result', async ({ agents, result }) => { - const plan = planOf([ - planEntry('orca-cli', [ - planOp('write', '/home/u/.agents/skills/orca-cli'), - ...agents.map((agent) => - planOp('link', `/home/u/.${agent}/skills/orca-cli`, { - agents: agent === 'cursor' ? ['cursor'] : ['gemini'] - }) - ) - ]) - ]) - mocks.plan.mockResolvedValue({ kind: 'plan', plan }) - mocks.execute.mockResolvedValue(doneResult(plan)) - render( - - ) - await waitFor(() => - expect(screen.getByRole('button', { name: 'Install' }).hasAttribute('disabled')).toBe(false) - ) - fireEvent.click(screen.getByRole('button', { name: 'Install' })) - expect(await screen.findByText(result, { exact: true })).toBeDefined() -}) diff --git a/src/renderer/src/components/settings/cli-source-control-integration-cards.test.tsx b/src/renderer/src/components/settings/cli-source-control-integration-cards.test.tsx deleted file mode 100644 index dad12f297b0..00000000000 --- a/src/renderer/src/components/settings/cli-source-control-integration-cards.test.tsx +++ /dev/null @@ -1,129 +0,0 @@ -// @vitest-environment happy-dom - -import { act } from 'react' -import { createRoot, type Root } from 'react-dom/client' -import { afterEach, describe, expect, it, vi } from 'vitest' -import { getExecutionHostLabel } from '../../../../shared/execution-host' -import { - GitHubIntegrationCard, - GitLabIntegrationCard -} from './cli-source-control-integration-cards' - -const LOCAL_HOST_LABEL = getExecutionHostLabel('local') - -type StoreState = { - settings: { activeRuntimeEnvironmentId: string | null } - openSettingsPage: () => void - openSettingsTarget: (target: { pane: string; repoId: string | null }) => void -} - -const mocks = vi.hoisted(() => ({ - store: { current: null as StoreState | null }, - preflight: { - statuses: { - ghStatus: 'connected', - glabStatus: 'connected', - bitbucketStatus: 'not-configured', - azureDevOpsStatus: 'not-configured', - giteaStatus: 'not-configured', - bitbucketAccount: null, - azureDevOpsAccount: null, - giteaAccount: null - }, - unavailable: false, - refresh: vi.fn() - } -})) - -vi.mock('@/store', () => ({ - useAppStore: (selector: (state: StoreState) => unknown) => { - if (!mocks.store.current) { - throw new Error('Store state was not installed') - } - return selector(mocks.store.current) - } -})) - -vi.mock('./source-control-preflight-card-status', () => ({ - usePreflightCardStatuses: () => mocks.preflight -})) - -let root: Root | null = null -let container: HTMLDivElement | null = null - -async function renderCard(card: React.ReactNode): Promise { - container = document.createElement('div') - document.body.appendChild(container) - root = createRoot(container) - await act(async () => { - root?.render(card) - }) - return container -} - -describe('CLI source-control integration card account scope', () => { - afterEach(async () => { - if (root) { - await act(async () => { - root?.unmount() - }) - } - root = null - container?.remove() - container = null - mocks.store.current = null - mocks.preflight.statuses.ghStatus = 'connected' - mocks.preflight.statuses.glabStatus = 'connected' - mocks.preflight.unavailable = false - mocks.preflight.refresh.mockClear() - }) - - it('shows local-client ownership for connected GitHub CLI credentials', async () => { - const openSettingsPage = vi.fn() - const openSettingsTarget = vi.fn() - mocks.store.current = { - settings: { activeRuntimeEnvironmentId: null }, - openSettingsPage, - openSettingsTarget - } - - const rendered = await renderCard() - - expect(rendered.textContent).toContain('GitHub') - expect(rendered.textContent).toContain('Connected') - expect(rendered.textContent).toContain(`Account scope: ${LOCAL_HOST_LABEL}`) - expect(rendered.textContent).toContain( - 'Credentials and account checks for this provider are owned by this desktop client. To use a server, change Settings > Remote Orca Servers > New projects open on.' - ) - await act(async () => { - Array.from(rendered.querySelectorAll('button')) - .find((button) => button.textContent === 'Open Remote Servers') - ?.dispatchEvent(new MouseEvent('click', { bubbles: true })) - }) - - expect(openSettingsPage).toHaveBeenCalledTimes(1) - expect(openSettingsTarget).toHaveBeenCalledWith({ - pane: 'servers', - repoId: null, - sectionId: 'default-runtime' - }) - }) - - it('shows remote-server ownership for GitLab CLI credential checks', async () => { - mocks.store.current = { - settings: { activeRuntimeEnvironmentId: 'runtime-1' }, - openSettingsPage: vi.fn(), - openSettingsTarget: vi.fn() - } - mocks.preflight.statuses.glabStatus = 'not-authenticated' - - const rendered = await renderCard() - - expect(rendered.textContent).toContain('GitLab') - expect(rendered.textContent).toContain('Account scope: Remote server: runtime-1') - expect(rendered.textContent).toContain( - 'Credentials and account checks for this provider are owned by this remote server. To use another host, change Settings > Remote Orca Servers > New projects open on.' - ) - expect(rendered.textContent).toContain('glab auth login') - }) -}) diff --git a/src/renderer/src/components/settings/linear-agent-skill-install-cta.test.tsx b/src/renderer/src/components/settings/linear-agent-skill-install-cta.test.tsx index d5018106c33..1c3ad44e0ad 100644 --- a/src/renderer/src/components/settings/linear-agent-skill-install-cta.test.tsx +++ b/src/renderer/src/components/settings/linear-agent-skill-install-cta.test.tsx @@ -108,43 +108,6 @@ describe('LinearAgentSkillInstallCta', () => { container = null }) - it('offers Install and the explanation when the skill is missing', async () => { - const rendered = await renderCta() - - expect(rendered.textContent).toContain('Agent skill:') - expect(rendered.textContent).toContain('orca-linear') - expect(rendered.textContent).toContain('Not installed') - expect(rendered.textContent).toContain( - 'Full guided setup (connect + skill + visibility) is under Settings → Task Sources.' - ) - expect(rendered.textContent).toContain('Install skill') - expect(rendered.textContent).not.toContain('Copy command') - }) - - it("installs the skill directly on the agents' host", async () => { - const rendered = await renderCta() - - await act(async () => { - Array.from(rendered.querySelectorAll('button')) - .find((button) => button.textContent === 'Install skill') - ?.dispatchEvent(new MouseEvent('click', { bubbles: true })) - }) - - expect(mocks.plan).toHaveBeenCalledWith({ kind: 'local' }, { names: ['orca-linear'] }) - expect(rendered.textContent).toContain('Update Orca on the server to install its skills.') - }) - - it('shows a subtle confirmation and Update when installed', async () => { - mocks.skillState.installed = true - mocks.skillState.skills = [discoveredSkill({ name: 'orca-linear' })] - - const rendered = await renderCta() - - expect(rendered.textContent).toContain('Installed') - expect(rendered.textContent).toContain('Update') - expect(rendered.textContent).not.toContain('Not installed') - }) - it('updates through the legacy skill name when only linear-tickets is installed', async () => { mocks.skillState.installed = true mocks.skillState.skills = [ @@ -166,16 +129,6 @@ describe('LinearAgentSkillInstallCta', () => { expect(mocks.plan).toHaveBeenCalledWith({ kind: 'local' }, { names: ['linear-tickets'] }) }) - it('notes that remote agent environments need their own setup', async () => { - const rendered = await renderCta({ - settings: { activeRuntimeEnvironmentId: 'runtime-1', terminalWindowsShell: '' } - }) - - expect(rendered.textContent).toContain( - 'This installs host setup; remote agent environments may need separate setup.' - ) - }) - it('keeps the resolved WSL project on the old command flow', async () => { mocks.agentRuntime = { runtime: 'wsl', wslDistro: 'Debian', label: 'WSL Debian' } const rendered = await renderCta() @@ -189,31 +142,4 @@ describe('LinearAgentSkillInstallCta', () => { expect.objectContaining({ discoveryTarget: { runtime: 'wsl', wslDistro: 'Debian' } }) ) }) - - it('uses project host discovery even when legacy global settings still prefer WSL', async () => { - mocks.agentRuntime = { runtime: 'host', label: 'Windows' } - await renderCta({ - settings: { - localAgentRuntime: 'wsl', - localAgentWslDistro: 'Ubuntu', - terminalWindowsShell: 'wsl.exe' - } - }) - expect(mocks.useInstalledAgentSkillNames).toHaveBeenCalledWith( - expect.anything(), - expect.objectContaining({ discoveryTarget: undefined }) - ) - }) - - it('re-checks the skill scan on demand', async () => { - const rendered = await renderCta() - - await act(async () => { - Array.from(rendered.querySelectorAll('button')) - .find((button) => button.textContent?.includes('Re-check')) - ?.dispatchEvent(new MouseEvent('click', { bubbles: true })) - }) - - expect(mocks.skillState.refresh).toHaveBeenCalledTimes(1) - }) }) diff --git a/src/renderer/src/components/settings/repository/source-control/RepositorySourceControlAiSection.test.ts b/src/renderer/src/components/settings/repository/source-control/RepositorySourceControlAiSection.test.ts index 874412263e2..236b88b79be 100644 --- a/src/renderer/src/components/settings/repository/source-control/RepositorySourceControlAiSection.test.ts +++ b/src/renderer/src/components/settings/repository/source-control/RepositorySourceControlAiSection.test.ts @@ -4,17 +4,9 @@ import { act, render, cleanup, fireEvent } from '@testing-library/react' import '../../__fixtures__/settings-persistence-test-fixture' import React from 'react' import type { TuiAgent } from '../../../../../../shared/tui-agent' -import { renderToStaticMarkup } from 'react-dom/server' import { normalizeRepoSourceControlAiOverrides } from '../../../../../../shared/source-control-ai-repo-settings' -import type { - RepoSourceControlAiOverrides, - SourceControlAiSettings -} from '../../../../../../shared/source-control-ai-types' -import { - SOURCE_CONTROL_ACTION_IDS, - setSourceControlActionAgentDefault, - type SourceControlActionId -} from '../../../../../../shared/source-control-ai-actions' +import type { RepoSourceControlAiOverrides } from '../../../../../../shared/source-control-ai-types' +import { setSourceControlActionAgentDefault } from '../../../../../../shared/source-control-ai-actions' import { computeActionDirtyById, dropRepoLegacyInstructionForAction, @@ -29,7 +21,6 @@ import { import { sourceControlActionRecipeMatchesTarget } from '../../../right-sidebar/source-control-action-recipe-match' import { SourceControlActionAgentPicker } from '../../SourceControlActionAgentPicker' import { RepositorySourceControlAiActionRows } from './RepositorySourceControlAiActionRows' -import { RepositorySourceControlAiEnablement } from './RepositorySourceControlAiEnablement' import { SourceControlAiActionRecipeDefaults } from '../../SourceControlAiActionRecipeDefaults' import { useAppStore } from '../../../../store/index' import { TooltipProvider } from '../../../ui/tooltip' @@ -103,24 +94,6 @@ describe('saved chat-or-CLI pick', () => { }) }) -describe('RepositorySourceControlAiEnablement', () => { - it('describes repo enablement as Source Control AI action visibility', () => { - const markup = renderToStaticMarkup( - React.createElement(RepositorySourceControlAiEnablement, { - value: false, - source: { enabled: true } as SourceControlAiSettings, - onChange: () => {} - }) - ) - - expect(markup).toContain('Show Source Control AI actions') - expect(markup).toContain('Controls whether Source Control AI buttons are shown') - expect(markup).toContain('separate features follows those features' settings') - expect(markup).toContain('Show') - expect(markup).not.toContain('Source Control AI enabled') - }) -}) - describe('RepositorySourceControlAiActionRows', () => { it.each([ { agent: 'codex', detected: ['codex'], surface: undefined }, @@ -270,67 +243,6 @@ describe('RepositorySourceControlAiActionRows', () => { expect(onEdit).not.toHaveBeenCalled() } ) - - it.each([undefined, null, '', '--repo'])( - 'renders save controls and argument ownership for %s', - (repoAgentArgs) => { - const actionDirtyById = Object.fromEntries( - SOURCE_CONTROL_ACTION_IDS.map((actionId) => [actionId, actionId === 'fixCommitFailure']) - ) as Record - const source: SourceControlAiSettings = { - enabled: true, - agentId: null, - selectedModelByAgent: {}, - selectedThinkingByModel: {}, - customAgentCommand: '', - instructionsByOperation: {}, - actions: { fixCommitFailure: { agentId: null, agentArgs: '--global' } } - } - - const rows = React.createElement(RepositorySourceControlAiActionRows, { - repoId: 'repo-1', - repoAi: { - actionOverrides: { - fixCommitFailure: { - agentId: null, - commandInputTemplate: '{basePrompt}\n\nrepo', - ...(repoAgentArgs !== undefined ? { agentArgs: repoAgentArgs } : {}) - } - } - }, - source, - defaultTuiAgent: 'codex', - savingActionIds: {}, - actionDirtyById, - onActionModeChange: () => {}, - onActionAgentChange: () => {}, - onActionLaunchPreferencesChange: () => {}, - onActionTemplateChange: () => {}, - onActionAgentArgsChange: () => {}, - onAppendVariable: () => {}, - onActionDiscard: () => {}, - onActionSave: () => {} - }) - const markup = renderToStaticMarkup(React.createElement(TooltipProvider, null, rows)) - - expect(markup).toContain('Commit failure fixes') - expect(markup).toContain('Push failure fixes') - expect(markup).toContain('Unsaved changes') - expect(markup).toContain('Discard') - expect(markup).toContain('Save') - expect(markup).toContain('repo-repo-1-source-control-ai-fixCommitFailure') - const card = new DOMParser() - .parseFromString(markup, 'text/html') - .getElementById('repo-repo-1-source-control-ai-fixCommitFailure') - expect(card?.textContent).toContain( - repoAgentArgs === undefined - ? 'Global custom args' - : repoAgentArgs - ? 'Repository custom args' - : 'No args' - ) - } - ) }) describe('dropRepoLegacyInstructionForAction', () => { @@ -353,17 +265,6 @@ describe('dropRepoLegacyInstructionForAction', () => { commandInputTemplate: '{basePrompt}\n\nUse PR style.' }) }) - - it('leaves launch action recipes alone because they have no legacy instruction key', () => { - const value = { - instructionsByOperation: { commitMessage: 'Use repo style.' }, - actionOverrides: { - fixChecks: { commandInputTemplate: '{basePrompt}' } - } - } - - expect(dropRepoLegacyInstructionForAction(value, 'fixChecks')).toBe(value) - }) }) it.each(['', '--model raw'])( diff --git a/src/renderer/src/components/settings/repository/source-control/repository-source-control-ai-global-ux.test.ts b/src/renderer/src/components/settings/repository/source-control/repository-source-control-ai-global-ux.test.ts index ce83937c83a..4d2216e5648 100644 --- a/src/renderer/src/components/settings/repository/source-control/repository-source-control-ai-global-ux.test.ts +++ b/src/renderer/src/components/settings/repository/source-control/repository-source-control-ai-global-ux.test.ts @@ -443,19 +443,6 @@ describe('editing one repository action', () => { expect(set().launchPreferences).toBeUndefined() }) - it('makes the copied model owned by the action, so it outranks the raw model it yielded to', () => { - const legacy = { actionOverrides: { fixCommitFailure: { agentArgs: '--model gpt-5.6-sol' } } } - const stored = { - settings, - repo: { sourceControlAi: legacy }, - actionId: 'fixCommitFailure' - } as const - expect(resolveSourceControlActionRecipe(stored).launchPreferencesYieldToAgentArgs).toBe(true) - const edited = reload(editPrompt(legacy, 'fixCommitFailure'), 'fixCommitFailure') - expect(edited).toMatchObject({ commandInputTemplate: 'mine', launchPreferences: terra }) - expect(edited.launchPreferencesYieldToAgentArgs).toBeUndefined() - }) - it('saves a prompt edit without authoring args the user never typed or touching a sibling', async () => { const sibling = { agentArgs: '--repo' } const cleared = { diff --git a/src/renderer/src/components/settings/repository/source-control/repository-source-control-ai-persist-queue.test.ts b/src/renderer/src/components/settings/repository/source-control/repository-source-control-ai-persist-queue.test.ts index 8fffe2ab71c..2e8b73b7d9d 100644 --- a/src/renderer/src/components/settings/repository/source-control/repository-source-control-ai-persist-queue.test.ts +++ b/src/renderer/src/components/settings/repository/source-control/repository-source-control-ai-persist-queue.test.ts @@ -38,14 +38,6 @@ describe('createRepoAiPersistQueue', () => { h = makeQueue() }) - it('persists a changed value and stores the normalized saved result', async () => { - h.updateRepo.mockResolvedValue(true) - const ok = await h.queue.persistTransform((base) => ({ ...base, enabled: true })) - expect(ok).toBe(true) - expect(h.updateRepo).toHaveBeenCalledTimes(1) - expect(h.getPersisted()).toMatchObject({ enabled: true }) - }) - it('skips the backend write when the transform is a no-op', async () => { h.updateRepo.mockResolvedValue(true) const ok = await h.queue.persistTransform((base) => base) @@ -128,20 +120,4 @@ describe('createRepoAiPersistQueue', () => { // Completing a stale write must not seed the new repo's local persisted base. expect(h.setPersisted).not.toHaveBeenCalled() }) - - it('does not apply setPersisted when the repo switches during an in-flight updateRepo', async () => { - let release: (ok: boolean) => void = () => {} - h.updateRepo.mockImplementation( - () => - new Promise((resolve) => { - release = resolve - }) - ) - const pending = h.queue.persistTransform((base) => ({ ...base, enabled: true })) - h.setRepoId('repo-2') - release(true) - await expect(pending).resolves.toBe(true) - expect(h.setPersisted).not.toHaveBeenCalled() - expect(h.onError).not.toHaveBeenCalled() - }) }) diff --git a/src/renderer/src/components/settings/settings-host-scope.test.ts b/src/renderer/src/components/settings/settings-host-scope.test.ts index c6b6aab4008..693bfe235cc 100644 --- a/src/renderer/src/components/settings/settings-host-scope.test.ts +++ b/src/renderer/src/components/settings/settings-host-scope.test.ts @@ -4,36 +4,6 @@ import { resolveSettingsHostScope } from './settings-host-scope' const saved = ['env-a', 'env-b'] describe('resolveSettingsHostScope', () => { - it('starts on the default host setting until the user picks one', () => { - expect( - resolveSettingsHostScope({ - choice: null, - defaultEnvironmentId: ' env-a ', - savedEnvironmentIds: saved, - catalogHydrated: true - }) - ).toEqual({ target: { kind: 'environment', environmentId: 'env-a' }, available: true }) - }) - - it('keeps the picked host when the default host setting changes', () => { - expect( - resolveSettingsHostScope({ - choice: { kind: 'local' }, - defaultEnvironmentId: 'env-a', - savedEnvironmentIds: saved, - catalogHydrated: true - }) - ).toEqual({ target: { kind: 'local' }, available: true }) - expect( - resolveSettingsHostScope({ - choice: { kind: 'environment', environmentId: 'env-b' }, - defaultEnvironmentId: null, - savedEnvironmentIds: saved, - catalogHydrated: true - }) - ).toEqual({ target: { kind: 'environment', environmentId: 'env-b' }, available: true }) - }) - it('marks a removed server unavailable instead of falling back to this computer', () => { expect( resolveSettingsHostScope({ diff --git a/src/renderer/src/components/settings/shortcut-groups.test.ts b/src/renderer/src/components/settings/shortcut-groups.test.ts index af69731d932..3f7aa7d95f7 100644 --- a/src/renderer/src/components/settings/shortcut-groups.test.ts +++ b/src/renderer/src/components/settings/shortcut-groups.test.ts @@ -1,44 +1,8 @@ import { describe, expect, it } from 'vitest' -import type { KeybindingDefinition } from '../../../../shared/keybindings' import type { ActivePluginCommand } from '@/store/plugin-panels' import { buildShortcutDefinitionCatalog } from './shortcut-definition-catalog' -import { groupDefinitions } from './shortcut-groups' - -const pluginDefinition: KeybindingDefinition = { - id: 'plugin:orca-samples.tasks/open', - title: 'Open Tasks — Tasks', - group: 'Plugins', - scope: 'global', - searchKeywords: ['plugin', 'tasks'], - defaultBindings: { - darwin: ['Mod+Alt+T'], - linux: ['Mod+Alt+T'], - win32: ['Mod+Alt+T'] - } -} describe('shortcut groups', () => { - it('includes dynamic plugin command definitions in Settings', () => { - expect(groupDefinitions([], [pluginDefinition])).toEqual( - expect.arrayContaining([ - expect.objectContaining({ - title: 'Plugins', - items: [pluginDefinition] - }) - ]) - ) - }) - - it('exposes the agent dashboard toggle as a customizable Global row', () => { - const global = groupDefinitions([]).find((group) => group.title === 'Global') - - expect(global?.items).toEqual( - expect.arrayContaining([ - expect.objectContaining({ id: 'dashboard.toggle', title: 'Toggle Agent Dashboard' }) - ]) - ) - }) - it('reports a plugin default that shadows a built-in shortcut', () => { const command: ActivePluginCommand = { pluginKey: 'orca-samples.tasks', diff --git a/src/renderer/src/components/settings/skill-setup-blocked-plan.test.tsx b/src/renderer/src/components/settings/skill-setup-blocked-plan.test.tsx index e9057032eb6..32b440f8b11 100644 --- a/src/renderer/src/components/settings/skill-setup-blocked-plan.test.tsx +++ b/src/renderer/src/components/settings/skill-setup-blocked-plan.test.tsx @@ -115,36 +115,6 @@ function renderSurface( ) } -for (const surface of ['panel', 'guided-panel', 'linear', 'browser', 'capabilities'] as const) { - describe(`${surface} blocked setup`, () => { - describe.each([ - [ - 'update-server', - 'Update Orca on the server to install its skills.', - 'Update Orca on the server' - ], - ['connect-host', 'Connect to this SSH host to install its skills.', 'Connect the SSH host'] - ] as const)('%s', (reason, message, pill) => { - it.each(surface === 'browser' || surface === 'capabilities' ? [false] : [false, true])( - 'explains the prerequisite before any click (installed: %s)', - async (installed) => { - mocks.installed = installed - mocks.plan.mockResolvedValue({ kind: 'unsupported', reason }) - renderSurface(surface) - - expect(await screen.findByText(message)).toBeDefined() - expect(screen.queryByRole('button', { name: /^(Install|Update|Replace)/ })).toBeNull() - expect(screen.queryByText('Check failed')).toBeNull() - if (surface === 'panel' || surface === 'guided-panel' || surface === 'linear') { - expect(screen.getByText(pill, { exact: true })).toBeDefined() - } - expect(mocks.execute).not.toHaveBeenCalled() - } - ) - }) - }) -} - for (const surface of ['panel', 'linear', 'browser', 'capabilities'] as const) { describe(`${surface} blocked setup`, () => { it.each([false, true])( diff --git a/src/renderer/src/components/settings/source-control-action-card-layout.test.tsx b/src/renderer/src/components/settings/source-control-action-card-layout.test.tsx index 238685e0faa..adbfbd8b184 100644 --- a/src/renderer/src/components/settings/source-control-action-card-layout.test.tsx +++ b/src/renderer/src/components/settings/source-control-action-card-layout.test.tsx @@ -17,7 +17,6 @@ import { } from '@/runtime/host-structured-agents' import { TooltipProvider } from '../ui/tooltip' import { SourceControlActionRecipeRow } from './SourceControlActionRecipeRow' -import { shouldShowSourceControlActionArguments } from './source-control-action-arguments-visibility' vi.mock('@/runtime/runtime-rpc-client', () => ({ callRuntimeRpc: vi.fn(async () => ({ agents: [{ agent: 'grok', capabilities: {} }] })) @@ -67,27 +66,6 @@ beforeEach(() => { }) afterEach(resetHostStructuredAgentsForTests) -it.each([ - ['chat on', 'claude', true, undefined, false], - ['chat off', 'claude', false, undefined, true], - ['provider off', 'claude', true, { claude: false }, true], - ['no chat support', 'goose', true, undefined, true] -] as const)('%s: shows Arguments only for the CLI', (_name, agent, chat, providers, visible) => { - configure({ experimentalNativeChat: chat, nativeChatProviders: providers }) - render() - expect(Boolean(screen.queryByRole('textbox', { name: 'Arguments' }))).toBe(visible) - expect(screen.getByText('Command template')).toBeDefined() -}) - -it('hides an unresolved launch even with chat off, but always shows text action Arguments', () => { - configure({ experimentalNativeChat: false }) - useAppStore.setState({ localDetectedAgentIdsByContext: {} }) - const view = render() - expect(screen.queryByRole('textbox', { name: 'Arguments' })).toBeNull() - view.rerender() - expect(screen.getByDisplayValue('--saved')).toBeDefined() -}) - it('keeps the draft and chip while Model/Effort controls stay hidden', () => { configure({ experimentalNativeChat: false }) render() @@ -124,22 +102,3 @@ it('reacts to the real host registry when another agent gains chat support', asy ) expect(screen.queryByRole('textbox', { name: 'Arguments' })).toBeNull() }) - -it('honors an explicit future surface only after an agent resolves', () => { - const input = { - agent: 'claude', - settings: { experimentalNativeChat: true }, - chatAgents: ['claude'] - } as const - expect(shouldShowSourceControlActionArguments({ ...input, launchKind: 'terminal' })).toBe(true) - expect( - shouldShowSourceControlActionArguments({ - ...input, - settings: { experimentalNativeChat: false }, - launchKind: 'chat' - }) - ).toBe(false) - expect( - shouldShowSourceControlActionArguments({ ...input, agent: null, launchKind: 'terminal' }) - ).toBe(false) -}) diff --git a/src/renderer/src/components/sidebar/linear-agent-skill-setup-reminders.test.ts b/src/renderer/src/components/sidebar/linear-agent-skill-setup-reminders.test.ts index 37f87a905fb..9072854977f 100644 --- a/src/renderer/src/components/sidebar/linear-agent-skill-setup-reminders.test.ts +++ b/src/renderer/src/components/sidebar/linear-agent-skill-setup-reminders.test.ts @@ -1,7 +1,6 @@ import { afterEach, describe, expect, it } from 'vitest' import { MAX_LINEAR_AGENT_SKILL_SETUP_REMINDER_RUNTIME_KEYS, - createLinearAgentSkillSetupActivationId, getExistingLinearAgentSkillSetupReminderState, getLinearAgentSkillSetupReminderState, getLinearAgentSkillSetupReminderStateCountForTests, @@ -77,13 +76,4 @@ describe('linear agent skill setup reminders', () => { expect(getExistingLinearAgentSkillSetupReminderState('missing')).toBeUndefined() expect(getLinearAgentSkillSetupReminderStateCountForTests()).toBe(0) }) - - it('resets activation ids with reminder state for tests', () => { - expect(createLinearAgentSkillSetupActivationId()).toBe('linear-agent-skill-setup-0') - expect(createLinearAgentSkillSetupActivationId()).toBe('linear-agent-skill-setup-1') - - resetLinearAgentSkillSetupReminderState() - - expect(createLinearAgentSkillSetupActivationId()).toBe('linear-agent-skill-setup-0') - }) }) diff --git a/src/renderer/src/components/skills/skill-delete-copy.test.ts b/src/renderer/src/components/skills/skill-delete-copy.test.ts index e9a7d540874..6b9e0a31c36 100644 --- a/src/renderer/src/components/skills/skill-delete-copy.test.ts +++ b/src/renderer/src/components/skills/skill-delete-copy.test.ts @@ -1,148 +1,11 @@ -import { describe, expect, it, vi } from 'vitest' +import { expect, it, vi } from 'vitest' import type { SkillDeletePlan } from '../../../../shared/skill-delete-contract' -import { - skillDeleteBlockedLines, - skillDeletePlacementSummary, - skillDeleteResultLines, - skillDeleteRetainedSourceLines -} from './skill-delete-copy' +import { skillDeletePlacementSummary } from './skill-delete-copy' function plan(skills: SkillDeletePlan['skills']): SkillDeletePlan { return { operationId: 'op', skills } } -describe('skillDeletePlacementSummary', () => { - it('summarizes folders, links, and the roots they sit in', () => { - const summary = skillDeletePlacementSummary( - plan([ - { - id: 'a', - name: 'demo', - canonicalPath: '/root/demo/SKILL.md', - placements: [ - { - path: '/home/.agents/skills/demo', - kind: 'canonical', - rootLabel: 'Agent skills home' - }, - { path: '/home/.claude/skills/demo', kind: 'alias-dir', rootLabel: 'Claude Code home' } - ] - } - ]) - ) - expect(summary).toContain('1') - expect(summary).toContain('Agent skills home') - expect(summary).toContain('Claude Code home') - }) - - it('drops the zero half so a link-only delete never reads "0 folders"', () => { - const summary = skillDeletePlacementSummary( - plan([ - { - id: 'a', - name: 'demo', - canonicalPath: '/home/.local/share/devex/skills/demo/SKILL.md', - placements: [ - { path: '/repo/.agents/skills/demo', kind: 'alias-dir', rootLabel: 'Repo app .agents' } - ] - } - ]) - ) - expect(summary).not.toContain('0') - expect(summary).toContain('link') - expect(summary).not.toContain('folder') - }) - - it('is null when the plan removes nothing', () => { - expect( - skillDeletePlacementSummary( - plan([ - { - id: 'a', - name: 'demo', - canonicalPath: '/elsewhere/demo/SKILL.md', - placements: [], - blocked: 'unowned' - } - ]) - ) - ).toBeNull() - }) -}) - -describe('skillDeleteRetainedSourceLines', () => { - it('says where the content stays when only links are removed', () => { - const lines = skillDeleteRetainedSourceLines( - plan([ - { - id: 'a', - name: 'demo', - canonicalPath: '/home/.local/share/devex/skills/demo/SKILL.md', - placements: [ - { path: '/repo/.agents/skills/demo', kind: 'alias-dir', rootLabel: 'Repo app .agents' } - ] - } - ]) - ) - expect(lines).toHaveLength(1) - expect(lines[0]).toContain('/home/.local/share/devex/skills/demo') - }) - - it('stays silent when a real folder is being removed', () => { - expect( - skillDeleteRetainedSourceLines( - plan([ - { - id: 'a', - name: 'demo', - canonicalPath: '/home/.agents/skills/demo/SKILL.md', - placements: [ - { - path: '/home/.agents/skills/demo', - kind: 'canonical', - rootLabel: 'Agent skills home' - } - ] - } - ]) - ) - ).toEqual([]) - }) -}) - -describe('skillDeleteBlockedLines', () => { - it('groups by typed reason rather than one merged skipped line', () => { - const lines = skillDeleteBlockedLines( - plan([ - { id: 'a', name: 'a', canonicalPath: '/p', placements: [], blocked: 'bundled' }, - { id: 'b', name: 'b', canonicalPath: '/p', placements: [], blocked: 'bundled' }, - { id: 'c', name: 'c', canonicalPath: '/p', placements: [], blocked: 'plugin' } - ]) - ) - expect(lines).toHaveLength(2) - expect(lines.some((line) => line.includes('2') && line.includes('Bundled'))).toBe(true) - }) -}) - -describe('skillDeleteResultLines', () => { - it('omits deleted rows and keeps one line per distinct outcome', () => { - const lines = skillDeleteResultLines([ - { id: 'a', name: 'a', status: 'deleted', removedPaths: ['/x'] }, - { id: 'b', name: 'b', status: 'skipped', blocked: 'unowned', removedPaths: [] }, - { id: 'c', name: 'c', status: 'skipped', blocked: 'unowned', removedPaths: [] }, - { id: 'd', name: 'd', status: 'busy', removedPaths: [] } - ]) - expect(lines.map((line) => line.key)).toEqual(['skipped:unowned', 'busy']) - expect(lines[0].label).toContain('2') - }) - - it('renders nothing when every skill was deleted', () => { - expect( - skillDeleteResultLines([{ id: 'a', name: 'a', status: 'deleted', removedPaths: ['/x'] }]) - ).toEqual([]) - }) -}) - it('sorts deletion roots once with unchanged case, accent, and number ordering', () => { const labels = ['éclair', 'Eclair', 'item2', 'item10', 'Ångström', 'zebra', 'İstanbul'] const expected = [...labels].sort((a, b) => diff --git a/src/renderer/src/components/skills/skill-package-checklist-items.test.ts b/src/renderer/src/components/skills/skill-package-checklist-items.test.ts deleted file mode 100644 index 55df248633a..00000000000 --- a/src/renderer/src/components/skills/skill-package-checklist-items.test.ts +++ /dev/null @@ -1,114 +0,0 @@ -import { describe, expect, it } from 'vitest' -import type { SkillCloudVersion } from '../../../../shared/skill-cloud-contract' -import { checklistItemSummary, checklistItemsFromVersion } from './skill-package-checklist-items' - -type ManifestFile = { - path: string - size: number - executable: boolean - classification: 'binary' | 'text' - sha256: string - identitySha256: string -} - -function file(path: string, executable = false): ManifestFile { - return { - path, - size: 1024, - executable, - classification: 'text' as const, - sha256: 'a'.repeat(64), - identitySha256: 'a'.repeat(64) - } -} - -function singleVersion(): SkillCloudVersion { - return { - packageId: 'pkg_1', - versionId: 'ver_1', - name: 'published-name', - description: 'Published description', - packageDigest: 'a'.repeat(64), - archiveSha256: 'b'.repeat(64), - compressedBytes: 128, - createdAt: '2026-08-11T00:00:00.000Z', - releaseNotes: '', - manifest: { - schemaVersion: 1, - packageId: 'pkg_1', - versionId: 'ver_1', - name: 'manifest-name', - description: 'Manifest description', - createdAt: '2026-08-11T00:00:00.000Z', - files: [file('SKILL.md')], - packageDigest: 'a'.repeat(64) - } - } -} - -describe('checklistItemsFromVersion', () => { - it('turns a single-skill package into a one-row checklist', () => { - expect(checklistItemsFromVersion(singleVersion())).toEqual([ - { - id: 'manifest-name', - name: 'published-name', - description: 'Published description', - files: [file('SKILL.md')] - } - ]) - }) - - it('keeps each bundled skill as its own row', () => { - const bundled = { - ...singleVersion(), - manifest: { - schemaVersion: 1 as const, - packageId: 'pkg_1', - versionId: 'ver_1', - bundleName: 'shared-skills', - description: 'Synthetic bundle description', - createdAt: '2026-08-11T00:00:00.000Z', - bundleDigest: 'c'.repeat(64), - skills: [ - { - id: 'a', - name: 'alpha', - description: 'Alpha', - digest: 'd'.repeat(64), - files: [file('SKILL.md')] - }, - { - id: 'b', - name: 'beta', - description: 'Beta', - digest: 'e'.repeat(64), - files: [file('SKILL.md')] - } - ] - } - } as unknown as SkillCloudVersion - expect(checklistItemsFromVersion(bundled).map((item) => item.name)).toEqual(['alpha', 'beta']) - }) -}) - -describe('checklistItemSummary', () => { - it('keeps instruction-only and supporting-file summaries simple', () => { - expect(checklistItemSummary([file('SKILL.md')])).toEqual({ - label: '1 file' - }) - expect(checklistItemSummary([file('SKILL.md'), file('references/guide.md')])).toEqual({ - label: '2 files' - }) - }) - - it('keeps runnable and binary classifications out of the row summary', () => { - expect(checklistItemSummary([file('SKILL.md'), file('scripts/setup.sh')])).toEqual({ - label: '2 files' - }) - expect(checklistItemSummary([file('bin/tool', true)])).toEqual({ label: '1 file' }) - const binary = { ...file('assets/logo.png'), classification: 'binary' as const } - expect(checklistItemSummary([file('SKILL.md'), binary])).toEqual({ - label: '2 files' - }) - }) -}) diff --git a/src/renderer/src/components/skills/skill-package-install-risk.test.ts b/src/renderer/src/components/skills/skill-package-install-risk.test.ts deleted file mode 100644 index 9208811d631..00000000000 --- a/src/renderer/src/components/skills/skill-package-install-risk.test.ts +++ /dev/null @@ -1,74 +0,0 @@ -import { describe, expect, it } from 'vitest' -import { - isSkillRunnableFile, - summarizeSkillInstallRisk, - type SkillInstallRiskFile -} from './skill-package-install-risk' - -function file( - path: string, - options: { classification?: 'text' | 'binary'; executable?: boolean } = {} -): SkillInstallRiskFile { - return { - path, - classification: options.classification ?? 'text', - executable: options.executable ?? false - } -} - -describe('isSkillRunnableFile', () => { - it('recognizes scripts outside the scripts folder and executable files without extensions', () => { - expect(isSkillRunnableFile(file('release.py'))).toBe(true) - expect(isSkillRunnableFile(file('tools/setup.PS1'))).toBe(true) - expect(isSkillRunnableFile(file('bin/team-tool'))).toBe(true) - expect(isSkillRunnableFile(file('references/guide.md', { executable: true }))).toBe(true) - }) - - it('does not label ordinary supporting files as runnable', () => { - expect(isSkillRunnableFile(file('references/guide.md'))).toBe(false) - expect(isSkillRunnableFile(file('assets/logo.png', { classification: 'binary' }))).toBe(false) - }) -}) - -describe('summarizeSkillInstallRisk', () => { - const items = [ - { id: 'plain', name: 'plain', files: [file('SKILL.md')] }, - { - id: 'docs', - name: 'docs', - files: [file('SKILL.md'), file('references/guide.md')] - }, - { - id: 'tools', - name: 'tools', - files: [ - file('SKILL.md'), - file('release.py'), - file('bin/tool', { classification: 'binary', executable: true }) - ] - } - ] - - it('separates supporting files from content that needs acknowledgement', () => { - expect(summarizeSkillInstallRisk(items, new Set(['docs']))).toMatchObject({ - selectedSkillCount: 1, - additionalFileCount: 1, - runnableFileCount: 0, - binaryFileCount: 0, - cautionFileCount: 0, - requiresAcknowledgement: false - }) - }) - - it('counts a runnable binary once for the acknowledgement gate', () => { - expect(summarizeSkillInstallRisk(items, new Set(['tools']))).toEqual({ - selectedSkillCount: 1, - additionalFileCount: 2, - runnableFileCount: 2, - binaryFileCount: 1, - cautionFileCount: 2, - cautionSkillNames: ['tools'], - requiresAcknowledgement: true - }) - }) -}) 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 8e5b7df9d21..e78e3facffa 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 @@ -15,8 +15,7 @@ import { isWorkspaceSpaceRowReadyToDelete, pruneWorkspaceSpaceSelectedIds, resolveWorkspaceSpaceInspectedWorktreeId, - resolveWorkspaceSpaceTreemapZoomWorktreeId, - sortWorkspaceSpaceRows + resolveWorkspaceSpaceTreemapZoomWorktreeId } from './workspace-space-presentation' import { getWorkspaceSpaceGitStatusRefreshCandidates } from './workspace-space-git-status-order' import { getWorkspaceDecisionDetails } from './workspace-space-decision-details' @@ -228,37 +227,6 @@ describe('workspace space presentation helpers', () => { expect(isWorkspaceSpaceRowReadyToDelete(sshRow, ssh)).toBe(false) }) - it('sorts rows by the selected key and direction', () => { - const rows = [ - row({ worktreeId: 'small', displayName: 'Small', sizeBytes: 10 }), - row({ worktreeId: 'large', displayName: 'Large', sizeBytes: 100 }), - row({ worktreeId: 'mid', displayName: 'Mid', sizeBytes: 50 }) - ] - - expect(sortWorkspaceSpaceRows(rows, 'size', 'desc').map((item) => item.worktreeId)).toEqual([ - 'large', - 'mid', - 'small' - ]) - expect(sortWorkspaceSpaceRows(rows, 'name', 'asc').map((item) => item.worktreeId)).toEqual([ - 'large', - 'mid', - 'small' - ]) - }) - - it('filters by search text and deletable status', () => { - const rows = [ - row({ worktreeId: 'a', displayName: 'Frontend Cache', repoDisplayName: 'app' }), - row({ worktreeId: 'b', displayName: 'Main', repoDisplayName: 'api', canDelete: false }) - ] - - expect(filterWorkspaceSpaceRows(rows, 'cache', false).map((item) => item.worktreeId)).toEqual([ - 'a' - ]) - expect(filterWorkspaceSpaceRows(rows, '', true).map((item) => item.worktreeId)).toEqual(['a']) - }) - it('rejects oversized pasted filters before reading workspace rows', () => { const oversizedQuery = 'secret-workspace-space'.repeat(WORKSPACE_SPACE_FILTER_QUERY_MAX_BYTES) const rows = [ @@ -456,117 +424,6 @@ describe('workspace space presentation helpers', () => { expect(details.reviewLabel).toBeNull() }) - it('hides only a matching suppressed GitHub review from workspace decisions', () => { - const matching = getWorkspaceDecisionDetails( - row({ branch: 'refs/heads/feature/local' }), - decisionInputs({ - hostedReviewCache: { - 'local::repo::feature/local': { - data: { - provider: 'github', - number: 12, - state: 'open', - status: 'success', - title: 'Suppressed PR' - } - } - }, - worktreeMap: new Map([ - [ - 'wt', - worktreeRecord({ - branch: 'refs/heads/feature/local', - linkedPR: null, - suppressedGitHubPR: 12 - }) - ] - ]) - }) - ) - const different = getWorkspaceDecisionDetails( - row({ branch: 'refs/heads/feature/local' }), - decisionInputs({ - hostedReviewCache: { - 'local::repo::feature/local': { - data: { - provider: 'github', - number: 13, - state: 'open', - status: 'success', - title: 'Different PR' - } - } - }, - worktreeMap: new Map([ - [ - 'wt', - worktreeRecord({ - branch: 'refs/heads/feature/local', - linkedPR: null, - suppressedGitHubPR: 12 - }) - ] - ]) - }) - ) - - expect(matching.reviewLabel).toBeNull() - expect(different.reviewLabel).toBe('PR #13 Open, success') - }) - - it('preserves explicit links and non-GitHub reviews in workspace decisions', () => { - const cachedReview = { - number: 12, - state: 'open', - status: 'success', - title: 'Review' - } - const explicit = getWorkspaceDecisionDetails( - row({ branch: 'refs/heads/feature/local' }), - decisionInputs({ - hostedReviewCache: { - 'local::repo::feature/local': { - data: { ...cachedReview, provider: 'github' } - } - }, - worktreeMap: new Map([ - [ - 'wt', - worktreeRecord({ - branch: 'refs/heads/feature/local', - linkedPR: 12, - suppressedGitHubPR: 12 - }) - ] - ]) - }) - ) - const gitLab = getWorkspaceDecisionDetails( - row({ branch: 'refs/heads/feature/local' }), - decisionInputs({ - hostedReviewCache: { - 'local::repo::feature/local': { - data: { ...cachedReview, provider: 'gitlab' } - } - }, - worktreeMap: new Map([ - [ - 'wt', - worktreeRecord({ - branch: 'refs/heads/feature/local', - linkedPR: null, - suppressedGitHubPR: 12, - linkedGitLabMR: 12 - }) - ] - ]) - }) - ) - - expect(explicit.reviewLabel).toBe('PR #12 Open, success') - expect(gitLab.reviewLabel).toBe('MR !12 Open, success') - }) - it('counts migration-unsupported agent entries by worktree id', () => { const count = countWorkspaceSpaceActiveAgents({ worktreeId: 'wt', diff --git a/src/renderer/src/components/workspace-cleanup/workspace-cleanup-candidate-row-data.test.ts b/src/renderer/src/components/workspace-cleanup/workspace-cleanup-candidate-row-data.test.ts index 256646080bf..f4a3bcf984d 100644 --- a/src/renderer/src/components/workspace-cleanup/workspace-cleanup-candidate-row-data.test.ts +++ b/src/renderer/src/components/workspace-cleanup/workspace-cleanup-candidate-row-data.test.ts @@ -1,61 +1,8 @@ import { describe, expect, it } from 'vitest' -import { - getCandidateFactStatuses, - getDirtyGitLabel, - shouldShowGitMetadataChip -} from './workspace-cleanup-candidate-row-data' +import { getCandidateFactStatuses } from './workspace-cleanup-candidate-row-data' import { makeCandidate } from './workspace-cleanup-presentation-fixtures' describe('workspace cleanup candidate row data', () => { - it('shows facts instead of cleanup policy tiers', () => { - expect(getCandidateFactStatuses(makeCandidate({ tier: 'ready' }))).toEqual([]) - expect(getCandidateFactStatuses(makeCandidate({ tier: 'review' }))).toEqual([]) - expect(getCandidateFactStatuses(makeCandidate({ reasons: ['archived'] }))).toContainEqual( - expect.objectContaining({ label: 'Archived' }) - ) - }) - - it('does not duplicate git status blockers as a separate git icon label', () => { - const gitStatusError = makeCandidate({ - blockers: ['git-status-error'], - git: { - clean: null, - upstreamAhead: null, - upstreamBehind: null, - checkedAt: null - } - }) - const unknownBase = makeCandidate({ - blockers: ['unknown-base'], - git: { - clean: true, - upstreamAhead: null, - upstreamBehind: null, - checkedAt: 1 - } - }) - - expect(getDirtyGitLabel(gitStatusError)).toBe('Git status check failed') - expect(shouldShowGitMetadataChip(gitStatusError)).toBe(false) - expect(getDirtyGitLabel(unknownBase)).toBe('Git status could not be verified') - expect(shouldShowGitMetadataChip(unknownBase)).toBe(false) - }) - - it('keeps the git metadata chip for ordinary clean rows', () => { - expect( - shouldShowGitMetadataChip( - makeCandidate({ - git: { - clean: true, - upstreamAhead: 0, - upstreamBehind: 0, - checkedAt: 1 - } - }) - ) - ).toBe(true) - }) - it('keeps unpushed risk visible for archived rows', () => { expect( getCandidateFactStatuses( @@ -72,59 +19,4 @@ describe('workspace cleanup candidate row data', () => { ) ).toContainEqual(expect.objectContaining({ label: 'Unpushed commits' })) }) - - it('renders every blocker fact instead of only the first', () => { - expect( - getCandidateFactStatuses(makeCandidate({ blockers: ['pinned', 'git-status-error'] })) - ).toEqual([ - expect.objectContaining({ label: 'Pinned' }), - expect.objectContaining({ - label: 'Git status unavailable', - tone: 'destructive' - }) - ]) - }) - - it('suppresses the git metadata chip when the status pill already names git risk', () => { - expect( - shouldShowGitMetadataChip( - makeCandidate({ - blockers: ['unpushed-commits'], - git: { - clean: true, - upstreamAhead: 2, - upstreamBehind: 0, - checkedAt: 1 - } - }) - ) - ).toBe(false) - expect( - shouldShowGitMetadataChip( - makeCandidate({ - tier: 'review', - blockers: [], - git: { - clean: true, - upstreamAhead: 2, - upstreamBehind: 0, - checkedAt: 1 - } - }) - ) - ).toBe(false) - expect( - shouldShowGitMetadataChip( - makeCandidate({ - blockers: ['dirty-files'], - git: { - clean: false, - upstreamAhead: 0, - upstreamBehind: 0, - checkedAt: 1 - } - }) - ) - ).toBe(false) - }) }) diff --git a/src/renderer/src/components/workspace-cleanup/workspace-cleanup-candidate-row.test.tsx b/src/renderer/src/components/workspace-cleanup/workspace-cleanup-candidate-row.test.tsx index 9d4ee5a6e2f..1e6fd782243 100644 --- a/src/renderer/src/components/workspace-cleanup/workspace-cleanup-candidate-row.test.tsx +++ b/src/renderer/src/components/workspace-cleanup/workspace-cleanup-candidate-row.test.tsx @@ -33,108 +33,6 @@ describe('CandidateRow', () => { container = null }) - it('does not present cleanup policy tiers as workspace facts', () => { - const candidate = makeCandidate({ tier: 'ready' }) - - act(() => { - root?.render( - - ) - }) - - expect(container?.textContent).not.toContain('Ready') - expect(container?.textContent).not.toContain('Status unavailable') - }) - - it('shows factual status, disk size, activity, and git state before expansion', () => { - const candidate = makeCandidate({ tier: 'ready' }) - - act(() => { - root?.render( - - ) - }) - - expect(container?.textContent).toContain('In progress') - expect(container?.textContent).toContain('4.00 MB') - expect(container?.textContent).toContain('1d') - expect(container?.textContent).toContain('Clean') - }) - - it('uses an external-link action to open the workspace', () => { - const candidate = makeCandidate() - - act(() => { - root?.render( - - ) - }) - - const openButton = container?.querySelector( - `[aria-label="Open ${getWorkspaceCleanupCandidateAccessibleName(candidate)}"]` - ) - expect(openButton?.querySelector('.lucide-external-link')).not.toBeNull() - }) - it('hides selection and remove controls while the workspace is already deleting', () => { const candidate = makeCandidate() @@ -169,43 +67,6 @@ describe('CandidateRow', () => { expect(container?.querySelector(`[aria-label="Remove ${accessibleName}"]`)).toBeNull() }) - it.each([ - ['deleting' as const, 'Deleting…'], - ['queued' as const, 'Queued for deletion'] - ])('replaces the status pill with the %s state', (deletionPhase, label) => { - const candidate = makeCandidate() - - act(() => { - root?.render( - - ) - }) - - expect(container?.textContent).toContain(label) - expect(container?.textContent).not.toContain('Ready') - }) - it('visibly and accessibly distinguishes colliding local and SSH rows', () => { const local = makeCandidate({ executionHostId: 'local' }) const remote = makeCandidate({ connectionId: 'builder', executionHostId: 'ssh:builder' }) @@ -247,78 +108,4 @@ describe('CandidateRow', () => { expect(container?.querySelector(`[aria-label="Select ${remoteName}"]`)).not.toBeNull() expect(container?.querySelector(`[aria-label="Host: builder"]`)).not.toBeNull() }) - - it('colors the review pill by state the way the PR page and item dialog do', () => { - const states = [ - { state: 'merged' as const, label: 'PR #1', hue: 'purple' }, - { state: 'closed' as const, label: 'PR #2', hue: 'rose' }, - { state: 'open' as const, label: 'PR #3', hue: 'emerald' }, - { state: 'draft' as const, label: 'PR #4', hue: 'slate' } - ] - - for (const { state, label, hue } of states) { - const candidate = makeCandidate() - - act(() => { - root?.render( - - ) - }) - - const pill = container?.querySelector(`[aria-label*="${label}"]`) - expect(pill?.className).toContain(`text-${hue}-`) - expect(pill?.className).not.toContain('text-muted-foreground') - } - }) - - it('leaves a review with an unfetched state neutral', () => { - const candidate = makeCandidate() - - act(() => { - root?.render( - - ) - }) - - const pill = container?.querySelector('[aria-label*="PR #9"]') - expect(pill?.className).toContain('text-muted-foreground') - }) }) diff --git a/src/renderer/src/components/workspace-cleanup/workspace-cleanup-presentation.test.ts b/src/renderer/src/components/workspace-cleanup/workspace-cleanup-presentation.test.ts index cb9cff663cf..293ff0c169f 100644 --- a/src/renderer/src/components/workspace-cleanup/workspace-cleanup-presentation.test.ts +++ b/src/renderer/src/components/workspace-cleanup/workspace-cleanup-presentation.test.ts @@ -3,11 +3,7 @@ import { getHostedReviewCacheKey } from '@/store/slices/hosted-review-cache-iden import { filterWorkspaceCleanupCandidates, getWorkspaceCleanupGitLabel, - getWorkspaceCleanupReviewInfo, - getWorkspaceCleanupSearchText, - hasWorkspaceCleanupLocalContext, - sortWorkspaceCleanupCandidates, - type WorkspaceCleanupReviewInfo + getWorkspaceCleanupReviewInfo } from './workspace-cleanup-presentation' import { DEFAULT_FILTERS, @@ -19,22 +15,6 @@ import { import type { Repo } from '../../../../shared/repo-types' describe('workspace cleanup presentation', () => { - it('finds hosted review details from renderer state', () => { - const cacheKey = getHostedReviewCacheKey('/repo', 'alpha', {}, 'repo-1', undefined) - const state = makeState({ - hostedReviewCache: { - [cacheKey]: { data: makeReview(), fetchedAt: NOW } - } - }) - - expect(getWorkspaceCleanupReviewInfo(makeCandidate(), state)).toMatchObject({ - hasReview: true, - label: 'PR #42', - state: 'open', - title: 'Review alpha cleanup' - }) - }) - it('hides only the matching suppressed GitHub review', () => { const cacheKey = getHostedReviewCacheKey('/repo', 'alpha', {}, 'repo-1', undefined) const matching = makeState({ @@ -65,48 +45,6 @@ describe('workspace cleanup presentation', () => { }) }) - it('preserves explicit GitHub links and non-GitHub reviews despite stale suppression', () => { - const cacheKey = getHostedReviewCacheKey('/repo', 'alpha', {}, 'repo-1', undefined) - const explicit = makeState({ - worktreesByRepo: { - 'repo-1': [ - { - ...makeState().worktreesByRepo['repo-1']![0]!, - linkedPR: 42, - suppressedGitHubPR: 42 - } - ] - }, - hostedReviewCache: { - [cacheKey]: { data: makeReview({ number: 42 }), fetchedAt: NOW } - } - }) - const gitLab = makeState({ - worktreesByRepo: { - 'repo-1': [ - { - ...makeState().worktreesByRepo['repo-1']![0]!, - linkedPR: null, - suppressedGitHubPR: 42, - linkedGitLabMR: 42 - } - ] - }, - hostedReviewCache: { - [cacheKey]: { - data: makeReview({ provider: 'gitlab', number: 42 }), - fetchedAt: NOW - } - } - }) - - expect(getWorkspaceCleanupReviewInfo(makeCandidate(), explicit).hasReview).toBe(true) - expect(getWorkspaceCleanupReviewInfo(makeCandidate(), gitLab)).toMatchObject({ - hasReview: true, - provider: 'gitlab' - }) - }) - it('host-qualifies same-id repo, worktree, and review cache joins', () => { const base = makeState() const localRepo = { ...base.repos[0]!, executionHostId: 'local' as const } @@ -177,66 +115,6 @@ describe('workspace cleanup presentation', () => { expect(getWorkspaceCleanupReviewInfo(makeCandidate(), state).hasReview).toBe(false) }) - it('filters by time, review, git, and context', () => { - const openReview: WorkspaceCleanupReviewInfo = { - hasReview: true, - label: 'PR #7', - state: 'open', - provider: 'github', - title: 'Review row' - } - const rows = [ - makeCandidate({ worktreeId: 'repo-1::/repo/clean', displayName: 'clean' }), - makeCandidate({ - worktreeId: 'repo-1::/repo/dirty-review', - displayName: 'dirty-review', - git: { clean: false, upstreamAhead: 0, upstreamBehind: 0, checkedAt: NOW }, - lastActivityAt: NOW - 100 * 24 * 60 * 60 * 1000, - localContext: { - terminalTabCount: 1, - cleanEditorTabCount: 0, - browserTabCount: 0, - diffCommentCount: 0, - newestDiffCommentAt: null, - retainedDoneAgentCount: 0 - } - }), - makeCandidate({ - worktreeId: 'repo-1::/repo/unknown', - displayName: 'unknown', - git: { clean: null, upstreamAhead: null, upstreamBehind: null, checkedAt: null }, - blockers: ['git-status-error'] - }) - ] - const reviewInfo = new Map([ - ['repo-1::/repo/dirty-review', openReview] - ]) - - expect( - filterWorkspaceCleanupCandidates( - rows, - { - ...DEFAULT_FILTERS, - time: '90d', - review: 'open-review', - git: 'dirty', - context: 'has-context' - }, - reviewInfo, - NOW - ).map((row) => row.displayName) - ).toEqual(['dirty-review']) - - expect( - filterWorkspaceCleanupCandidates( - rows, - { ...DEFAULT_FILTERS, git: 'unknown' }, - reviewInfo, - NOW - ).map((row) => row.displayName) - ).toEqual(['unknown']) - }) - it('treats unknown-base clean rows as unknown git risk', () => { const candidate = makeCandidate({ git: { clean: true, upstreamAhead: null, upstreamBehind: null, checkedAt: NOW }, @@ -261,111 +139,4 @@ describe('workspace cleanup presentation', () => { ) ).toEqual([candidate]) }) - - it('search includes repo, branch, path, review, git, and context labels', () => { - const candidate = makeCandidate({ - repoName: 'Search Repo', - branch: 'feature/search', - path: '/repo/search-target', - git: { clean: null, upstreamAhead: null, upstreamBehind: null, checkedAt: null }, - localContext: { - terminalTabCount: 0, - cleanEditorTabCount: 0, - browserTabCount: 1, - diffCommentCount: 0, - newestDiffCommentAt: null, - retainedDoneAgentCount: 0 - } - }) - const reviewInfo: WorkspaceCleanupReviewInfo = { - hasReview: true, - label: 'MR #9', - state: 'closed', - provider: 'gitlab', - title: 'Searchable review' - } - const text = getWorkspaceCleanupSearchText(candidate, reviewInfo) - - expect(text).toContain('search repo') - expect(text).toContain('feature/search') - expect(text).toContain('/repo/search-target') - expect(text).toContain('mr #9') - expect(text).toContain('unknown') - expect(text).toContain('has context') - }) - - it('sorts by activity, name, repo, review, and git', () => { - const rows = [ - makeCandidate({ - worktreeId: 'repo-1::/repo/bravo', - displayName: 'bravo', - repoName: 'Repo B', - lastActivityAt: NOW - 5, - git: { clean: false, upstreamAhead: 0, upstreamBehind: 0, checkedAt: NOW } - }), - makeCandidate({ - worktreeId: 'repo-1::/repo/alpha', - displayName: 'alpha', - repoName: 'Repo A', - lastActivityAt: NOW - 10, - git: { clean: true, upstreamAhead: 0, upstreamBehind: 0, checkedAt: NOW } - }), - makeCandidate({ - worktreeId: 'repo-1::/repo/charlie', - displayName: 'charlie', - repoName: 'Repo C', - lastActivityAt: NOW - 1, - git: { clean: true, upstreamAhead: 2, upstreamBehind: 0, checkedAt: NOW }, - blockers: ['unpushed-commits'] - }) - ] - const reviewInfo = new Map([ - [ - 'repo-1::/repo/charlie', - { - hasReview: true, - label: 'PR #3', - state: 'open', - provider: 'github', - title: null - } - ] - ]) - - expect( - sortWorkspaceCleanupCandidates(rows, 'activity', 'asc').map((row) => row.displayName) - ).toEqual(['alpha', 'bravo', 'charlie']) - expect( - sortWorkspaceCleanupCandidates(rows, 'name', 'asc').map((row) => row.displayName) - ).toEqual(['alpha', 'bravo', 'charlie']) - expect( - sortWorkspaceCleanupCandidates(rows, 'repo', 'asc').map((row) => row.displayName) - ).toEqual(['alpha', 'bravo', 'charlie']) - expect( - sortWorkspaceCleanupCandidates(rows, 'review', 'desc', reviewInfo).map( - (row) => row.displayName - ) - ).toEqual(['charlie', 'alpha', 'bravo']) - expect( - sortWorkspaceCleanupCandidates(rows, 'git', 'desc').map((row) => row.displayName) - ).toEqual(['charlie', 'bravo', 'alpha']) - }) - - it('detects local context', () => { - expect(hasWorkspaceCleanupLocalContext(makeCandidate())).toBe(false) - expect( - hasWorkspaceCleanupLocalContext( - makeCandidate({ - localContext: { - terminalTabCount: 0, - cleanEditorTabCount: 1, - browserTabCount: 0, - diffCommentCount: 0, - newestDiffCommentAt: null, - retainedDoneAgentCount: 0 - } - }) - ) - ).toBe(true) - }) }) diff --git a/src/renderer/src/components/workspace-cleanup/workspace-cleanup-sort-header.test.tsx b/src/renderer/src/components/workspace-cleanup/workspace-cleanup-sort-header.test.tsx index 151df522781..c666381078a 100644 --- a/src/renderer/src/components/workspace-cleanup/workspace-cleanup-sort-header.test.tsx +++ b/src/renderer/src/components/workspace-cleanup/workspace-cleanup-sort-header.test.tsx @@ -35,12 +35,6 @@ function render(sort: WorkspaceCleanupSortState, handlers: Record('[data-sort-option]') ?? [])].find( - (button) => button.textContent?.trim() === label - ) -} - describe('WorkspaceCleanupSortHeader', () => { beforeEach(() => { container = document.createElement('div') @@ -57,34 +51,6 @@ describe('WorkspaceCleanupSortHeader', () => { container = null }) - it('shows the active sort in one menu trigger', () => { - render({ field: 'size', direction: 'desc' }) - - expect(container?.querySelector('[aria-label="Sort by Size"]')?.textContent).toContain( - 'Sort by Size' - ) - }) - - it('routes a menu choice through the sort toggle', () => { - const onToggleSortField = vi.fn() - render({ field: 'last-activity', direction: 'asc' }, { onToggleSortField }) - - act(() => sortOption('Repository')?.click()) - - expect(onToggleSortField).toHaveBeenCalledWith('repo') - }) - - it('select-all reads the query result rather than the rendered page', () => { - const onToggleSelectAll = vi.fn() - render({ field: 'name', direction: 'asc' }, { onToggleSelectAll }) - - const checkbox = container?.querySelector('[role="checkbox"]') - expect(checkbox?.getAttribute('aria-checked')).toBe('false') - act(() => checkbox?.click()) - - expect(onToggleSelectAll).toHaveBeenCalledWith(true) - }) - it('exposes a partial selection as mixed', () => { render({ field: 'name', direction: 'asc' }, { selectableCount: 3, selectedCount: 1 }) @@ -92,29 +58,4 @@ describe('WorkspaceCleanupSortHeader', () => { container?.querySelector('[role="checkbox"]')?.getAttribute('aria-checked') ).toBe('mixed') }) - it('states how many workspaces select-all would take', () => { - render({ field: 'name', direction: 'asc' }, { selectableCount: 12 }) - - expect(container?.textContent).toContain('Select all 12 safety-checked workspaces') - }) - - it('uses singular copy for one safety-checked workspace', () => { - render({ field: 'name', direction: 'asc' }, { selectableCount: 1 }) - - expect(container?.textContent).toContain('Select 1 safety-checked workspace') - }) - - it('does not report all-selected when the selected rows are not the selectable ones', () => { - const onToggleSelectAll = vi.fn() - render( - { field: 'name', direction: 'asc' }, - { selectableCount: 3, selectedCount: 0, onToggleSelectAll } - ) - - const checkbox = container?.querySelector('[role="checkbox"]') - expect(checkbox?.getAttribute('aria-checked')).toBe('false') - - act(() => (checkbox as HTMLElement | null)?.click()) - expect(onToggleSelectAll).toHaveBeenCalledWith(true) - }) })