Files
orca/tests/e2e/spinner-workspace-fixture.ts
aeddfa463d perf(renderer): avoid per-second spinner animation events (#19407)
* perf(renderer): avoid per-second spinner animation events

* fix(bench): ensure the Electron runtime before bench:spinners

The script launches Electron via Playwright but skipped ensure:electron-runtime,
which every other Electron-launching bench script runs first.

* docs(renderer): scope spinner pixel-tolerance claim to paused-animation checks

---------

Co-authored-by: m4air <m4air@m4airs-MacBook-Air.local>
Co-authored-by: pullfrog[bot] <226033991+pullfrog[bot]@users.noreply.github.com>
2026-09-07 19:29:53 -07:00

135 lines
4.4 KiB
TypeScript

import type { Page } from '@stablyai/playwright-test'
import { mkdirSync, mkdtempSync, writeFileSync } from 'node:fs'
import path from 'node:path'
import { runProcess } from '../../src/shared/child-process/run-process'
import { attachRepoAndOpenTerminal } from './helpers/orca-restart'
import { configureRendererScaleFixture } from '../../config/scripts/idle-cpu-renderer-scale-fixture.mjs'
export async function createSpinnerRepository(worktrees: number) {
const parent = path.resolve('.bench-fixtures')
mkdirSync(parent, { recursive: true })
const directory = mkdtempSync(path.join(parent, 'spinner-workspaces-'))
const repoPath = path.join(directory, 'primary')
mkdirSync(repoPath)
const git = async (args: string[]) => {
const result = await runProcess({ program: 'git', args, cwd: repoPath })
if (result.code !== 0) {
throw new Error(result.stderr)
}
}
await git(['init'])
await git(['config', 'user.email', 'spinner-benchmark@test.local'])
await git(['config', 'user.name', 'Spinner benchmark'])
await git(['config', 'commit.gpgsign', 'false'])
writeFileSync(path.join(repoPath, 'README.md'), '# Spinner benchmark\n')
await git(['add', 'README.md'])
await git(['commit', '-m', 'Spinner fixture'])
for (let index = 1; index < worktrees; index++) {
await git([
'worktree',
'add',
'-b',
`spinner-${index}`,
path.join(directory, `workspace-${index}`)
])
}
return { directory, repoPath }
}
export async function seedSpinnerWorkspaces(
page: Page,
repoPath: string,
options: {
worktrees: number
lineageDepth: number
agentsPerWorktree: number
subagentsPerAgent: number
}
) {
await attachRepoAndOpenTerminal(page, repoPath)
await page.evaluate(async () => {
const store = window.__store!
const repo = store.getState().repos[0]
await store.getState().fetchWorktrees(repo.id, { requireAuthoritative: true })
const paths = (store.getState().detectedWorktreesByRepo[repo.id]?.worktrees ?? [])
.filter((worktree) => !worktree.selectedCheckout)
.map((worktree) => worktree.path)
await store.getState().updateRepo(repo.id, {
externalWorktreeVisibility: 'show',
importedExternalWorktreePaths: paths,
externalWorktreeInboxBaselinePaths: paths
})
await store.getState().fetchWorktrees(repo.id, { requireAuthoritative: true })
})
await page.waitForFunction(
(count) => Object.values(window.__store!.getState().worktreesByRepo).flat().length === count,
options.worktrees
)
return configureRendererScaleFixture(page, options, repoPath)
}
export async function refreshSpinnerAgents(page: Page) {
return page.evaluate(() => {
const store = window.__store!
const agents = Object.values(store.getState().agentStatusByPaneKey).filter((entry) =>
entry.prompt?.startsWith('Idle CPU agent ')
)
for (const entry of agents) {
store.getState().setAgentStatus(
entry.paneKey,
{
state: 'working',
prompt: entry.prompt,
agentType: entry.agentType,
subagents: entry.subagents
},
entry.agentType,
{ updatedAt: Date.now(), stateStartedAt: entry.stateStartedAt },
{
tabId: entry.tabId,
worktreeId: entry.worktreeId
}
)
}
return agents.length
})
}
export async function startSpinnerStatusTraffic(page: Page) {
return page.evaluateHandle(() => {
const store = window.__store!
const keys = Object.values(store.getState().agentStatusByPaneKey)
.filter((entry) => entry.prompt?.startsWith('Idle CPU agent '))
.map((entry) => entry.paneKey)
let cursor = 0
let updates = 0
const timer = setInterval(() => {
for (let index = 0; index < Math.min(8, keys.length); index++) {
const entry = store.getState().agentStatusByPaneKey[keys[cursor++ % keys.length]]
store.getState().setAgentStatus(
entry.paneKey,
{
state: 'working',
prompt: entry.prompt,
agentType: entry.agentType,
subagents: entry.subagents
},
entry.agentType,
{ updatedAt: Date.now(), stateStartedAt: entry.stateStartedAt },
{
tabId: entry.tabId,
worktreeId: entry.worktreeId
}
)
updates++
}
}, 200)
return {
stop() {
clearInterval(timer)
return updates
}
}
})
}