feat(admin): provide actionable error messages for disabled features (#17815)

This commit is contained in:
Shreya Keshive
2026-01-30 13:05:22 -05:00
committed by GitHub
parent b611f9a519
commit f14d0c6a17
12 changed files with 228 additions and 38 deletions

View File

@@ -30,6 +30,9 @@ vi.mock('@google/gemini-cli-core', async () => {
return {
...actualServerModule,
Config: vi.fn(),
getAdminErrorMessage: vi.fn(
(featureName: string) => `[Mock] ${featureName} is disabled`,
),
};
});
@@ -52,6 +55,9 @@ interface MockConfigInstanceShape {
getUserMemory: Mock<() => string>;
getGeminiMdFileCount: Mock<() => number>;
getToolRegistry: Mock<() => { discoverTools: Mock<() => void> }>;
getRemoteAdminSettings: Mock<
() => { strictModeDisabled?: boolean; mcpEnabled?: boolean } | undefined
>;
}
type UseKeypressHandler = (key: Key) => void;
@@ -109,6 +115,9 @@ describe('useApprovalModeIndicator', () => {
.mockReturnValue({ discoverTools: vi.fn() }) as Mock<
() => { discoverTools: Mock<() => void> }
>,
getRemoteAdminSettings: vi.fn().mockReturnValue(undefined) as Mock<
() => { strictModeDisabled?: boolean } | undefined
>,
};
instanceSetApprovalModeMock.mockImplementation((value: ApprovalMode) => {
instanceGetApprovalModeMock.mockReturnValue(value);
@@ -517,6 +526,9 @@ describe('useApprovalModeIndicator', () => {
it('should not enable YOLO mode when Ctrl+Y is pressed and add an info message', () => {
mockConfigInstance.getApprovalMode.mockReturnValue(ApprovalMode.DEFAULT);
mockConfigInstance.getRemoteAdminSettings.mockReturnValue({
strictModeDisabled: true,
});
const mockAddItem = vi.fn();
const { result } = renderHook(() =>
useApprovalModeIndicator({
@@ -544,6 +556,58 @@ describe('useApprovalModeIndicator', () => {
// The mode should not change
expect(result.current).toBe(ApprovalMode.DEFAULT);
});
it('should show admin error message when YOLO mode is disabled by admin', () => {
mockConfigInstance.getApprovalMode.mockReturnValue(ApprovalMode.DEFAULT);
mockConfigInstance.getRemoteAdminSettings.mockReturnValue({
mcpEnabled: true,
});
const mockAddItem = vi.fn();
renderHook(() =>
useApprovalModeIndicator({
config: mockConfigInstance as unknown as ActualConfigType,
addItem: mockAddItem,
}),
);
act(() => {
capturedUseKeypressHandler({ name: 'y', ctrl: true } as Key);
});
expect(mockAddItem).toHaveBeenCalledWith(
{
type: MessageType.WARNING,
text: '[Mock] YOLO mode is disabled',
},
expect.any(Number),
);
});
it('should show default error message when admin settings are empty', () => {
mockConfigInstance.getApprovalMode.mockReturnValue(ApprovalMode.DEFAULT);
mockConfigInstance.getRemoteAdminSettings.mockReturnValue({});
const mockAddItem = vi.fn();
renderHook(() =>
useApprovalModeIndicator({
config: mockConfigInstance as unknown as ActualConfigType,
addItem: mockAddItem,
}),
);
act(() => {
capturedUseKeypressHandler({ name: 'y', ctrl: true } as Key);
});
expect(mockAddItem).toHaveBeenCalledWith(
{
type: MessageType.WARNING,
text: 'You cannot enter YOLO mode since it is disabled in your settings.',
},
expect.any(Number),
);
});
});
it('should call onApprovalModeChange when switching to YOLO mode', () => {

View File

@@ -5,7 +5,11 @@
*/
import { useState, useEffect } from 'react';
import { ApprovalMode, type Config } from '@google/gemini-cli-core';
import {
ApprovalMode,
type Config,
getAdminErrorMessage,
} from '@google/gemini-cli-core';
import { useKeypress } from './useKeypress.js';
import { keyMatchers, Command } from '../keyMatchers.js';
import type { HistoryItemWithoutId } from '../types.js';
@@ -41,10 +45,19 @@ export function useApprovalModeIndicator({
config.getApprovalMode() !== ApprovalMode.YOLO
) {
if (addItem) {
let text =
'You cannot enter YOLO mode since it is disabled in your settings.';
const adminSettings = config.getRemoteAdminSettings();
const hasSettings =
adminSettings && Object.keys(adminSettings).length > 0;
if (hasSettings && !adminSettings.strictModeDisabled) {
text = getAdminErrorMessage('YOLO mode', config);
}
addItem(
{
type: MessageType.WARNING,
text: 'You cannot enter YOLO mode since it is disabled in your settings.',
text,
},
Date.now(),
);