完善客户端模块与工作区能力
Some checks failed
Electron E2E / Electron E2E (macos-latest) (push) Has been cancelled
Electron E2E / Electron E2E (ubuntu-latest) (push) Has been cancelled
Electron E2E / Electron E2E (windows-latest) (push) Has been cancelled

This commit is contained in:
inman
2026-08-13 19:45:30 +08:00
parent 0148b91a15
commit 22add3f01f
97 changed files with 4756 additions and 3226 deletions

View File

@@ -50,6 +50,14 @@ function renderSettings() {
);
}
async function unlockAdditionalSettings(): Promise<void> {
fireEvent.click(await screen.findByRole('button', { name: 'appearance.showMoreSettings' }));
const passwordInput = await screen.findByTestId('settings-admin-password-input');
fireEvent.change(passwordInput, { target: { value: 'zhiniankeji666' } });
fireEvent.click(screen.getByTestId('settings-admin-unlock-button'));
await screen.findByTestId('settings-opencode-workspace');
}
function mockValidatedWorkspaceHostApi(activeProject: {
id: string;
path: string;
@@ -98,7 +106,12 @@ function mockValidatedWorkspaceHostApi(activeProject: {
describe('Settings runtime workspace', () => {
beforeEach(() => {
vi.clearAllMocks();
invokeIpcMock.mockResolvedValue(undefined);
invokeIpcMock.mockImplementation(async (channel: string, payload?: unknown) => {
if (channel === 'admin:isUnlocked') return false;
if (channel === 'admin:verifyPassword') return { success: payload === 'zhiniankeji666' };
if (channel === 'admin:lock') return { success: true };
return undefined;
});
useSettingsStore.setState({
theme: 'system',
language: 'en',
@@ -148,7 +161,7 @@ describe('Settings runtime workspace', () => {
});
});
it('hides non-general settings until the general header reveal button is clicked', async () => {
it('keeps runtime status and updates public while protecting developer settings', async () => {
const activeProject = {
id: 'prj_1',
path: 'D:/repo/packages/ui',
@@ -162,20 +175,35 @@ describe('Settings runtime workspace', () => {
renderSettings();
expect(await screen.findByRole('heading', { name: 'appearance.title' })).toBeInTheDocument();
expect(screen.getByTestId('settings-opencode-workspace').closest('[data-state]')).toHaveAttribute('data-state', 'closed');
expect(screen.getByText('Network').closest('[data-state]')).toHaveAttribute('data-state', 'closed');
expect(screen.getByText('updates.title').closest('[data-state]')).toHaveAttribute('data-state', 'closed');
expect(screen.getByText('advanced.title').closest('[data-state]')).toHaveAttribute('data-state', 'closed');
expect(screen.getByTestId('settings-runtime-summary')).toBeInTheDocument();
expect(screen.getByRole('button', { name: 'Restart runtime' })).toBeInTheDocument();
expect(screen.getByText('updates.title')).toBeInTheDocument();
expect(screen.queryByTestId('settings-opencode-workspace')).not.toBeInTheDocument();
expect(screen.queryByText('Network')).not.toBeInTheDocument();
expect(screen.queryByText('advanced.title')).not.toBeInTheDocument();
fireEvent.click(screen.getByRole('button', { name: 'appearance.showMoreSettings' }));
expect(await screen.findByTestId('settings-opencode-workspace')).toBeInTheDocument();
expect(screen.getByText('Network')).toBeInTheDocument();
expect(screen.getByText('updates.title')).toBeInTheDocument();
expect(screen.getByText('advanced.title')).toBeInTheDocument();
expect(screen.getByTestId('settings-admin-password-dialog')).toBeInTheDocument();
expect(screen.queryByTestId('settings-opencode-workspace')).not.toBeInTheDocument();
});
it('renders runtime, model, and workspace controls in settings without exposing the runtime brand', async () => {
it('rejects an incorrect administrator password and unlocks the protected settings with the fixed password', async () => {
renderSettings();
fireEvent.click(await screen.findByRole('button', { name: 'appearance.showMoreSettings' }));
fireEvent.change(screen.getByTestId('settings-admin-password-input'), { target: { value: 'wrong' } });
fireEvent.click(screen.getByTestId('settings-admin-unlock-button'));
expect(await screen.findByRole('alert')).toBeInTheDocument();
expect(screen.queryByTestId('settings-opencode-workspace')).not.toBeInTheDocument();
fireEvent.change(screen.getByTestId('settings-admin-password-input'), { target: { value: 'zhiniankeji666' } });
fireEvent.click(screen.getByTestId('settings-admin-unlock-button'));
expect(await screen.findByTestId('settings-opencode-workspace')).toBeInTheDocument();
});
it('renders runtime, model, and workspace controls after administrator unlock without exposing the runtime brand', async () => {
const activeProject = {
id: 'prj_1',
path: 'D:/repo/packages/ui',
@@ -188,30 +216,21 @@ describe('Settings runtime workspace', () => {
mockValidatedWorkspaceHostApi(activeProject);
renderSettings();
fireEvent.click(await screen.findByRole('button', { name: 'appearance.showMoreSettings' }));
await unlockAdditionalSettings();
const workspace = await screen.findByTestId('settings-opencode-workspace');
expect(within(workspace).getByText('Workspace')).toBeInTheDocument();
expect(screen.getByTestId('settings-opencode-runtime')).toBeInTheDocument();
expect(screen.getByTestId('settings-runtime-summary')).toBeInTheDocument();
expect(screen.getByTestId('settings-opencode-models')).toBeInTheDocument();
expect(screen.getByText('openai/gpt-5.4')).toBeInTheDocument();
expect(screen.getByText('openai/gpt-5.4-mini')).toBeInTheDocument();
expect(screen.getByText('2 providers')).toBeInTheDocument();
expect(screen.getAllByText('D:/repo/packages/ui').length).toBeGreaterThan(0);
await waitFor(() =>
expect(hostApiFetchMock).toHaveBeenCalledWith('/api/opencode/start', {
method: 'POST',
})
);
expect(screen.getByRole('button', { name: 'Restart runtime' })).toBeInTheDocument();
expect(screen.getByRole('button', { name: 'Stop runtime' })).toBeInTheDocument();
expect(screen.queryByRole('button', { name: 'Stop runtime' })).not.toBeInTheDocument();
expect(screen.queryByRole('button', { name: 'Check health' })).not.toBeInTheDocument();
expect(document.body.textContent).not.toMatch(/opencode/i);
expect(screen.queryByLabelText(/opencode/i)).not.toBeInTheDocument();
fireEvent.click(screen.getByRole('button', { name: 'Check health' }));
await waitFor(() => expect(hostApiFetchMock).toHaveBeenCalledWith('/api/opencode/health'));
});
it('opens the model configuration page from the workspace models card', async () => {
@@ -226,7 +245,7 @@ describe('Settings runtime workspace', () => {
mockValidatedWorkspaceHostApi(activeProject);
renderSettings();
fireEvent.click(await screen.findByRole('button', { name: 'appearance.showMoreSettings' }));
await unlockAdditionalSettings();
const modelsCard = await screen.findByTestId('settings-opencode-models');
fireEvent.click(within(modelsCard).getByRole('button', { name: 'Configure models' }));
@@ -261,7 +280,7 @@ describe('Settings runtime workspace', () => {
});
renderSettings();
fireEvent.click(await screen.findByRole('button', { name: 'appearance.showMoreSettings' }));
await unlockAdditionalSettings();
fireEvent.click(await screen.findByRole('button', { name: `Use project ${activeProject.name}` }));
expect(await screen.findByText(/configuration is missing/i)).toBeInTheDocument();
@@ -304,7 +323,7 @@ describe('Settings runtime workspace', () => {
});
renderSettings();
fireEvent.click(await screen.findByRole('button', { name: 'appearance.showMoreSettings' }));
await unlockAdditionalSettings();
fireEvent.click(await screen.findByRole('button', { name: `Use project ${activeProject.name}` }));
await waitFor(() => {