Files
deepseek-harness/packages/client/ui-permission/tests/permission-row.spec.tsx
imccyu 159d102f99 refactor(client): name one assembly package for the carrier types
A business package imported the Remote assembly for `ctx.remote` and the
Connection plugin for the wire types it passes around. The assembly now
re-exports the carrier's Client-facing types, so a business package names one
package. The re-export is type-only: the carrier's runtime values keep their
own module edge, since inlining them here would duplicate the carrier inside
the assembly bundle.

Four surfaces that had no Remote assembly dependency declare one now.
2026-08-11 23:33:16 +08:00

158 lines
6.2 KiB
TypeScript

// @vitest-environment jsdom
import { afterEach, describe, expect, it, vi } from 'vitest'
import { cleanup, fireEvent, render, screen, waitFor } from '@testing-library/react'
import { bindSnapshotSelector } from '@deepseek-ai/dsh-client-web-react'
import type { SettingsNamespaceView } from '@deepseek-ai/dsh-api-remotes/client'
import { PermissionRow, type PermissionRowProps } from '../src/client/PermissionRow.tsx'
import { en } from '../src/client/locales.ts'
import { PermissionSettingsController } from '../src/client/settings-store.ts'
afterEach(cleanup)
const SCHEMA = {
uid: 5,
refs: {
1: { type: 'const', value: 'read-only' },
2: { type: 'const', value: 'workspace-write' },
3: { type: 'const', value: 'danger-full-access' },
4: { type: 'union', list: [1, 2, 3] },
5: { type: 'object', dict: { defaultPreset: 4 } },
},
}
function view(defaultPreset: string, revision = 0): SettingsNamespaceView {
return {
ns: 'permission',
schema: SCHEMA,
value: { defaultPreset },
base: { defaultPreset: 'read-only' },
applies: 'live',
secrets: [],
revision,
}
}
function ok<T>(value: T) {
return { rpcId: 'test', result: { ok: true as const, value } }
}
const dictionary: Record<string, string> = en
const t: PermissionRowProps['t'] = key => dictionary[key] ?? key
const runtime = {
useSessions: (() => { throw new Error('unused') }) as never,
useWorkspaces: (() => { throw new Error('unused') }) as never,
}
function mount(controller: PermissionSettingsController) {
return render(
<PermissionRow
{...runtime}
load={() => controller.load()}
select={preset => controller.select(preset)}
usePermission={bindSnapshotSelector(controller.store)}
t={t}
/>,
)
}
describe('PermissionRow', () => {
it('loads the descriptor, opens the menu, and selects a new default', async () => {
const mutate = vi.fn(() => Promise.resolve(ok(view('workspace-write', 1))))
const controller = new PermissionSettingsController({
settings: {
describe: () => Promise.resolve(ok({ writable: true, hasDocument: false, namespaces: [view('read-only')] })),
mutate,
} as never,
})
mount(controller)
const button = await screen.findByRole('button', { name: 'Read Only' })
expect(button.getAttribute('aria-expanded')).toBe('false')
fireEvent.click(button)
expect(button.getAttribute('aria-expanded')).toBe('true')
fireEvent.keyDown(document, { key: 'Escape' })
await waitFor(() => { expect(button.getAttribute('aria-expanded')).toBe('false') })
fireEvent.click(button)
fireEvent.click(button)
expect(button.getAttribute('aria-expanded')).toBe('false')
fireEvent.click(button)
fireEvent.click(screen.getByRole('menuitem', { name: 'Read Only' }))
expect(mutate).not.toHaveBeenCalled()
fireEvent.click(button)
fireEvent.click(screen.getByRole('menuitem', { name: 'Workspace Write' }))
await screen.findByRole('button', { name: 'Workspace Write' })
expect(mutate).toHaveBeenCalledOnce()
})
it('requires explicit acknowledgement before saving Full access', async () => {
const mutate = vi.fn(() => Promise.resolve(ok(view('danger-full-access', 1))))
const controller = new PermissionSettingsController({
settings: {
describe: () => Promise.resolve(ok({ writable: true, hasDocument: false, namespaces: [view('read-only')] })),
mutate,
} as never,
})
mount(controller)
fireEvent.click(await screen.findByRole('button', { name: 'Read Only' }))
fireEvent.click(screen.getByRole('menuitem', { name: 'Full access' }))
expect(mutate).not.toHaveBeenCalled()
fireEvent.click(screen.getByRole('button', { name: 'Cancel' }))
expect(screen.queryByRole('dialog', { name: 'Enable Full access?' })).toBeNull()
fireEvent.click(screen.getByRole('button', { name: 'Read Only' }))
fireEvent.click(screen.getByRole('menuitem', { name: 'Full access' }))
const dialog = screen.getByRole('dialog', { name: 'Enable Full access?' })
const enable = screen.getByRole('button', { name: 'Enable Full access' })
expect((enable as HTMLButtonElement).disabled).toBe(true)
fireEvent.click(screen.getByRole('checkbox'))
fireEvent.click(enable)
await waitFor(() => { expect(mutate).toHaveBeenCalledOnce() })
expect(dialog.isConnected).toBe(false)
})
it('hides an unavailable namespace and disables a read-only provider', async () => {
const absent = new PermissionSettingsController({
settings: {
describe: () => Promise.resolve(ok({ writable: true, hasDocument: false, namespaces: [] })),
mutate: vi.fn(),
} as never,
})
const rendered = mount(absent)
await waitFor(() => { expect(rendered.container.textContent).toBe('') })
rendered.unmount()
const readonly = new PermissionSettingsController({
settings: {
describe: () => Promise.resolve(ok({ writable: false, hasDocument: false, namespaces: [view('read-only')] })),
mutate: vi.fn(),
} as never,
})
mount(readonly)
expect((await screen.findByRole('button', { name: 'Read Only' })).hasAttribute('disabled')).toBe(true)
})
it('shows loading and a contained write error', async () => {
const describe = Promise.withResolvers<ReturnType<typeof ok<{
writable: boolean
namespaces: SettingsNamespaceView[]
}>>>()
const controller = new PermissionSettingsController({
settings: {
describe: () => describe.promise,
mutate: () => Promise.resolve({
rpcId: 'test',
result: {
ok: false as const,
error: { code: 'settings-conflict', message: 'changed elsewhere', details: {} },
},
}),
} as never,
})
mount(controller)
expect((await screen.findByRole('button', { name: 'Loading' })).hasAttribute('disabled')).toBe(true)
describe.resolve(ok({ writable: true, hasDocument: false, namespaces: [view('read-only')] }))
const button = await screen.findByRole('button', { name: 'Read Only' })
fireEvent.click(button)
fireEvent.click(screen.getByRole('menuitem', { name: 'Workspace Write' }))
expect((await screen.findByRole('alert')).textContent).toBe('changed elsewhere')
})
})