feat(web): localize shipped agent presets
This commit is contained in:
@@ -90,7 +90,7 @@ describe('the General-settings row', () => {
|
||||
const actions = renderRow()
|
||||
|
||||
await waitFor(() => { expect(actions.load).toHaveBeenCalledTimes(1) })
|
||||
expect(screen.getByRole('button').textContent).toContain('标准模式')
|
||||
expect(screen.getByRole('button').textContent).toContain(en.presetStandardName)
|
||||
})
|
||||
|
||||
it('marks a locally authored option as local', () => {
|
||||
@@ -102,7 +102,7 @@ describe('the General-settings row', () => {
|
||||
// list says which rows are local rather than presenting all as vetted.
|
||||
expect(screen.getByText(`mine · ${en.userTrust}`)).toBeTruthy()
|
||||
// The shipped one carries no marker; only local rows are called out.
|
||||
expect(screen.getAllByText('标准模式')).toHaveLength(2)
|
||||
expect(screen.getAllByText(en.presetStandardName)).toHaveLength(2)
|
||||
})
|
||||
|
||||
it('falls back to the id for a preset that published no name', () => {
|
||||
@@ -128,6 +128,12 @@ describe('the General-settings row', () => {
|
||||
expect(screen.getByText('bare')).toBeTruthy()
|
||||
})
|
||||
|
||||
it('shows the selected id until a stale roster contains it', () => {
|
||||
renderRow({ currentValue: 'arriving', options: [] })
|
||||
|
||||
expect(screen.getByRole('button').textContent).toContain('arriving')
|
||||
})
|
||||
|
||||
it('writes the picked preset and closes the menu', () => {
|
||||
const actions = renderRow()
|
||||
fireEvent.click(screen.getByRole('button'))
|
||||
@@ -194,7 +200,7 @@ describe('the new-session chip', () => {
|
||||
const actions = renderSeat()
|
||||
|
||||
await waitFor(() => { expect(actions.load).toHaveBeenCalledTimes(1) })
|
||||
expect(screen.getByRole('button').textContent).toContain('标准模式')
|
||||
expect(screen.getByRole('button').textContent).toContain(en.presetStandardName)
|
||||
expect(screen.getByRole('button').getAttribute('title')).toBe(en.seatHint)
|
||||
})
|
||||
|
||||
@@ -205,7 +211,7 @@ describe('the new-session chip', () => {
|
||||
|
||||
// The id alone never said what a preset does; the description is the
|
||||
// whole reason a preset can publish metadata at all.
|
||||
expect(screen.getByText('完整的编码 agent。')).toBeTruthy()
|
||||
expect(screen.getByText(en.presetStandardDescription)).toBeTruthy()
|
||||
// A preset that published none still reads as a row, with its id standing
|
||||
// in for the name.
|
||||
expect(screen.getByText(en.noDescription)).toBeTruthy()
|
||||
@@ -218,6 +224,12 @@ describe('the new-session chip', () => {
|
||||
expect(screen.getByRole('button').textContent).toContain('mine')
|
||||
})
|
||||
|
||||
it('shows the staged id until a stale roster contains it', () => {
|
||||
renderSeat({ current: 'arriving' })
|
||||
|
||||
expect(screen.getByRole('button').textContent).toContain('arriving')
|
||||
})
|
||||
|
||||
it('stages the picked preset and closes the menu', () => {
|
||||
const actions = renderSeat()
|
||||
fireEvent.click(screen.getByRole('button'))
|
||||
@@ -267,7 +279,7 @@ describe('the session-header label', () => {
|
||||
await waitFor(() => { expect(load).toHaveBeenCalledTimes(1) })
|
||||
// A control here would promise a switch the host refuses outright.
|
||||
expect(screen.queryByRole('button')).toBeNull()
|
||||
expect(screen.getByTitle('完整的编码 agent。').textContent).toBe('标准模式')
|
||||
expect(screen.getByTitle(en.presetStandardDescription).textContent).toBe(en.presetStandardName)
|
||||
})
|
||||
|
||||
it('falls back to the id, and to the generic hint, when metadata is absent', () => {
|
||||
|
||||
33
packages/client/ui-agent-preset/tests/locales.spec.ts
Normal file
33
packages/client/ui-agent-preset/tests/locales.spec.ts
Normal file
@@ -0,0 +1,33 @@
|
||||
/** Web-localized copy for the four shipped presets and file copy for every other row. */
|
||||
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { en, presetDisplayText, zh } from '../src/client/locales.ts'
|
||||
|
||||
const translate = (bundle: typeof en) => (key: keyof typeof en): string => bundle[key]
|
||||
|
||||
describe('preset display copy', () => {
|
||||
it.each([
|
||||
['standard', 'presetStandardName', 'presetStandardDescription'],
|
||||
['code', 'presetCodeName', 'presetCodeDescription'],
|
||||
['minimal', 'presetMinimalName', 'presetMinimalDescription'],
|
||||
['cordis', 'presetCordisName', 'presetCordisDescription'],
|
||||
] as const)('localizes the shipped %s preset in English and Chinese', (id, nameKey, descriptionKey) => {
|
||||
const preset = { id, trust: 'system' as const, name: 'file name', description: 'file description' }
|
||||
|
||||
expect(presetDisplayText(preset, translate(en)))
|
||||
.toEqual({ name: en[nameKey], description: en[descriptionKey] })
|
||||
expect(presetDisplayText(preset, translate(zh)))
|
||||
.toEqual({ name: zh[nameKey], description: zh[descriptionKey] })
|
||||
})
|
||||
|
||||
it('keeps file metadata for user and unknown system presets', () => {
|
||||
const fileCopy = { name: '我的标准', description: '团队自己的 preset。' }
|
||||
|
||||
expect(presetDisplayText({ id: 'standard', trust: 'user', ...fileCopy }, translate(en)))
|
||||
.toEqual(fileCopy)
|
||||
expect(presetDisplayText({ id: 'deployment-extra', trust: 'system', ...fileCopy }, translate(en)))
|
||||
.toEqual(fileCopy)
|
||||
expect(presetDisplayText({ id: 'bare', trust: 'user' }, translate(en)))
|
||||
.toEqual({ name: 'bare' })
|
||||
})
|
||||
})
|
||||
@@ -85,13 +85,13 @@ describe('the preset list', () => {
|
||||
await waitFor(() => { expect(actions.load).toHaveBeenCalledTimes(1) })
|
||||
})
|
||||
|
||||
it('shows the published name and description, falling back to the id', () => {
|
||||
it('shows resolved copy for built-ins and falls back to custom ids', () => {
|
||||
renderSection()
|
||||
|
||||
// The name is what a picker reads; the id stays visible as the key the
|
||||
// Display copy is what a picker reads; the id stays visible as the key the
|
||||
// composition and the session header actually carry.
|
||||
expect(screen.getByText('标准模式')).toBeTruthy()
|
||||
expect(screen.getByText('完整的编码 agent。')).toBeTruthy()
|
||||
expect(screen.getByText(en.presetStandardName)).toBeTruthy()
|
||||
expect(screen.getByText(en.presetStandardDescription)).toBeTruthy()
|
||||
const mine = rowFor('mine')
|
||||
expect(within(mine).getAllByText('mine').length).toBeGreaterThan(0)
|
||||
expect(within(mine).getByText(en.noDescription)).toBeTruthy()
|
||||
@@ -134,7 +134,7 @@ describe('the preset list', () => {
|
||||
it('picks a preset by clicking its card, and the one in use is inert', () => {
|
||||
const actions = renderSection()
|
||||
|
||||
const inUse = within(rowFor('standard')).getByRole('button', { name: `${en.inUse}: 标准模式` })
|
||||
const inUse = within(rowFor('standard')).getByRole('button', { name: `${en.inUse}: ${en.presetStandardName}` })
|
||||
expect(inUse).toHaveProperty('disabled', true)
|
||||
fireEvent.click(inUse)
|
||||
|
||||
@@ -150,8 +150,8 @@ describe('the preset list', () => {
|
||||
// the point. A custom preset is edited in its files, so its row leads
|
||||
// there instead; there is no editor for either.
|
||||
const standard = rowFor('standard')
|
||||
expect(within(standard).getByRole('button', { name: `${en.view}: 标准模式` })).toBeTruthy()
|
||||
expect(within(standard).queryByRole('button', { name: `${en.openLocation}: 标准模式` })).toBeNull()
|
||||
expect(within(standard).getByRole('button', { name: `${en.view}: ${en.presetStandardName}` })).toBeTruthy()
|
||||
expect(within(standard).queryByRole('button', { name: `${en.openLocation}: ${en.presetStandardName}` })).toBeNull()
|
||||
const mine = rowFor('mine')
|
||||
expect(within(mine).getByRole('button', { name: `${en.openLocation}: mine` })).toBeTruthy()
|
||||
expect(within(mine).queryByRole('button', { name: `${en.view}: mine` })).toBeNull()
|
||||
@@ -161,13 +161,13 @@ describe('the preset list', () => {
|
||||
renderSection()
|
||||
|
||||
expect(within(rowFor('mine')).getByRole('button', { name: `${en.delete}: mine` })).toBeTruthy()
|
||||
expect(within(rowFor('standard')).queryByRole('button', { name: `${en.delete}: 标准模式` })).toBeNull()
|
||||
expect(within(rowFor('standard')).queryByRole('button', { name: `${en.delete}: ${en.presetStandardName}` })).toBeNull()
|
||||
})
|
||||
|
||||
it('disables duplication when nothing is writable, and says why', () => {
|
||||
renderSection({ authorable: false })
|
||||
|
||||
const duplicate = within(rowFor('standard')).getByRole('button', { name: `${en.duplicate}: 标准模式` })
|
||||
const duplicate = within(rowFor('standard')).getByRole('button', { name: `${en.duplicate}: ${en.presetStandardName}` })
|
||||
expect(duplicate).toHaveProperty('disabled', true)
|
||||
expect(duplicate.getAttribute('data-tip')).toBe(en.duplicateUnavailable)
|
||||
})
|
||||
@@ -205,7 +205,7 @@ describe('the preset list', () => {
|
||||
// There is no readable composition to offer; the reason on the card is
|
||||
// the whole story a shipped row can tell.
|
||||
const standard = rowFor('standard')
|
||||
expect(within(standard).queryByRole('button', { name: `${en.view}: 标准模式` })).toBeNull()
|
||||
expect(within(standard).queryByRole('button', { name: `${en.view}: ${en.presetStandardName}` })).toBeNull()
|
||||
expect(within(standard).getByRole('alert').textContent).toContain('not valid YAML')
|
||||
})
|
||||
|
||||
@@ -232,7 +232,7 @@ describe('the preset list', () => {
|
||||
fireEvent.click(within(rowFor('mine')).getByRole('button', { name: `${en.setDefault}: mine` }))
|
||||
fireEvent.click(within(rowFor('mine')).getByRole('button', { name: `${en.openLocation}: mine` }))
|
||||
fireEvent.click(within(rowFor('mine')).getByRole('button', { name: `${en.duplicate}: mine` }))
|
||||
fireEvent.click(within(rowFor('standard')).getByRole('button', { name: `${en.view}: 标准模式` }))
|
||||
fireEvent.click(within(rowFor('standard')).getByRole('button', { name: `${en.view}: ${en.presetStandardName}` }))
|
||||
|
||||
expect(actions.makeDefault).toHaveBeenCalledWith('mine')
|
||||
expect(actions.openLocation).toHaveBeenCalledWith('mine')
|
||||
@@ -311,7 +311,7 @@ describe('the copy dialog', () => {
|
||||
const actions = renderSection({ copy: draft })
|
||||
|
||||
const dialog = screen.getByRole('dialog')
|
||||
expect(dialog.getAttribute('aria-label')).toBe(`${en.copyTitle} · ${en.copyOf} 标准模式`)
|
||||
expect(dialog.getAttribute('aria-label')).toBe(`${en.copyTitle} · ${en.copyOf} ${en.presetStandardName}`)
|
||||
expect(within(dialog).getByText(en.copyIntro)).toBeTruthy()
|
||||
fireEvent.change(within(dialog).getByPlaceholderText(en.presetIdPlaceholder), { target: { value: 'my-agent' } })
|
||||
fireEvent.change(within(dialog).getByPlaceholderText(en.displayNamePlaceholder), { target: { value: '我的模式' } })
|
||||
@@ -374,11 +374,17 @@ describe('the read-only viewer', () => {
|
||||
renderSection({ view: { id: 'standard', title: '标准模式', content: '- id: tool-bash\n' } })
|
||||
|
||||
const dialog = screen.getByRole('dialog')
|
||||
expect(dialog.getAttribute('aria-label')).toBe(`${en.view} · 标准模式`)
|
||||
expect(dialog.getAttribute('aria-label')).toBe(`${en.view} · ${en.presetStandardName}`)
|
||||
expect(within(dialog).getByText(en.composition)).toBeTruthy()
|
||||
expect(within(dialog).getByText(/tool-bash/).textContent).toBe('- id: tool-bash\n')
|
||||
})
|
||||
|
||||
it('keeps the loaded title when the viewed row leaves the roster', () => {
|
||||
renderSection({ view: { id: 'retired', title: 'Retired mode', content: '- id: tool-bash\n' } })
|
||||
|
||||
expect(screen.getByRole('dialog').getAttribute('aria-label')).toBe(`${en.view} · Retired mode`)
|
||||
})
|
||||
|
||||
it('closes through the controller', () => {
|
||||
const actions = renderSection({ view: { id: 'standard', title: '标准模式', content: '- id: x\n' } })
|
||||
|
||||
|
||||
Reference in New Issue
Block a user