mirror of
https://github.com/stablyai/orca.git
synced 2026-10-30 16:47:34 +00:00
test: consolidate discovery coverage and replace copied oracles (#32211)
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
This commit is contained in:
@@ -204,6 +204,26 @@ describe('installed agent skill discovery lifecycle', () => {
|
||||
expect(discover).toHaveBeenLastCalledWith(LOCAL, { projectRuntime: repairProjectRuntime })
|
||||
})
|
||||
|
||||
it('keeps host, WSL, project, and filtered results in separate cache entries', async () => {
|
||||
const targets: (SkillDiscoveryTarget | undefined)[] = [
|
||||
undefined,
|
||||
{ runtime: 'wsl', wslDistro: null },
|
||||
{ runtime: 'wsl', wslDistro: 'Ubuntu' },
|
||||
{ projectRuntime: resolvedHostProjectRuntime },
|
||||
{ projectRuntime: resolvedWslProjectRuntime },
|
||||
{ names: ['orchestration'], sourceKinds: ['home'] },
|
||||
{ names: ['computer-use'], sourceKinds: ['home'] }
|
||||
]
|
||||
for (const [index, target] of targets.entries()) {
|
||||
discoverSkillsForRuntimeTarget.mockResolvedValueOnce(result(index))
|
||||
await expect(discoverInstalledAgentSkills(false, target)).resolves.toEqual(result(index))
|
||||
}
|
||||
for (const [index, target] of targets.entries()) {
|
||||
await expect(discoverInstalledAgentSkills(false, target)).resolves.toEqual(result(index))
|
||||
}
|
||||
expect(discoverSkillsForRuntimeTarget).toHaveBeenCalledTimes(targets.length)
|
||||
})
|
||||
|
||||
it('keys targets by runtime and project identity', () => {
|
||||
expect(getSkillDiscoveryTargetKey(undefined)).toBe('host')
|
||||
expect(getSkillDiscoveryTargetKey({ runtime: 'wsl', wslDistro: 'Ubuntu' })).toBe('wsl:Ubuntu')
|
||||
|
||||
@@ -15,11 +15,14 @@ import type { PublicKnownRuntimeEnvironment } from '../../../shared/runtime-envi
|
||||
import { createCompatibleRuntimeStatusResponseIfNeeded } from '@/runtime/runtime-compatibility-test-fixture'
|
||||
import { clearRuntimeCompatibilityCacheForTests } from '@/runtime/runtime-rpc-client'
|
||||
import { useAppStore } from '@/store'
|
||||
import {
|
||||
discoverInstalledAgentSkills,
|
||||
resetSkillDiscoveryCacheForTests
|
||||
} from './installed-agent-skill-discovery'
|
||||
import { INSTALLED_AGENT_SKILL_DISCOVERY_FRESH_MS } from './installed-agent-skill-discovery-cache'
|
||||
import {
|
||||
GLOBAL_AGENT_SKILL_SOURCE_KINDS,
|
||||
type InstalledAgentSkillState,
|
||||
_installedAgentSkillDiscoveryInternalsForTests,
|
||||
notifyInstalledAgentSkillsChanged,
|
||||
notifyInstalledAgentSkillsRefreshed,
|
||||
useInstalledAgentSkillNames
|
||||
@@ -172,7 +175,7 @@ afterEach(async () => {
|
||||
latestState = null
|
||||
renderedStates.length = 0
|
||||
suspendNextProbeRender = null
|
||||
_installedAgentSkillDiscoveryInternalsForTests.reset()
|
||||
resetSkillDiscoveryCacheForTests()
|
||||
clearRuntimeCompatibilityCacheForTests()
|
||||
useAppStore.setState({
|
||||
settings: null,
|
||||
@@ -924,7 +927,6 @@ describe('useInstalledAgentSkill', () => {
|
||||
value: { skills: { discover } }
|
||||
})
|
||||
|
||||
const { discoverInstalledAgentSkills } = _installedAgentSkillDiscoveryInternalsForTests
|
||||
await discoverInstalledAgentSkills(false, undefined)
|
||||
await expect(discoverInstalledAgentSkills(false, undefined)).resolves.toEqual(
|
||||
expect.objectContaining({ skills: [] })
|
||||
|
||||
@@ -1,20 +1,11 @@
|
||||
import { afterEach, describe, expect, it, vi } from 'vitest'
|
||||
import type { DiscoveredSkill, SkillDiscoveryResult } from '../../../shared/skills'
|
||||
import type { ProjectExecutionRuntimeResolution } from '../../../shared/project-execution-runtime'
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import type { DiscoveredSkill } from '../../../shared/skills'
|
||||
import {
|
||||
GLOBAL_AGENT_SKILL_SOURCE_KINDS,
|
||||
_installedAgentSkillDiscoveryInternalsForTests,
|
||||
hasInstalledAgentSkill,
|
||||
hasInstalledAgentSkillNamed,
|
||||
notifyInstalledAgentSkillsRefreshed
|
||||
hasInstalledAgentSkillNamed
|
||||
} from './useInstalledAgentSkills'
|
||||
|
||||
afterEach(() => {
|
||||
_installedAgentSkillDiscoveryInternalsForTests.reset()
|
||||
vi.restoreAllMocks()
|
||||
vi.unstubAllGlobals()
|
||||
})
|
||||
|
||||
function skill(overrides: Partial<DiscoveredSkill>): DiscoveredSkill {
|
||||
return {
|
||||
id: 'skill-1',
|
||||
@@ -32,28 +23,6 @@ function skill(overrides: Partial<DiscoveredSkill>): DiscoveredSkill {
|
||||
}
|
||||
}
|
||||
|
||||
function discoveryResult(skills: DiscoveredSkill[] = []): SkillDiscoveryResult {
|
||||
return {
|
||||
skills,
|
||||
sources: [],
|
||||
scannedAt: Date.now()
|
||||
}
|
||||
}
|
||||
|
||||
function deferred<T>(): {
|
||||
promise: Promise<T>
|
||||
resolve: (value: T) => void
|
||||
reject: (reason?: unknown) => void
|
||||
} {
|
||||
let resolve!: (value: T) => void
|
||||
let reject!: (reason?: unknown) => void
|
||||
const promise = new Promise<T>((resolvePromise, rejectPromise) => {
|
||||
resolve = resolvePromise
|
||||
reject = rejectPromise
|
||||
})
|
||||
return { promise, resolve, reject }
|
||||
}
|
||||
|
||||
describe('hasInstalledAgentSkill', () => {
|
||||
it('matches installed skills by summarized name', () => {
|
||||
expect(hasInstalledAgentSkill([skill({ name: 'orca-cli' })], 'orca-cli')).toBe(true)
|
||||
@@ -156,219 +125,3 @@ describe('hasInstalledAgentSkill', () => {
|
||||
expect(hasInstalledAgentSkill([skill({ name: 'linear-tickets' })], 'orca-linear')).toBe(false)
|
||||
})
|
||||
})
|
||||
|
||||
describe('isOrchestrationSkillName', () => {
|
||||
it('matches only the orchestration skill name', () => {
|
||||
expect(
|
||||
_installedAgentSkillDiscoveryInternalsForTests.isOrchestrationSkillName('orchestration')
|
||||
).toBe(true)
|
||||
expect(
|
||||
_installedAgentSkillDiscoveryInternalsForTests.isOrchestrationSkillName(' Orchestration ')
|
||||
).toBe(true)
|
||||
expect(
|
||||
_installedAgentSkillDiscoveryInternalsForTests.isOrchestrationSkillName('computer-use')
|
||||
).toBe(false)
|
||||
})
|
||||
})
|
||||
|
||||
describe('discoverInstalledAgentSkills', () => {
|
||||
const projectWslRuntime: ProjectExecutionRuntimeResolution = {
|
||||
status: 'resolved',
|
||||
runtime: {
|
||||
kind: 'wsl',
|
||||
hostPlatform: 'wsl',
|
||||
projectId: 'repo-1',
|
||||
distro: 'Ubuntu',
|
||||
reason: 'project-override',
|
||||
cacheKey: 'repo-1:wsl:Ubuntu'
|
||||
}
|
||||
}
|
||||
|
||||
const projectHostRuntime: ProjectExecutionRuntimeResolution = {
|
||||
status: 'resolved',
|
||||
runtime: {
|
||||
kind: 'windows-host',
|
||||
hostPlatform: 'win32',
|
||||
projectId: 'repo-1',
|
||||
reason: 'project-override',
|
||||
cacheKey: 'repo-1:windows-host'
|
||||
}
|
||||
}
|
||||
|
||||
it('starts a fresh scan when a forced refresh arrives during a background scan', async () => {
|
||||
const firstScan = deferred<SkillDiscoveryResult>()
|
||||
const secondScan = deferred<SkillDiscoveryResult>()
|
||||
const discover = vi.fn<() => Promise<SkillDiscoveryResult>>()
|
||||
discover.mockReturnValueOnce(firstScan.promise)
|
||||
discover.mockReturnValueOnce(secondScan.promise)
|
||||
vi.stubGlobal('window', {
|
||||
api: { skills: { discover } }
|
||||
})
|
||||
|
||||
const backgroundRefresh =
|
||||
_installedAgentSkillDiscoveryInternalsForTests.discoverInstalledAgentSkills(false)
|
||||
const forcedRefresh =
|
||||
_installedAgentSkillDiscoveryInternalsForTests.discoverInstalledAgentSkills(true)
|
||||
|
||||
expect(discover).toHaveBeenCalledTimes(1)
|
||||
|
||||
const staleResult = discoveryResult([])
|
||||
firstScan.resolve(staleResult)
|
||||
await expect(backgroundRefresh).resolves.toBe(staleResult)
|
||||
|
||||
expect(discover).toHaveBeenCalledTimes(2)
|
||||
|
||||
const freshResult = discoveryResult([skill({ name: 'orca-cli' })])
|
||||
secondScan.resolve(freshResult)
|
||||
await expect(forcedRefresh).resolves.toBe(freshResult)
|
||||
})
|
||||
|
||||
it('lets completed-scan broadcasts reuse the cached result', async () => {
|
||||
const discover = vi
|
||||
.fn<() => Promise<SkillDiscoveryResult>>()
|
||||
.mockResolvedValue(discoveryResult([skill({ name: 'orca-linear' })]))
|
||||
vi.stubGlobal('window', {
|
||||
api: { skills: { discover } },
|
||||
dispatchEvent: vi.fn(),
|
||||
CustomEvent: class {}
|
||||
})
|
||||
|
||||
await _installedAgentSkillDiscoveryInternalsForTests.discoverInstalledAgentSkills(true)
|
||||
notifyInstalledAgentSkillsRefreshed()
|
||||
const fromSubscribers = [1, 2, 3].map(() =>
|
||||
_installedAgentSkillDiscoveryInternalsForTests.discoverInstalledAgentSkills(false)
|
||||
)
|
||||
|
||||
expect(discover).toHaveBeenCalledTimes(1)
|
||||
for (const pending of fromSubscribers) {
|
||||
await expect(pending).resolves.toMatchObject({
|
||||
skills: [expect.objectContaining({ name: 'orca-linear' })]
|
||||
})
|
||||
}
|
||||
expect(discover).toHaveBeenCalledTimes(1)
|
||||
})
|
||||
|
||||
it('caches host and WSL discovery results separately', async () => {
|
||||
const hostResult = discoveryResult([skill({ name: 'host-skill' })])
|
||||
const wslResult = discoveryResult([skill({ name: 'wsl-skill' })])
|
||||
const discover = vi
|
||||
.fn<
|
||||
(target?: {
|
||||
runtime?: 'host' | 'wsl'
|
||||
wslDistro?: string | null
|
||||
}) => Promise<SkillDiscoveryResult>
|
||||
>()
|
||||
.mockResolvedValueOnce(hostResult)
|
||||
.mockResolvedValueOnce(wslResult)
|
||||
vi.stubGlobal('window', {
|
||||
api: { skills: { discover } }
|
||||
})
|
||||
|
||||
await expect(
|
||||
_installedAgentSkillDiscoveryInternalsForTests.discoverInstalledAgentSkills(false)
|
||||
).resolves.toBe(hostResult)
|
||||
await expect(
|
||||
_installedAgentSkillDiscoveryInternalsForTests.discoverInstalledAgentSkills(false, {
|
||||
runtime: 'wsl'
|
||||
})
|
||||
).resolves.toBe(wslResult)
|
||||
await expect(
|
||||
_installedAgentSkillDiscoveryInternalsForTests.discoverInstalledAgentSkills(false)
|
||||
).resolves.toBe(hostResult)
|
||||
|
||||
expect(discover).toHaveBeenCalledTimes(2)
|
||||
expect(discover).toHaveBeenNthCalledWith(1, undefined)
|
||||
expect(discover).toHaveBeenNthCalledWith(2, { runtime: 'wsl', wslDistro: null })
|
||||
})
|
||||
|
||||
it('forwards filters and isolates filtered discovery caches', async () => {
|
||||
const orchestrationResult = discoveryResult([skill({ name: 'orchestration' })])
|
||||
const computerUseResult = discoveryResult([skill({ name: 'computer-use' })])
|
||||
const discover = vi
|
||||
.fn()
|
||||
.mockResolvedValueOnce(orchestrationResult)
|
||||
.mockResolvedValueOnce(computerUseResult)
|
||||
vi.stubGlobal('window', { api: { skills: { discover } } })
|
||||
|
||||
await _installedAgentSkillDiscoveryInternalsForTests.discoverInstalledAgentSkills(
|
||||
false,
|
||||
{ runtime: 'wsl', wslDistro: 'Ubuntu' },
|
||||
undefined,
|
||||
['orchestration'],
|
||||
GLOBAL_AGENT_SKILL_SOURCE_KINDS
|
||||
)
|
||||
await _installedAgentSkillDiscoveryInternalsForTests.discoverInstalledAgentSkills(
|
||||
false,
|
||||
{ runtime: 'wsl', wslDistro: 'Ubuntu' },
|
||||
undefined,
|
||||
['computer-use'],
|
||||
GLOBAL_AGENT_SKILL_SOURCE_KINDS
|
||||
)
|
||||
|
||||
expect(discover).toHaveBeenNthCalledWith(1, {
|
||||
runtime: 'wsl',
|
||||
wslDistro: 'Ubuntu',
|
||||
names: ['orchestration'],
|
||||
sourceKinds: ['home']
|
||||
})
|
||||
expect(discover).toHaveBeenNthCalledWith(2, {
|
||||
runtime: 'wsl',
|
||||
wslDistro: 'Ubuntu',
|
||||
names: ['computer-use'],
|
||||
sourceKinds: ['home']
|
||||
})
|
||||
})
|
||||
|
||||
it('forwards project runtime targets to skill discovery', async () => {
|
||||
const wslResult = discoveryResult([skill({ name: 'wsl-skill' })])
|
||||
const discover = vi.fn().mockResolvedValueOnce(wslResult)
|
||||
vi.stubGlobal('window', {
|
||||
api: { skills: { discover } }
|
||||
})
|
||||
|
||||
await expect(
|
||||
_installedAgentSkillDiscoveryInternalsForTests.discoverInstalledAgentSkills(false, {
|
||||
projectRuntime: projectWslRuntime
|
||||
})
|
||||
).resolves.toBe(wslResult)
|
||||
|
||||
expect(discover).toHaveBeenCalledWith({
|
||||
runtime: 'wsl',
|
||||
wslDistro: 'Ubuntu',
|
||||
projectRuntime: projectWslRuntime
|
||||
})
|
||||
})
|
||||
|
||||
it('caches project host runtime separately from generic host discovery', async () => {
|
||||
const genericHostResult = discoveryResult([skill({ name: 'generic-host-skill' })])
|
||||
const projectHostResult = discoveryResult([skill({ name: 'project-host-skill' })])
|
||||
const discover = vi
|
||||
.fn()
|
||||
.mockResolvedValueOnce(genericHostResult)
|
||||
.mockResolvedValueOnce(projectHostResult)
|
||||
vi.stubGlobal('window', {
|
||||
api: { skills: { discover } }
|
||||
})
|
||||
|
||||
await expect(
|
||||
_installedAgentSkillDiscoveryInternalsForTests.discoverInstalledAgentSkills(false)
|
||||
).resolves.toBe(genericHostResult)
|
||||
await expect(
|
||||
_installedAgentSkillDiscoveryInternalsForTests.discoverInstalledAgentSkills(false, {
|
||||
projectRuntime: projectHostRuntime
|
||||
})
|
||||
).resolves.toBe(projectHostResult)
|
||||
await expect(
|
||||
_installedAgentSkillDiscoveryInternalsForTests.discoverInstalledAgentSkills(false, {
|
||||
projectRuntime: projectHostRuntime
|
||||
})
|
||||
).resolves.toBe(projectHostResult)
|
||||
|
||||
expect(discover).toHaveBeenCalledTimes(2)
|
||||
expect(discover).toHaveBeenNthCalledWith(1, undefined)
|
||||
expect(discover).toHaveBeenNthCalledWith(2, {
|
||||
runtime: 'host',
|
||||
projectRuntime: projectHostRuntime
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -12,9 +12,7 @@ import { markOrchestrationSetupComplete } from '@/lib/orchestration-setup-state'
|
||||
import {
|
||||
discoverInstalledAgentSkills,
|
||||
getCachedSkillDiscovery,
|
||||
getRuntimeScopedSkillDiscoveryKey,
|
||||
getSkillDiscoveryTargetKey,
|
||||
resetSkillDiscoveryCacheForTests
|
||||
getRuntimeScopedSkillDiscoveryKey
|
||||
} from './installed-agent-skill-discovery'
|
||||
import {
|
||||
getInstalledAgentSkillVerdict,
|
||||
@@ -107,13 +105,6 @@ export function notifyInstalledAgentSkillsRefreshed(): void {
|
||||
}
|
||||
}
|
||||
|
||||
export const _installedAgentSkillDiscoveryInternalsForTests = {
|
||||
discoverInstalledAgentSkills,
|
||||
getSkillDiscoveryTargetKey,
|
||||
isOrchestrationSkillName,
|
||||
reset: resetSkillDiscoveryCacheForTests
|
||||
}
|
||||
|
||||
export function useInstalledAgentSkill(
|
||||
skillName: string,
|
||||
options: InstalledAgentSkillOptions = {}
|
||||
|
||||
@@ -12,76 +12,11 @@ describe('web preload API composition', () => {
|
||||
vi.doUnmock('../../../shared/e2e-config')
|
||||
})
|
||||
|
||||
it('installs the exact concrete surface without enumerating fallback namespaces', async () => {
|
||||
it('keeps fallback namespaces non-enumerable and non-thenable', async () => {
|
||||
const globals = installBrowserGlobals('Linux')
|
||||
const { installWebPreloadApi } = await import('./web-preload-api')
|
||||
installWebPreloadApi()
|
||||
|
||||
expect(Object.keys(globals.window.api)).toEqual([
|
||||
'app',
|
||||
'starNag',
|
||||
'platform',
|
||||
'workspacePorts',
|
||||
'orcaProfiles',
|
||||
'e2e',
|
||||
'settings',
|
||||
'agentAwake',
|
||||
'keybindings',
|
||||
'ui',
|
||||
'crashReports',
|
||||
'diagnostics',
|
||||
'session',
|
||||
'onboarding',
|
||||
'cache',
|
||||
'runtime',
|
||||
'nativeChat',
|
||||
'runtimeEnvironments',
|
||||
'repos',
|
||||
'worktrees',
|
||||
'fs',
|
||||
'git',
|
||||
'browser',
|
||||
'emulator',
|
||||
'gh',
|
||||
'gl',
|
||||
'hostedReview',
|
||||
'linear',
|
||||
'jira',
|
||||
'hooks',
|
||||
'stats',
|
||||
'memory',
|
||||
'aiVault',
|
||||
'preflight',
|
||||
'notifications',
|
||||
'rateLimits',
|
||||
'opencodeGoCredentials',
|
||||
'minimaxCredentials',
|
||||
'zcodePlanCredentials',
|
||||
'grokAccounts',
|
||||
'cursorAccounts',
|
||||
'codexAccounts',
|
||||
'claudeAccounts',
|
||||
'cli',
|
||||
'macosTccPrompts',
|
||||
'codexConfigSync',
|
||||
'developerPermissions',
|
||||
'computerUsePermissions',
|
||||
'updater',
|
||||
'shell',
|
||||
'skills',
|
||||
'pty',
|
||||
'ssh',
|
||||
'wsl',
|
||||
'wslMove',
|
||||
'pwsh',
|
||||
'gitBash',
|
||||
'agentStatus',
|
||||
'mobile',
|
||||
'telemetryTrack',
|
||||
'telemetrySetOptIn',
|
||||
'telemetryGetConsentState',
|
||||
'telemetryAcknowledgeBanner'
|
||||
])
|
||||
expect('onFileDrop' in globals.window.api.ui).toBe(false)
|
||||
expect(Object.keys(globals.window.api.projects)).toEqual([])
|
||||
const projects: Record<string, unknown> = globals.window.api.projects
|
||||
|
||||
@@ -2,7 +2,6 @@ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
import type { PreloadApi } from '../../../preload/api-types'
|
||||
import type { RuntimeRpcResponse } from '../../../shared/runtime-rpc-envelope'
|
||||
import {
|
||||
installApi,
|
||||
installBrowserGlobals,
|
||||
writeStoredRuntimeEnvironment
|
||||
} from './web-preload-api-test-harness'
|
||||
@@ -17,76 +16,6 @@ describe('web GitHub preload API', () => {
|
||||
vi.doUnmock('./web-runtime-client')
|
||||
})
|
||||
|
||||
it('keeps the web GitHub preload key set in parity with the preload contract', async () => {
|
||||
const { api } = await installApi('Linux')
|
||||
|
||||
expect(Object.keys(api.gh).sort()).toEqual(
|
||||
[
|
||||
'addIssueComment',
|
||||
'addIssueCommentBySlug',
|
||||
'addPRReviewComment',
|
||||
'addPRReviewCommentReply',
|
||||
'checkOrcaStarred',
|
||||
'clearProjectItemField',
|
||||
'countWorkItems',
|
||||
'createIssue',
|
||||
'deleteIssueCommentBySlug',
|
||||
'diagnoseAuth',
|
||||
'enqueuePRRefresh',
|
||||
'getProjectViewTable',
|
||||
'issue',
|
||||
'listAccessibleProjects',
|
||||
'listAssignableUsers',
|
||||
'listAssignableUsersBySlug',
|
||||
'listBindableAccounts',
|
||||
'listIssueTypesBySlug',
|
||||
'listIssues',
|
||||
'listLabels',
|
||||
'listLabelsBySlug',
|
||||
'listProjectViews',
|
||||
'listWorkItems',
|
||||
'markPRReadyForReview',
|
||||
'mergePR',
|
||||
'notifyWorkItemMutated',
|
||||
'onPRRefreshEvent',
|
||||
'onWorkItemMutated',
|
||||
'prCheckDetails',
|
||||
'prChecks',
|
||||
'prComments',
|
||||
'prFileContents',
|
||||
'prForBranch',
|
||||
'projectWorkItemDetailsBySlug',
|
||||
'rateLimit',
|
||||
'refreshPRNow',
|
||||
'removePRReviewers',
|
||||
'repoSlug',
|
||||
'repoUpstream',
|
||||
'reportVisiblePRRefreshCandidates',
|
||||
'rerunPRChecks',
|
||||
'requestPRReviewers',
|
||||
'resolveProjectRef',
|
||||
'resolveReviewThread',
|
||||
'setPRAutoMerge',
|
||||
'setPRCommentReaction',
|
||||
'setPRFileViewed',
|
||||
'starOrca',
|
||||
'updateIssue',
|
||||
'updateIssueBySlug',
|
||||
'updateIssueCommentBySlug',
|
||||
'updateIssueTypeBySlug',
|
||||
'updatePRState',
|
||||
'updatePRTitle',
|
||||
'updateProjectItemField',
|
||||
'updatePullRequestBySlug',
|
||||
'validateAccountBinding',
|
||||
'viewer',
|
||||
'workItem',
|
||||
'workItemByOwnerRepo',
|
||||
'workItemDetails'
|
||||
].sort()
|
||||
)
|
||||
})
|
||||
|
||||
it('routes every runtime-backed GitHub method through the expected RPC method', async () => {
|
||||
type GitHubApi = NonNullable<PreloadApi['gh']>
|
||||
const runtimeCalls: { method: string; params: unknown }[] = []
|
||||
|
||||
@@ -15,24 +15,6 @@ describe('web GitLab preload API', () => {
|
||||
afterEach(() => {
|
||||
vi.unstubAllGlobals()
|
||||
vi.doUnmock('./web-runtime-client')
|
||||
vi.doUnmock('electron')
|
||||
})
|
||||
|
||||
it('keeps the web GitLab preload key set in parity with desktop preload', async () => {
|
||||
vi.doMock('electron', () => ({
|
||||
ipcRenderer: { invoke: vi.fn() }
|
||||
}))
|
||||
const globals = installBrowserGlobals('Linux')
|
||||
const { glApi } = (await import(
|
||||
new URL('../../../preload/gitlab.ts', import.meta.url).href
|
||||
)) as {
|
||||
glApi: Record<string, unknown>
|
||||
}
|
||||
const { installWebPreloadApi } = await import('./web-preload-api')
|
||||
|
||||
installWebPreloadApi()
|
||||
|
||||
expect(Object.keys(globals.window.api.gl).sort()).toEqual(Object.keys(glApi).sort())
|
||||
})
|
||||
|
||||
it('routes every runtime-backed GitLab method through the expected RPC method', async () => {
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
import { afterEach, describe, expect, it, vi } from 'vitest'
|
||||
import {
|
||||
createCommandCodeOutputStatusDetector,
|
||||
stripTerminalControl
|
||||
} from './command-code-output-status'
|
||||
import { createCommandCodeOutputStatusDetector } from './command-code-output-status'
|
||||
import { stripTerminalControl } from './terminal-control-stripping'
|
||||
|
||||
afterEach(() => {
|
||||
vi.restoreAllMocks()
|
||||
@@ -389,44 +387,6 @@ function maxStringContextLength(contexts: unknown[]): number {
|
||||
}
|
||||
|
||||
describe('terminal control stripping', () => {
|
||||
const esc = String.fromCharCode(0x1b)
|
||||
const bel = String.fromCharCode(0x07)
|
||||
const ansiEscape = new RegExp(
|
||||
`${esc}(?:[@-Z\\\\-_]|\\[[0-?]*[ -/]*[@-~]|\\][^${bel}]*(?:${bel}|${esc}\\\\))`,
|
||||
'g'
|
||||
)
|
||||
const incompleteAnsiEscape = new RegExp(
|
||||
`${esc}(?:\\[[0-?]*[ -/]*|\\][^${bel}${esc}]*|\\S?)?$`,
|
||||
'g'
|
||||
)
|
||||
|
||||
function legacyStripTerminalControl(data: string): string {
|
||||
const withoutAnsi = data.replace(ansiEscape, '').replace(incompleteAnsiEscape, '')
|
||||
let output = ''
|
||||
for (let index = 0; index < withoutAnsi.length; index += 1) {
|
||||
const code = withoutAnsi.charCodeAt(index)
|
||||
if ((code <= 0x1f && code !== 0x0a && code !== 0x0d) || (code >= 0x7f && code <= 0x9f)) {
|
||||
continue
|
||||
}
|
||||
output += withoutAnsi[index]
|
||||
}
|
||||
return output
|
||||
}
|
||||
|
||||
function makeRandom(seed: number): () => number {
|
||||
let state = seed >>> 0
|
||||
return () => {
|
||||
state ^= state << 13
|
||||
state ^= state >>> 17
|
||||
state ^= state << 5
|
||||
return (state >>> 0) / 0x1_0000_0000
|
||||
}
|
||||
}
|
||||
|
||||
function expectLegacyEquivalent(data: string): void {
|
||||
expect(stripTerminalControl(data)).toBe(legacyStripTerminalControl(data))
|
||||
}
|
||||
|
||||
function promptFrom(raw: string): string | null {
|
||||
let captured: string | null = null
|
||||
const detector = createCommandCodeOutputStatusDetector({
|
||||
@@ -458,77 +418,26 @@ describe('terminal control stripping', () => {
|
||||
)
|
||||
})
|
||||
|
||||
it('matches the legacy filter across ANSI, C0/C1, line breaks, and Unicode', () => {
|
||||
for (const data of [
|
||||
'',
|
||||
'plain text',
|
||||
'\x1b[35mstyled\x1b[0m',
|
||||
'\x1b[unterminated',
|
||||
'\x00leading',
|
||||
'trailing\x9f',
|
||||
'\x01\x02adjacent\x7f\x80',
|
||||
'keep\r\nline breaks',
|
||||
'日本語 \u{1f389} \ud83d \ude00',
|
||||
'\x1b]0;title\x07prompt',
|
||||
'\x1b]0;title\x1b\\prompt'
|
||||
]) {
|
||||
expectLegacyEquivalent(data)
|
||||
}
|
||||
})
|
||||
|
||||
it('matches legacy output at density thresholds and block resets', () => {
|
||||
const control = '\x01'
|
||||
const fixtures = [
|
||||
`${control.repeat(31)}${'a'.repeat(33)}`,
|
||||
`${control.repeat(32)}${'a'.repeat(32)}`,
|
||||
`${'a'.repeat(32)}${control.repeat(31)}a`,
|
||||
`${control.repeat(31)}${'a'.repeat(33)}${control.repeat(31)}z`,
|
||||
`${control.repeat(31)}${'a'.repeat(33)}${control.repeat(32)}z`,
|
||||
`${'a'.repeat(64 * 3)}${control.repeat(32)}tail`,
|
||||
`${'a\x01'.repeat(2048)}tail`
|
||||
]
|
||||
for (const data of fixtures) {
|
||||
expectLegacyEquivalent(data)
|
||||
}
|
||||
})
|
||||
|
||||
it('exhaustively matches short strings over control and Unicode code units', () => {
|
||||
const alphabet = ['a', '\r', '\n', '\x01', '\x7f', '\x80', '\u20ac', '\ud83d']
|
||||
for (let encoded = 0; encoded < alphabet.length ** 4; encoded += 1) {
|
||||
let cursor = encoded
|
||||
let data = ''
|
||||
for (let position = 0; position < 4; position += 1) {
|
||||
data += alphabet[cursor % alphabet.length]
|
||||
cursor = Math.floor(cursor / alphabet.length)
|
||||
}
|
||||
expectLegacyEquivalent(data)
|
||||
}
|
||||
})
|
||||
|
||||
it('matches seeded random terminal text', () => {
|
||||
const random = makeRandom(0xc0de_0727)
|
||||
const alphabet = [
|
||||
'a',
|
||||
'Z',
|
||||
'\r',
|
||||
'\n',
|
||||
'\x00',
|
||||
'\x1f',
|
||||
'\x7f',
|
||||
'\x9f',
|
||||
'\u00e9',
|
||||
'\u20ac',
|
||||
'\u{1f389}',
|
||||
'\x1b[35m',
|
||||
'\x1b[0m'
|
||||
]
|
||||
for (let trial = 0; trial < 2_000; trial += 1) {
|
||||
let data = ''
|
||||
const parts = Math.floor(random() * 256)
|
||||
for (let index = 0; index < parts; index += 1) {
|
||||
data += alphabet[Math.floor(random() * alphabet.length)]
|
||||
}
|
||||
expectLegacyEquivalent(data)
|
||||
}
|
||||
it.each([
|
||||
{ data: '', expected: '' },
|
||||
{ data: 'plain text', expected: 'plain text' },
|
||||
{ data: '\x1b[35mstyled\x1b[0m', expected: 'styled' },
|
||||
{ data: 'prompt\x1b[35', expected: 'prompt' },
|
||||
{ data: '\x00leading', expected: 'leading' },
|
||||
{ data: 'trailing\x9f', expected: 'trailing' },
|
||||
{ data: '\x01\x02adjacent\x7f\x80', expected: 'adjacent' },
|
||||
{ data: 'keep\r\nline breaks', expected: 'keep\r\nline breaks' },
|
||||
{ data: '日本語 🎉 \ud83d \ude00', expected: '日本語 🎉 \ud83d \ude00' },
|
||||
{ data: '\x01'.repeat(31) + 'a'.repeat(33), expected: 'a'.repeat(33) },
|
||||
{ data: '\x01'.repeat(32) + 'a'.repeat(32), expected: 'a'.repeat(32) },
|
||||
{ data: `${'a'.repeat(32) + '\x01'.repeat(31)}a`, expected: 'a'.repeat(33) },
|
||||
{
|
||||
data: `${'\x01'.repeat(31) + 'a'.repeat(33) + '\x01'.repeat(32)}z`,
|
||||
expected: `${'a'.repeat(33)}z`
|
||||
},
|
||||
{ data: `${'a'.repeat(192) + '\x01'.repeat(32)}tail`, expected: `${'a'.repeat(192)}tail` },
|
||||
{ data: `${'a\x01'.repeat(2048)}tail`, expected: `${'a'.repeat(2048)}tail` }
|
||||
])('strips controls while preserving printable text (case %#)', ({ data, expected }) => {
|
||||
expect(stripTerminalControl(data)).toBe(expected)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -13,8 +13,6 @@ import { stripTerminalControl } from './terminal-control-stripping'
|
||||
import { escapeRegex } from './string-utils'
|
||||
import { ownRetainedString } from './own-retained-string'
|
||||
|
||||
export { stripTerminalControl } from './terminal-control-stripping'
|
||||
|
||||
type CommandCodeOutputStatusDetector = {
|
||||
observe: (data: string) => boolean
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user