mirror of
https://github.com/stablyai/orca.git
synced 2026-10-30 16:47:34 +00:00
test: remove 53 low-value cases and eight suites (#34302)
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
This commit is contained in:
@@ -7803,7 +7803,6 @@
|
||||
"src/renderer/src/components/feature-wall/feature-wall-animation-visibility.test.tsx": 592,
|
||||
"src/renderer/src/components/feature-wall/feature-wall-setup-checklist-localized-copy.test.ts": 578,
|
||||
"src/renderer/src/components/feature-wall/feature-wall-setup-progress.test.ts": 62,
|
||||
"src/renderer/src/components/feature-wall/feature-wall-usage-tracking.test.ts": 106,
|
||||
"src/renderer/src/components/feature-wall/use-feature-wall-completion.test.ts": 47,
|
||||
"src/renderer/src/components/feature-wall/use-feature-wall-tour-telemetry.test.ts": 31,
|
||||
"src/renderer/src/components/feature-wall/use-integration-connection-status.test.ts": 1429,
|
||||
@@ -8066,7 +8065,6 @@
|
||||
"src/renderer/src/components/native-chat/native-chat-launch-default-adoption.test.ts": 197,
|
||||
"src/renderer/src/components/native-chat/native-chat-launch-draft-resolution.test.ts": 70,
|
||||
"src/renderer/src/components/native-chat/native-chat-launch-draft-send.test.ts": 39,
|
||||
"src/renderer/src/components/native-chat/native-chat-layout-actions.test.ts": 2289,
|
||||
"src/renderer/src/components/native-chat/native-chat-leaf-routing.test.ts": 24,
|
||||
"src/renderer/src/components/native-chat/native-chat-live-message-preparation.test.ts": 152,
|
||||
"src/renderer/src/components/native-chat/native-chat-live-status.test.ts": 87,
|
||||
@@ -8289,7 +8287,6 @@
|
||||
"src/renderer/src/components/new-workspace/SmartWorkspaceSourceResultList.linear-read-error.test.tsx": 717,
|
||||
"src/renderer/src/components/new-workspace/project-combobox-matching.test.ts": 73,
|
||||
"src/renderer/src/components/new-workspace/smart-workspace-command-value.test.ts": 48,
|
||||
"src/renderer/src/components/new-workspace/smart-workspace-source-popover-focus.test.ts": 123,
|
||||
"src/renderer/src/components/new-workspace/smart-workspace-source-results.test.ts": 204,
|
||||
"src/renderer/src/components/new-workspace/use-jira-composer-project.test.tsx": 172,
|
||||
"src/renderer/src/components/new-workspace/use-jira-source-connection.test.tsx": 208,
|
||||
@@ -8720,7 +8717,6 @@
|
||||
"src/renderer/src/components/settings/NotificationHostToggles.test.tsx": 427,
|
||||
"src/renderer/src/components/settings/NotificationsPane.hidden-workspaces.test.tsx": 1362,
|
||||
"src/renderer/src/components/settings/NotificationsPane.test.tsx": 1723,
|
||||
"src/renderer/src/components/settings/OpenInMenuSetting.test.ts": 148,
|
||||
"src/renderer/src/components/settings/OrchestrationPane.test.tsx": 1824,
|
||||
"src/renderer/src/components/settings/OrchestrationSkillAgentCoverage.test.tsx": 247,
|
||||
"src/renderer/src/components/settings/PluginConsentDialog.test.tsx": 1638,
|
||||
@@ -8747,7 +8743,6 @@
|
||||
"src/renderer/src/components/settings/RuntimeSshAccessControl.test.tsx": 441,
|
||||
"src/renderer/src/components/settings/SessionHistoryComputerRow.test.tsx": 642,
|
||||
"src/renderer/src/components/settings/SessionHistorySettingsPane.test.tsx": 903,
|
||||
"src/renderer/src/components/settings/Settings.load-performance.test.ts": 38,
|
||||
"src/renderer/src/components/settings/SettingsConstants.test.ts": 97,
|
||||
"src/renderer/src/components/settings/SettingsFormControls.font-autocomplete.test.tsx": 789,
|
||||
"src/renderer/src/components/settings/SettingsFormControls.number-field.test.tsx": 753,
|
||||
@@ -8836,7 +8831,6 @@
|
||||
"src/renderer/src/components/settings/repository/source-control/repository-source-control-ai-persist-queue.test.ts": 67,
|
||||
"src/renderer/src/components/settings/runtime-default-host-row.test.tsx": 1765,
|
||||
"src/renderer/src/components/settings/runtime-server-default-name.test.ts": 122,
|
||||
"src/renderer/src/components/settings/runtime-server-row-meta.test.ts": 165,
|
||||
"src/renderer/src/components/settings/runtime-server-row-presentation.test.ts": 357,
|
||||
"src/renderer/src/components/settings/runtime-server-row-unverifiable-host.test.tsx": 2162,
|
||||
"src/renderer/src/components/settings/runtime-share-computer-button.test.tsx": 1304,
|
||||
@@ -9353,7 +9347,6 @@
|
||||
"src/renderer/src/components/status-bar/usage-percentage-label.localization.test.ts": 185,
|
||||
"src/renderer/src/components/status-bar/usage-percentage-label.test.ts": 20,
|
||||
"src/renderer/src/components/status-bar/usage-pool-label.test.ts": 76,
|
||||
"src/renderer/src/components/status-bar/usage-provider-settings-target.test.ts": 34,
|
||||
"src/renderer/src/components/status-bar/usage-roster-row-state.test.ts": 71,
|
||||
"src/renderer/src/components/status-bar/usage-unit-label-parity.test.tsx": 335,
|
||||
"src/renderer/src/components/status-bar/use-owner-usage-rate-limits.test.tsx": 261,
|
||||
@@ -12593,7 +12586,6 @@
|
||||
"src/shared/native-chat-agent-support.test.ts": 38,
|
||||
"src/shared/native-chat-appearance-settings.test.ts": 34,
|
||||
"src/shared/native-chat-approval-envelope.test.ts": 49,
|
||||
"src/shared/native-chat/input/native-chat-ask-fifo.test.ts": 30,
|
||||
"src/shared/native-chat/input/native-chat-ask.test.ts": 95,
|
||||
"src/shared/native-chat/input/native-chat-background-task-row.test.ts": 67,
|
||||
"src/shared/native-chat-context-usage.test.ts": 28,
|
||||
|
||||
@@ -1,23 +1,7 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import {
|
||||
buildCheckRunDetailsTabId,
|
||||
getCheckRunDetailsTabLabel,
|
||||
getCheckRunTabIdentity
|
||||
} from './check-run-details-tab'
|
||||
import { buildCheckRunDetailsTabId, getCheckRunTabIdentity } from './check-run-details-tab'
|
||||
|
||||
describe('check-run-details-tab', () => {
|
||||
it('builds a stable tab id from worktree and check identity', () => {
|
||||
expect(
|
||||
buildCheckRunDetailsTabId('wt-1', {
|
||||
name: 'verify',
|
||||
status: 'completed',
|
||||
conclusion: 'failure',
|
||||
url: null,
|
||||
checkRunId: 99
|
||||
})
|
||||
).toBe('wt-1::check-details::check-run:99')
|
||||
})
|
||||
|
||||
it('falls back to workflow and url identities when check run id is missing', () => {
|
||||
expect(
|
||||
getCheckRunTabIdentity({
|
||||
@@ -48,15 +32,4 @@ describe('check-run-details-tab', () => {
|
||||
buildCheckRunDetailsTabId('wt-1', { ...base, name: 'test: unit', gitlabJobId: 42 })
|
||||
).not.toBe(buildCheckRunDetailsTabId('wt-1', { ...base, name: 'test: unit', gitlabJobId: 43 }))
|
||||
})
|
||||
|
||||
it('uses the check name for the tab label', () => {
|
||||
expect(
|
||||
getCheckRunDetailsTabLabel({
|
||||
name: 'verify',
|
||||
status: 'completed',
|
||||
conclusion: 'failure',
|
||||
url: null
|
||||
})
|
||||
).toBe('verify')
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import type { DiffComment } from '../../../../shared/diff-comment-types'
|
||||
import {
|
||||
shouldExpandRichMarkdownReviewRail,
|
||||
stackRichMarkdownReviewNotePositions
|
||||
} from './rich-markdown-review-note-layout'
|
||||
import { stackRichMarkdownReviewNotePositions } from './rich-markdown-review-note-layout'
|
||||
|
||||
function makeComment(overrides: Partial<DiffComment>): DiffComment {
|
||||
return {
|
||||
@@ -36,41 +33,4 @@ describe('stackRichMarkdownReviewNotePositions', () => {
|
||||
expect(stacked[0].top).toBe(100)
|
||||
expect(stacked[1].top).toBeGreaterThan(stacked[0].top)
|
||||
})
|
||||
|
||||
it('reserves quote height for fallback source-line excerpts', () => {
|
||||
const stacked = stackRichMarkdownReviewNotePositions([
|
||||
{
|
||||
comment: makeComment({ id: 'first', body: 'note' }),
|
||||
top: 100
|
||||
},
|
||||
{
|
||||
comment: makeComment({ id: 'second', body: 'note' }),
|
||||
top: 100
|
||||
}
|
||||
])
|
||||
|
||||
expect(stacked[1].top).toBe(210)
|
||||
})
|
||||
})
|
||||
|
||||
describe('shouldExpandRichMarkdownReviewRail', () => {
|
||||
it('expands while a markdown note draft is open even before the note is saved', () => {
|
||||
expect(
|
||||
shouldExpandRichMarkdownReviewRail({
|
||||
hasReviewNotes: false,
|
||||
reviewRailOpen: false,
|
||||
hasDraftNote: true
|
||||
})
|
||||
).toBe(true)
|
||||
})
|
||||
|
||||
it('keeps saved notes collapsed until the user opens the rail', () => {
|
||||
expect(
|
||||
shouldExpandRichMarkdownReviewRail({
|
||||
hasReviewNotes: true,
|
||||
reviewRailOpen: false,
|
||||
hasDraftNote: false
|
||||
})
|
||||
).toBe(false)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,83 +0,0 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import type { ProviderRateLimits } from '../../../../shared/rate-limit-types'
|
||||
import {
|
||||
getFeatureWallUsageProviderConnection,
|
||||
hasFeatureWallUsageTracking
|
||||
} from './feature-wall-usage-tracking'
|
||||
|
||||
function rateLimits(overrides: Partial<ProviderRateLimits> = {}): ProviderRateLimits {
|
||||
return {
|
||||
provider: 'codex',
|
||||
session: null,
|
||||
weekly: null,
|
||||
updatedAt: 1,
|
||||
error: null,
|
||||
status: 'ok',
|
||||
...overrides
|
||||
}
|
||||
}
|
||||
|
||||
describe('feature wall usage tracking state', () => {
|
||||
it('treats system-default Codex rate limit data as connected', () => {
|
||||
expect(
|
||||
getFeatureWallUsageProviderConnection({
|
||||
managedAccountCount: 0,
|
||||
provider: rateLimits({
|
||||
session: {
|
||||
usedPercent: 10,
|
||||
windowMinutes: 300,
|
||||
resetsAt: null,
|
||||
resetDescription: null
|
||||
}
|
||||
})
|
||||
})
|
||||
).toEqual({ connected: true, label: 'Connected · System default' })
|
||||
})
|
||||
|
||||
it('keeps managed accounts connected even before live quota data arrives', () => {
|
||||
expect(
|
||||
getFeatureWallUsageProviderConnection({
|
||||
managedAccountCount: 2,
|
||||
provider: null
|
||||
})
|
||||
).toEqual({ connected: true, label: 'Connected · 2' })
|
||||
})
|
||||
|
||||
it('returns not set up when there are no accounts or quota signals', () => {
|
||||
expect(
|
||||
getFeatureWallUsageProviderConnection({
|
||||
managedAccountCount: 0,
|
||||
provider: null
|
||||
})
|
||||
).toEqual({ connected: false, label: 'Tracking not set up' })
|
||||
})
|
||||
|
||||
it('returns unknown when the account list has not loaded', () => {
|
||||
expect(
|
||||
getFeatureWallUsageProviderConnection({
|
||||
managedAccountCount: undefined,
|
||||
provider: null
|
||||
})
|
||||
).toEqual({ connected: false, label: 'Account status unknown' })
|
||||
})
|
||||
|
||||
it('prefers observed provider usage over an unknown account list', () => {
|
||||
expect(
|
||||
getFeatureWallUsageProviderConnection({
|
||||
managedAccountCount: undefined,
|
||||
provider: rateLimits()
|
||||
})
|
||||
).toEqual({ connected: true, label: 'Connected · System default' })
|
||||
})
|
||||
|
||||
it('marks the usage step complete from system-default provider data', () => {
|
||||
expect(
|
||||
hasFeatureWallUsageTracking({
|
||||
claudeManagedAccountCount: 0,
|
||||
codexManagedAccountCount: 0,
|
||||
claudeRateLimits: null,
|
||||
codexRateLimits: rateLimits()
|
||||
})
|
||||
).toBe(true)
|
||||
})
|
||||
})
|
||||
@@ -13,16 +13,6 @@ describe('filterPullRequestPickerOptions', () => {
|
||||
{ key: 'docs', primary: 'documentation' }
|
||||
]
|
||||
|
||||
it('returns all options for empty queries', () => {
|
||||
expect(filterPullRequestPickerOptions(options, '')).toEqual(options)
|
||||
expect(filterPullRequestPickerOptions(options, ' ')).toEqual(options)
|
||||
})
|
||||
|
||||
it('matches primary and secondary text case-insensitively', () => {
|
||||
expect(filterPullRequestPickerOptions(options, 'BUG')).toEqual([options[1]])
|
||||
expect(filterPullRequestPickerOptions(options, 'smith')).toEqual([options[0]])
|
||||
})
|
||||
|
||||
it('rejects oversized pasted queries before reading picker option text', () => {
|
||||
const oversizedQuery = 'secret-pr-picker-filter'.repeat(PULL_REQUEST_PICKER_QUERY_MAX_BYTES)
|
||||
const throwingOptions = [
|
||||
|
||||
@@ -32,16 +32,6 @@ describe('resumeFailureGuidance', () => {
|
||||
}
|
||||
)
|
||||
|
||||
it.each([
|
||||
'agent_session_conflict',
|
||||
'agent_session_ownership_unknown',
|
||||
'execution_owner_reconciling'
|
||||
])('words %s without Orca internals', (reason) => {
|
||||
expect(resumeFailureGuidance({ outcome: 'refused', reason }).text).not.toMatch(
|
||||
/owns|process|session/
|
||||
)
|
||||
})
|
||||
|
||||
it('never offers a retry for a delivery nobody could confirm', () => {
|
||||
expect(
|
||||
resumeFailureGuidance({ outcome: 'unconfirmed', reason: 'agent_session_conflict' })
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
// @vitest-environment happy-dom
|
||||
import { readFileSync } from 'node:fs'
|
||||
import { resolve } from 'node:path'
|
||||
import { compile } from 'tailwindcss'
|
||||
import { afterEach, describe, expect, it, vi } from 'vitest'
|
||||
import { createGlobalSettingsFixture } from '../../../../shared/global-settings-test-fixture'
|
||||
import * as terminalThemeSelection from '../../../../shared/terminal-theme-selection'
|
||||
@@ -123,23 +122,3 @@ describe('matching chat surfaces with opposite app schemes', () => {
|
||||
expect(styles.getPropertyValue('--chat-source-foreground')).toBe('#fafafa')
|
||||
})
|
||||
})
|
||||
|
||||
describe('chat scheme and inherited dark utilities', () => {
|
||||
it('compiles the custom variant and excludes light chats and enables dark chats', async () => {
|
||||
const declaration = css.match(/^@custom-variant dark \((.+)\);$/m)
|
||||
expect(declaration).not.toBeNull()
|
||||
if (!declaration) {
|
||||
throw new Error('Missing dark variant')
|
||||
}
|
||||
const compiler = await compile(
|
||||
`${declaration[0]}\n@theme { --color-input: #123456; }\n@tailwind utilities;`
|
||||
)
|
||||
expect(compiler.build(['dark:bg-input/30'])).toContain('data-native-chat-scheme')
|
||||
expect(declaration[1]).toContain(
|
||||
".dark *:not(:where([data-native-chat-scheme='light'], [data-native-chat-scheme='light'] *))"
|
||||
)
|
||||
expect(declaration[1]).toContain(
|
||||
", [data-native-chat-scheme='dark'], [data-native-chat-scheme='dark'] *"
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,51 +0,0 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import type { AppState } from '@/store/types'
|
||||
import {
|
||||
canRunNativeChatSplitTarget,
|
||||
resolveActiveNativeChatSplitTarget
|
||||
} from './native-chat-layout-actions'
|
||||
|
||||
function stateWithActiveTab(tab: Record<string, unknown>, tabOrder = ['chat']) {
|
||||
return {
|
||||
groupsByWorktree: {
|
||||
workspace: [{ id: 'group', worktreeId: 'workspace', activeTabId: 'chat', tabOrder }]
|
||||
},
|
||||
unifiedTabsByWorktree: {
|
||||
workspace: [
|
||||
{
|
||||
id: 'chat',
|
||||
entityId: 'session',
|
||||
groupId: 'group',
|
||||
worktreeId: 'workspace',
|
||||
...tab
|
||||
}
|
||||
]
|
||||
}
|
||||
} as unknown as Pick<AppState, 'groupsByWorktree' | 'unifiedTabsByWorktree'>
|
||||
}
|
||||
|
||||
describe('native chat layout actions', () => {
|
||||
it('can split a structured chat even when it is the only tab', () => {
|
||||
const state = stateWithActiveTab({ contentType: 'agent-session', agentSessionAgent: 'codex' })
|
||||
const target = resolveActiveNativeChatSplitTarget(state, 'workspace', 'group')
|
||||
|
||||
expect(target).toEqual({ kind: 'workspace-tab', unifiedTabId: 'chat', groupId: 'group' })
|
||||
expect(canRunNativeChatSplitTarget(state, target)).toBe(true)
|
||||
})
|
||||
|
||||
it('resolves terminal-backed chat mode to the existing pane split path', () => {
|
||||
const state = stateWithActiveTab({ contentType: 'terminal', viewMode: 'chat' })
|
||||
|
||||
expect(resolveActiveNativeChatSplitTarget(state, 'workspace', 'group')).toEqual({
|
||||
kind: 'terminal-pane',
|
||||
terminalTabId: 'session'
|
||||
})
|
||||
expect(
|
||||
resolveActiveNativeChatSplitTarget(
|
||||
stateWithActiveTab({ contentType: 'terminal', viewMode: 'terminal' }),
|
||||
'workspace',
|
||||
'group'
|
||||
)
|
||||
).toBeNull()
|
||||
})
|
||||
})
|
||||
@@ -30,14 +30,6 @@ function markedText(): string {
|
||||
}
|
||||
|
||||
describe('MatchedText', () => {
|
||||
it('underlines the matched run', () => {
|
||||
act(() => {
|
||||
root.render(<MatchedText text="orca" hits={[0, 1, 2, 3]} />)
|
||||
})
|
||||
|
||||
expect(markedText()).toBe('orca')
|
||||
})
|
||||
|
||||
// UTF-16 hit offsets and code-point rendering differ for emoji; marks must stay on the matched glyphs.
|
||||
it('aligns marks with the query when the name starts with an emoji', () => {
|
||||
const option: NewWorkspaceProjectOption = {
|
||||
|
||||
-58
@@ -1,58 +0,0 @@
|
||||
// @vitest-environment happy-dom
|
||||
import { describe, expect, it } from 'vitest'
|
||||
|
||||
import { isComposerFieldToFieldFocus } from './smart-workspace-source-popover-focus'
|
||||
|
||||
describe('isComposerFieldToFieldFocus', () => {
|
||||
it('returns true when focus moves between fields inside the composer root', () => {
|
||||
document.body.innerHTML = `
|
||||
<div data-workspace-composer-root="true">
|
||||
<button id="project" type="button">Project</button>
|
||||
<input id="name" type="text" />
|
||||
</div>
|
||||
`
|
||||
const nameInput = document.getElementById('name') as HTMLInputElement
|
||||
const projectButton = document.getElementById('project') as HTMLButtonElement
|
||||
|
||||
expect(
|
||||
isComposerFieldToFieldFocus({
|
||||
currentTarget: nameInput,
|
||||
relatedTarget: projectButton
|
||||
})
|
||||
).toBe(true)
|
||||
})
|
||||
|
||||
it('returns false for dialog autofocus where focus comes from outside the composer', () => {
|
||||
document.body.innerHTML = `
|
||||
<button id="close" type="button">Close</button>
|
||||
<div data-workspace-composer-root="true">
|
||||
<input id="name" type="text" />
|
||||
</div>
|
||||
`
|
||||
const nameInput = document.getElementById('name') as HTMLInputElement
|
||||
const closeButton = document.getElementById('close') as HTMLButtonElement
|
||||
|
||||
expect(
|
||||
isComposerFieldToFieldFocus({
|
||||
currentTarget: nameInput,
|
||||
relatedTarget: closeButton
|
||||
})
|
||||
).toBe(false)
|
||||
})
|
||||
|
||||
it('returns false when relatedTarget is missing', () => {
|
||||
document.body.innerHTML = `
|
||||
<div data-workspace-composer-root="true">
|
||||
<input id="name" type="text" />
|
||||
</div>
|
||||
`
|
||||
const nameInput = document.getElementById('name') as HTMLInputElement
|
||||
|
||||
expect(
|
||||
isComposerFieldToFieldFocus({
|
||||
currentTarget: nameInput,
|
||||
relatedTarget: null
|
||||
})
|
||||
).toBe(false)
|
||||
})
|
||||
})
|
||||
@@ -1,73 +0,0 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import {
|
||||
createPresetOpenInApplication,
|
||||
shouldCommitOpenInApplicationsDraft
|
||||
} from './OpenInMenuSetting'
|
||||
import {
|
||||
getOpenInAppPresets,
|
||||
isOpenInAppPresetAdded,
|
||||
OpenInApplicationIcon
|
||||
} from '@/lib/open-in-app-catalog'
|
||||
import type { OpenInAppPreset } from '@/lib/open-in-app-catalog'
|
||||
|
||||
function requirePreset(id: string): OpenInAppPreset {
|
||||
const preset = getOpenInAppPresets().find((entry) => entry.id === id)
|
||||
if (!preset) {
|
||||
throw new Error(`Preset not found: ${id}`)
|
||||
}
|
||||
return preset
|
||||
}
|
||||
|
||||
describe('OpenInMenuSetting presets', () => {
|
||||
it('creates stable preset rows for known apps', () => {
|
||||
const cursor = requirePreset('cursor')
|
||||
|
||||
expect(createPresetOpenInApplication(cursor)).toEqual({
|
||||
id: 'cursor',
|
||||
label: 'Cursor',
|
||||
command: 'cursor'
|
||||
})
|
||||
})
|
||||
|
||||
it('recognizes legacy preset rows by command', () => {
|
||||
const cursor = requirePreset('cursor')
|
||||
|
||||
expect(isOpenInAppPresetAdded([{ command: ' cursor ' }], cursor)).toBe(true)
|
||||
})
|
||||
|
||||
it('keeps the Zed icon visible on dark menus', () => {
|
||||
const icon = OpenInApplicationIcon({ application: { command: 'zed' } })
|
||||
|
||||
expect(icon.props.className).toContain('dark:invert')
|
||||
})
|
||||
})
|
||||
|
||||
describe('OpenInMenuSetting application drafts', () => {
|
||||
it('does not commit rows until both label and command are present', () => {
|
||||
expect(
|
||||
shouldCommitOpenInApplicationsDraft([{ id: 'draft', label: 'Cursor', command: '' }])
|
||||
).toBe(false)
|
||||
expect(
|
||||
shouldCommitOpenInApplicationsDraft([{ id: 'draft', label: '', command: 'cursor' }])
|
||||
).toBe(false)
|
||||
expect(
|
||||
shouldCommitOpenInApplicationsDraft([{ id: 'draft', label: ' ', command: 'cursor' }])
|
||||
).toBe(false)
|
||||
expect(
|
||||
shouldCommitOpenInApplicationsDraft([{ id: 'draft', label: 'Cursor', command: ' ' }])
|
||||
).toBe(false)
|
||||
})
|
||||
|
||||
it('allows commit when every draft row has a label and command', () => {
|
||||
expect(shouldCommitOpenInApplicationsDraft([])).toBe(true)
|
||||
expect(
|
||||
shouldCommitOpenInApplicationsDraft([{ id: 'cursor', label: 'Cursor', command: 'cursor' }])
|
||||
).toBe(true)
|
||||
expect(
|
||||
shouldCommitOpenInApplicationsDraft([
|
||||
{ id: 'cursor', label: 'Cursor', command: 'cursor' },
|
||||
{ id: 'zed', label: 'Zed', command: 'zed' }
|
||||
])
|
||||
).toBe(true)
|
||||
})
|
||||
})
|
||||
@@ -1,65 +0,0 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { deriveNeededSectionIds } from './settings-load-performance'
|
||||
|
||||
describe('Settings load-performance helpers', () => {
|
||||
it('keeps only eager and active sections mounted for empty search on first paint', () => {
|
||||
const needed = deriveNeededSectionIds({
|
||||
navSectionIds: ['general', 'agents', 'appearance', 'terminal', 'stats', 'ssh', 'repo-a'],
|
||||
mountedSectionIds: new Set(['general']),
|
||||
activeSectionId: 'general',
|
||||
pendingSectionId: null,
|
||||
query: '',
|
||||
visibleSectionIds: new Set([
|
||||
'general',
|
||||
'agents',
|
||||
'appearance',
|
||||
'terminal',
|
||||
'stats',
|
||||
'ssh',
|
||||
'repo-a'
|
||||
])
|
||||
})
|
||||
|
||||
expect(Array.from(needed).sort()).toEqual(['general'])
|
||||
})
|
||||
|
||||
it('keeps search mounting scoped to the active section', () => {
|
||||
const needed = deriveNeededSectionIds({
|
||||
navSectionIds: ['general', 'agents', 'appearance', 'terminal', 'stats', 'repo-a'],
|
||||
mountedSectionIds: new Set(['general']),
|
||||
activeSectionId: 'general',
|
||||
pendingSectionId: null,
|
||||
query: 'stats',
|
||||
visibleSectionIds: new Set(['stats'])
|
||||
})
|
||||
|
||||
expect(needed.has('stats')).toBe(false)
|
||||
expect(needed.has('general')).toBe(false)
|
||||
})
|
||||
|
||||
it('mounts the active matched section during search', () => {
|
||||
const needed = deriveNeededSectionIds({
|
||||
navSectionIds: ['general', 'agents', 'appearance', 'terminal', 'stats', 'repo-a'],
|
||||
mountedSectionIds: new Set(['general']),
|
||||
activeSectionId: 'stats',
|
||||
pendingSectionId: null,
|
||||
query: 'stats',
|
||||
visibleSectionIds: new Set(['stats'])
|
||||
})
|
||||
|
||||
expect(needed.has('stats')).toBe(true)
|
||||
})
|
||||
|
||||
it('keeps a pending deep-link target mounted before jump work continues', () => {
|
||||
const needed = deriveNeededSectionIds({
|
||||
navSectionIds: ['general', 'agents', 'appearance', 'terminal', 'repo-a'],
|
||||
mountedSectionIds: new Set(['general']),
|
||||
activeSectionId: 'general',
|
||||
pendingSectionId: 'repo-a',
|
||||
query: '',
|
||||
visibleSectionIds: new Set(['general', 'agents', 'appearance', 'terminal', 'repo-a'])
|
||||
})
|
||||
|
||||
expect(needed.has('repo-a')).toBe(true)
|
||||
})
|
||||
})
|
||||
@@ -54,9 +54,4 @@ describe('developer permissions LAN search', () => {
|
||||
expect(await searchInLocale('ko', 'LAN')).toBe(true)
|
||||
expect(await searchInLocale('es', 'LAN')).toBe(true)
|
||||
})
|
||||
|
||||
it('indexes both wordings as distinct Chinese keywords', async () => {
|
||||
await i18n.changeLanguage('zh')
|
||||
expect(getLanEntry().keywords).toEqual(expect.arrayContaining(['局域网', '本地网络']))
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,67 +0,0 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { describeNetworkAddress } from './network-address-label'
|
||||
import { getRuntimeServerProblemLine } from './runtime-server-row-meta'
|
||||
|
||||
const quietRow = {
|
||||
accessRevoked: false,
|
||||
problem: 'Timed out waiting for the remote Orca runtime.',
|
||||
probeFailed: true,
|
||||
compatibilityBlocked: false
|
||||
}
|
||||
|
||||
describe('runtime server problem line', () => {
|
||||
it('says when an unreachable server was last seen instead of repeating the failure', () => {
|
||||
const now = Date.UTC(2026, 9, 20)
|
||||
expect(
|
||||
getRuntimeServerProblemLine({
|
||||
environment: { lastUsedAt: now - 3 * 86_400_000 },
|
||||
row: quietRow,
|
||||
remoteUpdate: undefined,
|
||||
managedStatusError: null,
|
||||
managedSummary: null,
|
||||
address: '100.88.12.5:6768',
|
||||
now
|
||||
})
|
||||
).toEqual({ text: 'Last seen 3 days ago · 100.88.12.5:6768', destructive: false })
|
||||
})
|
||||
|
||||
it('says a server was never connected rather than still waiting', () => {
|
||||
expect(
|
||||
getRuntimeServerProblemLine({
|
||||
environment: { lastUsedAt: null },
|
||||
row: quietRow,
|
||||
remoteUpdate: undefined,
|
||||
managedStatusError: null,
|
||||
managedSummary: null,
|
||||
address: '100.88.12.5:6768'
|
||||
})?.text
|
||||
).toBe('Never connected · 100.88.12.5:6768')
|
||||
})
|
||||
})
|
||||
|
||||
describe('a server paired twice', () => {
|
||||
it('names the twin ahead of reachability, since one entry must go', () => {
|
||||
expect(
|
||||
getRuntimeServerProblemLine({
|
||||
environment: { lastUsedAt: null },
|
||||
row: quietRow,
|
||||
remoteUpdate: undefined,
|
||||
managedStatusError: null,
|
||||
managedSummary: null,
|
||||
address: '100.88.12.5:6768',
|
||||
duplicateOfNames: ['Studio relay']
|
||||
})
|
||||
).toEqual({
|
||||
text: 'Same server as Studio relay, paired twice. Remove one so its projects can be edited.',
|
||||
destructive: true
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('network address label', () => {
|
||||
it('names Tailscale and LAN addresses instead of the interface', () => {
|
||||
expect(describeNetworkAddress('utun6', '100.108.226.109')).toBe('Tailscale · 100.108.226.109')
|
||||
expect(describeNetworkAddress('en0', '192.168.1.20')).toBe('Local network · 192.168.1.20')
|
||||
expect(describeNetworkAddress('eth0', '203.0.113.5')).toBe('eth0 · 203.0.113.5')
|
||||
})
|
||||
})
|
||||
@@ -12,17 +12,6 @@ describe('unnestWorktrees', () => {
|
||||
vi.spyOn(console, 'error').mockImplementation(() => {})
|
||||
})
|
||||
|
||||
it('clears the parent link on every requested worktree', async () => {
|
||||
const updateWorktreeLineage = vi.fn().mockResolvedValue(undefined)
|
||||
|
||||
await unnestWorktrees(['repo1::/a', 'repo1::/b'], updateWorktreeLineage)
|
||||
|
||||
expect(updateWorktreeLineage).toHaveBeenCalledTimes(2)
|
||||
expect(updateWorktreeLineage).toHaveBeenCalledWith('repo1::/a', { noParent: true })
|
||||
expect(updateWorktreeLineage).toHaveBeenCalledWith('repo1::/b', { noParent: true })
|
||||
expect(toast.error).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('toasts instead of rejecting when the lineage update fails', async () => {
|
||||
const updateWorktreeLineage = vi
|
||||
.fn()
|
||||
@@ -40,13 +29,4 @@ describe('unnestWorktrees', () => {
|
||||
|
||||
expect(toast.error).toHaveBeenCalledTimes(1)
|
||||
})
|
||||
|
||||
it('does nothing for an empty selection', async () => {
|
||||
const updateWorktreeLineage = vi.fn().mockResolvedValue(undefined)
|
||||
|
||||
await unnestWorktrees([], updateWorktreeLineage)
|
||||
|
||||
expect(updateWorktreeLineage).not.toHaveBeenCalled()
|
||||
expect(toast.error).not.toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import type { FolderWorkspace } from '../../../../shared/folder-workspace-types'
|
||||
import type { Repo } from '../../../../shared/repo-types'
|
||||
import type { Worktree } from '../../../../shared/worktree/types'
|
||||
import { skillInstallWorkspaceChoices } from './skill-install-workspace-choices'
|
||||
@@ -25,23 +24,4 @@ describe('skillInstallWorkspaceChoices', () => {
|
||||
})
|
||||
).toEqual([{ id: worktree.id, label: worktree.displayName, kind: 'worktree' }])
|
||||
})
|
||||
|
||||
it('keeps local, paired-runtime, and SSH workspace catalogs isolated', () => {
|
||||
const folders = [
|
||||
{ id: 'local', name: 'Local', folderPath: '/local', executionHostId: 'local' },
|
||||
{
|
||||
id: 'runtime',
|
||||
name: 'Runtime',
|
||||
folderPath: '/runtime',
|
||||
executionHostId: 'runtime:environment_1'
|
||||
},
|
||||
{ id: 'ssh', name: 'SSH', folderPath: '/ssh', executionHostId: 'ssh:ssh_1' }
|
||||
] as FolderWorkspace[]
|
||||
const input = { repos: [], worktreesByRepo: {}, folderWorkspaces: folders }
|
||||
expect(skillInstallWorkspaceChoices({ ...input, environmentId: 'local' })).toHaveLength(1)
|
||||
expect(skillInstallWorkspaceChoices({ ...input, environmentId: 'environment_1' })).toHaveLength(
|
||||
1
|
||||
)
|
||||
expect(skillInstallWorkspaceChoices({ ...input, environmentId: 'ssh:ssh_1' })).toHaveLength(1)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,54 +0,0 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import type { ClaudeManagedAccount } from '../../../../shared/managed-account-types'
|
||||
import {
|
||||
getUsageProviderAccountsSectionId,
|
||||
usageRowSignInOpensSettings
|
||||
} from './usage-provider-settings-target'
|
||||
|
||||
describe('getUsageProviderAccountsSectionId', () => {
|
||||
it('routes providers only to settings sections that exist', () => {
|
||||
expect(getUsageProviderAccountsSectionId('claude')).toBe('accounts-claude')
|
||||
expect(getUsageProviderAccountsSectionId('codex')).toBe('accounts-codex')
|
||||
expect(getUsageProviderAccountsSectionId('gemini')).toBe('accounts-gemini')
|
||||
expect(getUsageProviderAccountsSectionId('opencode-go')).toBe('accounts-opencode-go')
|
||||
expect(getUsageProviderAccountsSectionId('minimax')).toBe('accounts-minimax')
|
||||
expect(getUsageProviderAccountsSectionId('grok')).toBe('accounts-grok')
|
||||
expect(getUsageProviderAccountsSectionId('cursor')).toBe('accounts-cursor')
|
||||
expect(getUsageProviderAccountsSectionId('zcode')).toBe('accounts-zcode')
|
||||
})
|
||||
|
||||
it('does not invent an Accounts section for CLI-owned credentials', () => {
|
||||
expect(getUsageProviderAccountsSectionId('antigravity')).toBeNull()
|
||||
expect(getUsageProviderAccountsSectionId('kimi')).toBeNull()
|
||||
})
|
||||
})
|
||||
|
||||
describe('usageRowSignInOpensSettings', () => {
|
||||
const account: ClaudeManagedAccount = {
|
||||
id: 'a',
|
||||
email: 'a@example.com',
|
||||
authMethod: 'subscription-oauth',
|
||||
managedAuthPath: '/unused',
|
||||
createdAt: 1,
|
||||
updatedAt: 1,
|
||||
lastAuthenticatedAt: 1
|
||||
}
|
||||
const saved = { claudeManagedAccounts: [account] }
|
||||
|
||||
it('keeps the Claude menu, with its per-account Sign in, once accounts are saved', () => {
|
||||
expect(usageRowSignInOpensSettings('claude', saved)).toBe(false)
|
||||
expect(usageRowSignInOpensSettings('claude', { claudeManagedAccounts: [] })).toBe(true)
|
||||
expect(usageRowSignInOpensSettings('claude', null)).toBe(true)
|
||||
expect(usageRowSignInOpensSettings('codex', saved)).toBe(true)
|
||||
expect(usageRowSignInOpensSettings('kimi', null)).toBe(false)
|
||||
})
|
||||
|
||||
it("keeps the Settings shortcut on a remote server, whose accounts aren't local", () => {
|
||||
expect(
|
||||
usageRowSignInOpensSettings('claude', {
|
||||
claudeManagedAccounts: [account],
|
||||
activeRuntimeEnvironmentId: 'env-1'
|
||||
})
|
||||
).toBe(true)
|
||||
})
|
||||
})
|
||||
@@ -14,17 +14,6 @@ function team(id: string): LinearTeam {
|
||||
}
|
||||
|
||||
describe('reconcileLinearTeamSelection', () => {
|
||||
it('selects every available team when the saved selection is sticky-all', () => {
|
||||
expect(Array.from(reconcileLinearTeamSelection([team('a'), team('b')], null))).toEqual([
|
||||
'a',
|
||||
'b'
|
||||
])
|
||||
})
|
||||
|
||||
it('preserves saved teams that still exist', () => {
|
||||
expect(Array.from(reconcileLinearTeamSelection([team('a'), team('b')], ['b']))).toEqual(['b'])
|
||||
})
|
||||
|
||||
it('drops stale saved teams after switching workspaces', () => {
|
||||
expect(Array.from(reconcileLinearTeamSelection([team('c'), team('d')], ['a', 'd']))).toEqual([
|
||||
'd'
|
||||
@@ -52,21 +41,6 @@ describe('reconcileLinearTeamSelection', () => {
|
||||
})
|
||||
|
||||
describe('storedLinearTeamSelection', () => {
|
||||
it('keeps a string array', () => {
|
||||
expect(storedLinearTeamSelection(['a', 'b'])).toEqual(['a', 'b'])
|
||||
})
|
||||
|
||||
it('reads null and undefined as sticky-all', () => {
|
||||
expect(storedLinearTeamSelection(null)).toBeNull()
|
||||
expect(storedLinearTeamSelection(undefined)).toBeNull()
|
||||
})
|
||||
|
||||
it('reads a string, an object or a number as sticky-all instead of throwing', () => {
|
||||
expect(storedLinearTeamSelection('a')).toBeNull()
|
||||
expect(storedLinearTeamSelection({ 0: 'a' })).toBeNull()
|
||||
expect(storedLinearTeamSelection(7)).toBeNull()
|
||||
})
|
||||
|
||||
it('drops non-string entries from a mixed array', () => {
|
||||
expect(storedLinearTeamSelection(['a', 1, null, 'b'])).toEqual(['a', 'b'])
|
||||
})
|
||||
|
||||
-4
@@ -36,8 +36,4 @@ describe('pane-scoped terminal keyboard protocol agent', () => {
|
||||
|
||||
expect(resolvedKittyKeyboard(null, 'grok')).toBe(false)
|
||||
})
|
||||
|
||||
it('keeps explicit pane startup identity ahead of a stale tab launch agent', () => {
|
||||
expect(resolvePaneKeyboardProtocolAgent({ launchAgent: 'claude' }, 'grok')).toBe('claude')
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { readFileSync } from 'node:fs'
|
||||
import { join } from 'node:path'
|
||||
import { Terminal } from '@xterm/headless'
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { TerminalKittyKeyboardModeTracker } from '../../../../shared/terminal-kitty-keyboard-mode-tracker'
|
||||
import {
|
||||
@@ -15,26 +14,6 @@ function readFixture(name: string): string {
|
||||
return readFileSync(join(FIXTURE_DIR, `${name}.txt`), 'utf8')
|
||||
}
|
||||
|
||||
/** pi's editor rows: the lines between the last two full-width rules above the footer. */
|
||||
async function renderEditorRows(transcript: string): Promise<string[]> {
|
||||
const terminal = new Terminal({ cols: 100, rows: 30, allowProposedApi: true })
|
||||
await new Promise<void>((resolve) => terminal.write(transcript, resolve))
|
||||
const buffer = terminal.buffer.active
|
||||
const rows: string[] = []
|
||||
for (let row = 0; row < terminal.rows; row++) {
|
||||
rows.push(
|
||||
buffer
|
||||
.getLine(buffer.viewportY + row)
|
||||
?.translateToString(true)
|
||||
.trim() ?? ''
|
||||
)
|
||||
}
|
||||
terminal.dispose()
|
||||
const rules = rows.flatMap((text, index) => (/^─{50,}$/.test(text) ? [index] : []))
|
||||
const [open, close] = rules.slice(-2)
|
||||
return rows.slice(open + 1, close).filter((text) => text.length > 0)
|
||||
}
|
||||
|
||||
function enter(overrides: Partial<TerminalShortcutEvent>): TerminalShortcutEvent {
|
||||
return {
|
||||
key: 'Enter',
|
||||
@@ -68,25 +47,6 @@ function resolveForPane(event: TerminalShortcutEvent, modes: TerminalKittyKeyboa
|
||||
}
|
||||
|
||||
describe('modified Enter for an app that armed modifyOtherKeys (STA-7701)', () => {
|
||||
it('reads pi editing semantics from the captured transcripts', async () => {
|
||||
// ESC CR flips meaning with pi's private kitty state, which the pane cannot see.
|
||||
expect(await renderEditorRows(readFixture('pi-0-74-modify-other-keys-esc-cr'))).toEqual(['def'])
|
||||
expect(await renderEditorRows(readFixture('pi-0-74-late-kitty-esc-cr'))).toEqual(['abc', 'def'])
|
||||
// CSI 27 ; mod ; 13 ~ means the same thing in both states.
|
||||
for (const name of [
|
||||
'pi-0-74-modify-other-keys-shift-enter-csi27',
|
||||
'pi-0-74-late-kitty-shift-enter-csi27'
|
||||
]) {
|
||||
expect(await renderEditorRows(readFixture(name))).toEqual(['abc', 'def'])
|
||||
}
|
||||
for (const name of [
|
||||
'pi-0-74-modify-other-keys-alt-enter-csi27',
|
||||
'pi-0-74-late-kitty-alt-enter-csi27'
|
||||
]) {
|
||||
expect(await renderEditorRows(readFixture(name))).toEqual(['def'])
|
||||
}
|
||||
})
|
||||
|
||||
it('sends xterm modifyOtherKeys Enter chords once pi has armed the mode', () => {
|
||||
const modes = new TerminalKittyKeyboardModeTracker()
|
||||
modes.scan(readFixture('pi-0-74-modify-other-keys-esc-cr'))
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
// Paired-owned workspace layouts stay on their server; even layout-owner builds must never send them to local main.
|
||||
import { readdirSync, readFileSync, statSync } from 'node:fs'
|
||||
import path from 'node:path'
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
import type { WorktreeOperationRouteState } from '@/lib/worktree-operation-route'
|
||||
import type { AppState } from '@/store/types'
|
||||
@@ -14,7 +12,6 @@ import {
|
||||
sendToServerMain,
|
||||
type WindowLayoutCommand
|
||||
} from './layout-apply-send'
|
||||
import { runtimeEnvironmentIdUnlessTwin } from '@/lib/resolve-owner'
|
||||
import { createInitialTerminalThroughLayoutOwner } from '@/store/terminals/terminal-tab-create-through-layout-owner'
|
||||
|
||||
const WORKTREE_ID = 'repo-1::/srv/w'
|
||||
@@ -80,10 +77,6 @@ describe.each(PAIRED_WORKSPACES)('$name', ({ workspaceId, rows }) => {
|
||||
mocks.callRuntimeRpc.mockReset()
|
||||
})
|
||||
|
||||
it('resolves to its server, so every guard below reads it as paired', () => {
|
||||
expect(runtimeEnvironmentIdUnlessTwin(rows, workspaceId)).toBe('env-b')
|
||||
})
|
||||
|
||||
it.each(COMMANDS)('sendLayoutApply sends no $type', (command) => {
|
||||
const handlers = { apply: vi.fn(), asToday: vi.fn(), refused: vi.fn() }
|
||||
|
||||
@@ -117,39 +110,3 @@ describe.each(PAIRED_WORKSPACES)('$name', ({ workspaceId, rows }) => {
|
||||
expect(layoutApplyCalls()).toEqual([])
|
||||
})
|
||||
})
|
||||
|
||||
/** Every renderer source file that names the `layout.apply` method, with its count. */
|
||||
function layoutApplySenders(root: string): Record<string, number> {
|
||||
const found: Record<string, number> = {}
|
||||
const walk = (dir: string): void => {
|
||||
for (const entry of readdirSync(dir)) {
|
||||
const full = path.join(dir, entry)
|
||||
if (statSync(full).isDirectory()) {
|
||||
walk(full)
|
||||
continue
|
||||
}
|
||||
if (!/\.tsx?$/.test(entry) || /\.(test|spec)\.tsx?$/.test(entry)) {
|
||||
continue
|
||||
}
|
||||
const count = readFileSync(full, 'utf8').split(`'layout.apply'`).length - 1
|
||||
if (count > 0) {
|
||||
found[path.relative(root, full).split(path.sep).join('/')] = count
|
||||
}
|
||||
}
|
||||
}
|
||||
walk(root)
|
||||
return found
|
||||
}
|
||||
|
||||
describe('layout.apply senders', () => {
|
||||
// A new sender must go through sendLayoutApply or carry the same guard, plus a case above.
|
||||
it('are only the guarded paths pinned above', () => {
|
||||
expect(layoutApplySenders(path.resolve(__dirname, '..'))).toEqual({
|
||||
// Only sendLayoutApply and sendToServerMain (layout-apply-send.ts) call it.
|
||||
'runtime/layout-apply-call.ts': 1,
|
||||
// Tests' stand-in for main: it answers layout.apply, never sends it.
|
||||
'runtime/layout-apply-test-transport.test-fixture.ts': 1,
|
||||
'store/terminals/terminal-tab-create-through-layout-owner.ts': 1
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -41,11 +41,6 @@ afterEach(() => {
|
||||
})
|
||||
|
||||
describe('setActiveFile with an id that has no open file', () => {
|
||||
it('premise: the open document is active', () => {
|
||||
expect(store.getState().activeFileId).toBe(fileId)
|
||||
expect(store.getState().activeFileIdByWorktree['wt-1']).toBe(fileId)
|
||||
})
|
||||
|
||||
it('leaves the in-memory and per-workspace active file unchanged', () => {
|
||||
store.getState().setActiveFile(GHOST_ID)
|
||||
expect(store.getState().activeFileId).toBe(fileId)
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
import { expect, it } from 'vitest'
|
||||
import { extractPendingAsk } from './native-chat-ask'
|
||||
import type { NativeChatMessage } from '../../native-chat-types'
|
||||
|
||||
type Block = NativeChatMessage['blocks'][number]
|
||||
|
||||
function ask(text: string): Block {
|
||||
return { type: 'tool-call', name: 'AskUserQuestion', input: { questions: [{ question: text }] } }
|
||||
}
|
||||
function toolResult(): Block {
|
||||
return { type: 'tool-result', output: 'ok' }
|
||||
}
|
||||
function assistant(id: string, blocks: Block[]): NativeChatMessage {
|
||||
return { id, role: 'assistant', timestamp: 1, source: 'transcript', blocks }
|
||||
}
|
||||
|
||||
it('keeps the newest ask pending when an older duplicate-content ask resolves first', () => {
|
||||
const pending = extractPendingAsk([assistant('m', [ask('same'), ask('same'), toolResult()])])
|
||||
expect(pending?.questions[0]?.question).toBe('same')
|
||||
})
|
||||
Reference in New Issue
Block a user