Import AITURK IDE 1.0.0-beta.1 from Hermes 63279301; preserve MIT license
This commit is contained in:
commit
03634b1ca3
11340 files changed
+3442369
No files matched your search
@@ -0,0 +1,177 @@
|
||||
/**
|
||||
* The bot row's two side effects: pre-warming and opening.
|
||||
*
|
||||
* Pre-warm is per-row and hover-scoped. Warming the whole roster on paint
|
||||
* spun up every profile backend the moment the Bots rail rendered, so the row
|
||||
* warms exactly one bot and only once a pointer is actually over it — and a
|
||||
* source-scoped row pre-dials its OWN source rather than the active gateway.
|
||||
*
|
||||
* Opening is delegated whole: the row hands its exact roster row to
|
||||
* openRosterBot and does nothing else. It never activates a connection
|
||||
* itself, which is what keeps a remote row from resolving into the same-named
|
||||
* local bot.
|
||||
*
|
||||
* Ported from tests/profile-prewarm.test.mjs, which sliced BotRow out of the
|
||||
* old plugin.js bundle and rendered it against a hand-built jsx stub.
|
||||
*/
|
||||
|
||||
import type * as HermesSdk from '@hermes/plugin-sdk'
|
||||
import { fireEvent, render, screen } from '@testing-library/react'
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
|
||||
import { BotRow } from './bot-row'
|
||||
import { translateBots } from './i18n-test-helper'
|
||||
import type { RosterRow } from './types'
|
||||
|
||||
const { ensureAgent, ensureBotMetadata, notifyError, openRosterBot, requestProfile, warmAgent, warmProfile } =
|
||||
vi.hoisted(() => ({
|
||||
ensureAgent: vi.fn(),
|
||||
ensureBotMetadata: vi.fn(),
|
||||
notifyError: vi.fn(),
|
||||
openRosterBot: vi.fn(),
|
||||
requestProfile: vi.fn(),
|
||||
warmAgent: vi.fn(),
|
||||
warmProfile: vi.fn()
|
||||
}))
|
||||
|
||||
vi.mock('@hermes/plugin-sdk', async importOriginal => {
|
||||
const sdk = await importOriginal<typeof HermesSdk>()
|
||||
|
||||
return {
|
||||
...sdk,
|
||||
host: { ...sdk.host, ensureAgent, notifyError, requestProfile, warmAgent, warmProfile },
|
||||
// The plugin bundle normally lands via `ctx.i18n.register` at load, so
|
||||
// without this every localized label in the row renders empty.
|
||||
usePluginI18n: () => translateBots
|
||||
}
|
||||
})
|
||||
|
||||
vi.mock('./canonical-chat', () => ({
|
||||
ensureBotMetadata,
|
||||
notifyBotOpenFailure: vi.fn(),
|
||||
openBotCanonicalChat: vi.fn(),
|
||||
prepareBotSource: vi.fn(),
|
||||
PROFILE_SESSION_LIST_LIMIT: 200
|
||||
}))
|
||||
|
||||
vi.mock('./roster-actions', () => ({ openRosterBot }))
|
||||
|
||||
const noop = () => undefined
|
||||
|
||||
function renderRow(bot: RosterRow) {
|
||||
render(<BotRow bot={bot} onDelete={noop} onEdit={noop} onGroup={noop} onNewSection={noop} />)
|
||||
|
||||
return screen.getByRole('button')
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks()
|
||||
ensureBotMetadata.mockResolvedValue({ pinned: true })
|
||||
openRosterBot.mockResolvedValue(true)
|
||||
requestProfile.mockResolvedValue({})
|
||||
})
|
||||
|
||||
describe('pre-warm is hover-scoped, never roster-wide', () => {
|
||||
it('warms nothing on paint and exactly the hovered bot on pointer entry', async () => {
|
||||
const row = renderRow({ name: 'alpha' } as RosterRow)
|
||||
|
||||
expect(warmProfile).not.toHaveBeenCalled()
|
||||
|
||||
fireEvent.pointerEnter(row)
|
||||
|
||||
expect(warmProfile.mock.calls).toEqual([['alpha']])
|
||||
expect(warmAgent).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('pre-dials a source-scoped row on its own source', async () => {
|
||||
const row = renderRow({
|
||||
connectionId: 'work',
|
||||
connectionLabel: 'Work',
|
||||
name: 'research',
|
||||
remoteSource: true,
|
||||
sourceScoped: true
|
||||
} as RosterRow)
|
||||
|
||||
fireEvent.pointerEnter(row)
|
||||
|
||||
expect(warmAgent.mock.calls).toEqual([['work', 'research']])
|
||||
expect(warmProfile).not.toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
|
||||
describe('the row delegates the open and claims no activation authority', () => {
|
||||
it('hands a remote Connections row to openRosterBot without activating it', async () => {
|
||||
const bot = {
|
||||
connectionId: 'work',
|
||||
connectionLabel: 'Work',
|
||||
name: 'research',
|
||||
remoteSource: true,
|
||||
sourceScoped: true
|
||||
} as RosterRow
|
||||
|
||||
fireEvent.click(renderRow(bot))
|
||||
|
||||
expect(ensureAgent).not.toHaveBeenCalled()
|
||||
expect(openRosterBot.mock.calls).toEqual([[bot]])
|
||||
})
|
||||
|
||||
it('never resolves a remote default into the same-named local bot', async () => {
|
||||
const bot = {
|
||||
connectionId: 'mac-mini',
|
||||
connectionLabel: 'Mac Mini',
|
||||
name: 'default',
|
||||
remoteSource: true,
|
||||
sourceScoped: true
|
||||
} as RosterRow
|
||||
|
||||
fireEvent.click(renderRow(bot))
|
||||
|
||||
expect(ensureAgent).not.toHaveBeenCalled()
|
||||
expect(openRosterBot.mock.calls[0][0].connectionId).toBe('mac-mini')
|
||||
expect(notifyError).not.toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
|
||||
describe('the menu opens the same forever-chat a row click does', () => {
|
||||
it('opens the canonical chat', async () => {
|
||||
const bot = { name: 'alpha' } as RosterRow
|
||||
|
||||
fireEvent.contextMenu(renderRow(bot))
|
||||
fireEvent.click(await screen.findByText('Open Bot Chat'))
|
||||
|
||||
expect(openRosterBot.mock.calls).toEqual([[bot]])
|
||||
})
|
||||
})
|
||||
|
||||
describe('context-menu mutations hydrate the alias first', () => {
|
||||
it('reads the backend row before toggling pin, and writes to the alias target', async () => {
|
||||
// A non-identity alias (Desktop calls it `worker`, the backend calls it
|
||||
// `backend-worker`) must have its CURRENT state hydrated from its own
|
||||
// source before the toggle — flipping a locally-assumed value would
|
||||
// fight whatever the backend actually holds.
|
||||
const bot = {
|
||||
connectionId: 'remote-a',
|
||||
name: 'worker',
|
||||
remoteSource: true,
|
||||
route: { connectionId: 'remote-a', mode: 'remote', profile: 'worker', targetProfile: 'backend-worker' },
|
||||
sourceScoped: true
|
||||
} as RosterRow
|
||||
|
||||
fireEvent.contextMenu(renderRow(bot))
|
||||
// The label reads from LOCAL meta (unpinned here); the toggle reads from
|
||||
// the hydrated backend row, which says pinned. That divergence is the
|
||||
// point — an alias whose state lives elsewhere must not be flipped
|
||||
// against a locally-assumed value.
|
||||
fireEvent.click(await screen.findByText('Pin to top'))
|
||||
await vi.waitFor(() =>
|
||||
expect(requestProfile.mock.calls.some(([, method]) => method === 'profiles.configure')).toBe(true)
|
||||
)
|
||||
|
||||
expect(ensureBotMetadata).toHaveBeenCalledWith(bot)
|
||||
|
||||
const [route, , params] = requestProfile.mock.calls.find(([, method]) => method === 'profiles.configure')!
|
||||
|
||||
expect(route.profile).toBe('worker')
|
||||
expect(params).toMatchObject({ name: 'backend-worker', ui_meta: { 'hermes-bots': { pinned: false } } })
|
||||
})
|
||||
})
|
||||
Reference in new issue
Block a user