fix: remove scoped bash

This commit is contained in:
07akioni
2026-07-31 10:48:50 +08:00
committed by kingwl
parent 5c4b701afe
commit 575e1217bb
20 changed files with 38 additions and 93 deletions

View File

@@ -149,7 +149,7 @@ describe('terminal card assembly', () => {
const view = runtime.renderRoot()
// Keyed BashRow: collapsed by default, the whole summary row is the toggle.
const keyedRow = view.container.querySelector('[data-sample="bash-global"]')
const keyedRow = view.container.querySelector('[data-sample="bash"]')
const keyed = keyedRow?.parentElement
expect(keyed?.querySelector('[data-terminal]')).toBeNull()
fireEvent.click(keyedRow!)

View File

@@ -181,7 +181,7 @@ describe('run_code sub-calls through the real chat machinery', () => {
// sub-tool fell back to GenericToolCard at the same render site.
const nest = view.container.querySelector('[data-subcalls]')
expect(nest).not.toBeNull()
expect(nest!.querySelector('[data-sample="bash-global"]')).not.toBeNull()
expect(nest!.querySelector('[data-sample="bash"]')).not.toBeNull()
expect(view.getByText('Bash')).toBeTruthy()
expect(view.getByText('List notes')).toBeTruthy()
expect(view.getByText('Tool call')).toBeTruthy()
@@ -264,7 +264,7 @@ describe('run_code sub-calls through the real chat machinery', () => {
expect(running).not.toBeNull()
const nest = view.container.querySelector('[data-subcalls]')
expect(nest).not.toBeNull()
expect(nest!.querySelector('[data-sample="bash-global"]')).not.toBeNull()
expect(nest!.querySelector('[data-sample="bash"]')).not.toBeNull()
})
it('a started-but-unsettled sub-call renders the running state exactly like a native in-flight row', async () => {

View File

@@ -1,8 +1,7 @@
// @vitest-environment jsdom
// StatsLine (composer.dock entry): totals derivation + the RFC
// hard acceptance — zero renders during streaming. Bash sample row: the
// canonical sub-agent differential decided INSIDE the component off the
// standard useSessions kit (no registry predicates — tool ring dissolved).
// hard acceptance — zero renders during streaming. Bash sample row: ToolRow
// chrome (Bash · description) without a row click target.
import { afterEach, describe, expect, it, vi } from 'vitest'
import { act, cleanup, render } from '@testing-library/react'
@@ -232,8 +231,7 @@ describe('StatsLine', () => {
})
describe('bash sample row', () => {
const ROOT = 'root-1' as SessionId
const CHILD = 'child-1' as SessionId
const SID = 'root-1' as SessionId
const result = (callId: string): ToolResultNode => ({
kind: 'tool-result', seq: 3, time: 3_000, callId,
@@ -242,68 +240,30 @@ describe('bash sample row', () => {
content: [], isError: false, callView: null, resultView: null,
})
/** Real list-store engine: the family fixture the in-component parentId branch reads. */
function listStore() {
return createSnapshotStore<SessionListState>({
ids: [ROOT, CHILD],
ids: [SID],
byId: {
[ROOT]: { id: ROOT, title: 'r', displayTitle: 'r', running: false, waitingApproval: false, blank: false, updatedAt: 0 },
[CHILD]: { id: CHILD, title: 'c', displayTitle: 'c', parentId: ROOT, running: false, waitingApproval: false, blank: false, updatedAt: 0 },
[SID]: { id: SID, title: 'r', displayTitle: 'r', running: false, waitingApproval: false, blank: false, updatedAt: 0 },
},
current: undefined,
phase: 'ready',
})
}
const rowProps = (sessionId: SessionId, over?: {
store?: ReturnType<typeof listStore>
}): BashRowProps => ({
const rowProps = (): BashRowProps => ({
callId: 'c1', toolName: 'bash', block: result('c1'),
openFile: vi.fn(),
sessionId,
useSessions: bindSnapshotSelector(over?.store ?? listStore()),
sessionId: SID,
useSessions: bindSnapshotSelector(listStore()),
t,
} as unknown as BashRowProps)
it('differential rendering: the scoped variant in sub-sessions, global at roots', () => {
const scoped = render(<BashRow {...rowProps(CHILD)} />)
expect(scoped.container.querySelector('[data-sample="bash-scoped"]')).not.toBeNull()
expect(scoped.getByText('scoped')).toBeTruthy()
const plain = render(<BashRow {...rowProps(ROOT)} />)
expect(plain.container.querySelector('[data-sample="bash-global"]')).not.toBeNull()
})
it('a session outside the list renders the global arm (no parent known)', () => {
const view = render(<BashRow {...rowProps('gone' as SessionId)} />)
expect(view.container.querySelector('[data-sample="bash-global"]')).not.toBeNull()
})
it('a live parentId write flips the row to the scoped variant (store subscription)', () => {
const store = listStore()
const orphan = 'late-child' as SessionId
store.update((d) => {
d.ids.push(orphan)
d.byId[orphan] = { id: orphan, title: 'l', displayTitle: 'l', running: false, waitingApproval: false, blank: false, updatedAt: 0 }
})
const view = render(<BashRow {...rowProps(orphan, { store })} />)
expect(view.container.querySelector('[data-sample="bash-global"]')).not.toBeNull()
act(() => {
store.update((d) => { d.byId[orphan]!.parentId = ROOT })
})
expect(view.container.querySelector('[data-sample="bash-scoped"]')).not.toBeNull()
})
it('summarizes as Bash · description on both arms without row click targets', () => {
const global = render(<BashRow {...rowProps(ROOT)} />)
// Two renders share document.body: query inside each container.
const globalRow = global.container.querySelector('[data-sample="bash-global"]')!
expect(globalRow.textContent).toContain('Bash')
expect(globalRow.textContent).toContain('Build')
expect(globalRow.getAttribute('data-clickable')).toBeNull()
const scoped = render(<BashRow {...rowProps(CHILD)} />)
const scopedRow = scoped.container.querySelector('[data-sample="bash-scoped"]')!
expect(scopedRow.textContent).toContain('Bash')
expect(scopedRow.textContent).toContain('Build')
expect(scopedRow.getAttribute('data-clickable')).toBeNull()
it('summarizes as Bash · description without a row click target', () => {
const view = render(<BashRow {...rowProps()} />)
const row = view.container.querySelector('[data-sample="bash"]')!
expect(row.textContent).toContain('Bash')
expect(row.textContent).toContain('Build')
expect(row.getAttribute('data-clickable')).toBeNull()
})
})

View File

@@ -91,7 +91,7 @@ describe('keyed toolview hole through the real machinery', () => {
const view = b.runtime.renderRoot()
// bash: the sample plugin's keyed registration took the row (root
// session → global arm, decided inside the component off useSessions).
expect(view.container.querySelector('[data-sample="bash-global"]')).not.toBeNull()
expect(view.container.querySelector('[data-sample="bash"]')).not.toBeNull()
expect(view.getByText('Bash')).toBeTruthy()
expect(view.getByText('Build')).toBeTruthy()
// mystery: no registration under that key → render-site fallback.

View File

@@ -89,7 +89,7 @@ describe('tails', () => {
expect(view.container.querySelector('[data-state="ok"]')).not.toBeNull()
})
it('BashRow carries data-state for running (row sweep) and StateDots for error/stopped (root session arm)', () => {
it('BashRow carries data-state for running (row sweep) and StateDots for error/stopped', () => {
const sid = 'root-1' as SessionId
const list = createSnapshotStore<SessionListState>({
ids: [sid],
@@ -125,7 +125,7 @@ describe('tails', () => {
runningView.unmount()
const errorView = render(<BashRow {...props(errorResult)} />)
expect(errorView.container.querySelector('[data-sample="bash-global"]')).not.toBeNull()
expect(errorView.container.querySelector('[data-sample="bash"]')).not.toBeNull()
expect(errorView.container.querySelector('[data-state="error"]')).not.toBeNull()
expect(errorView.getByText('失败')).toBeTruthy()
errorView.unmount()