mirror of
https://github.com/QwenLM/qwen-code.git
synced 2025-12-21 01:07:46 +00:00
Add system-wide settings config for administrators (#3498)
Co-authored-by: Jack Wotherspoon <jackwoth@google.com>
This commit is contained in:
@@ -185,19 +185,30 @@ describe('App UI', () => {
|
||||
let currentUnmount: (() => void) | undefined;
|
||||
|
||||
const createMockSettings = (
|
||||
settings: Partial<Settings> = {},
|
||||
settings: {
|
||||
system?: Partial<Settings>;
|
||||
user?: Partial<Settings>;
|
||||
workspace?: Partial<Settings>;
|
||||
} = {},
|
||||
): LoadedSettings => {
|
||||
const systemSettingsFile: SettingsFile = {
|
||||
path: '/system/settings.json',
|
||||
settings: settings.system || {},
|
||||
};
|
||||
const userSettingsFile: SettingsFile = {
|
||||
path: '/user/settings.json',
|
||||
settings: {},
|
||||
settings: settings.user || {},
|
||||
};
|
||||
const workspaceSettingsFile: SettingsFile = {
|
||||
path: '/workspace/.gemini/settings.json',
|
||||
settings: {
|
||||
...settings,
|
||||
},
|
||||
settings: settings.workspace || {},
|
||||
};
|
||||
return new LoadedSettings(userSettingsFile, workspaceSettingsFile, []);
|
||||
return new LoadedSettings(
|
||||
systemSettingsFile,
|
||||
userSettingsFile,
|
||||
workspaceSettingsFile,
|
||||
[],
|
||||
);
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
@@ -222,7 +233,7 @@ describe('App UI', () => {
|
||||
mockConfig.getShowMemoryUsage.mockReturnValue(false); // Default for most tests
|
||||
|
||||
// Ensure a theme is set so the theme dialog does not appear.
|
||||
mockSettings = createMockSettings({ theme: 'Default' });
|
||||
mockSettings = createMockSettings({ workspace: { theme: 'Default' } });
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
@@ -268,8 +279,7 @@ describe('App UI', () => {
|
||||
|
||||
it('should display custom contextFileName in footer when set and count is 1', async () => {
|
||||
mockSettings = createMockSettings({
|
||||
contextFileName: 'AGENTS.md',
|
||||
theme: 'Default',
|
||||
workspace: { contextFileName: 'AGENTS.md', theme: 'Default' },
|
||||
});
|
||||
mockConfig.getGeminiMdFileCount.mockReturnValue(1);
|
||||
mockConfig.getDebugMode.mockReturnValue(false);
|
||||
@@ -288,8 +298,10 @@ describe('App UI', () => {
|
||||
|
||||
it('should display a generic message when multiple context files with different names are provided', async () => {
|
||||
mockSettings = createMockSettings({
|
||||
contextFileName: ['AGENTS.md', 'CONTEXT.md'],
|
||||
theme: 'Default',
|
||||
workspace: {
|
||||
contextFileName: ['AGENTS.md', 'CONTEXT.md'],
|
||||
theme: 'Default',
|
||||
},
|
||||
});
|
||||
mockConfig.getGeminiMdFileCount.mockReturnValue(2);
|
||||
mockConfig.getDebugMode.mockReturnValue(false);
|
||||
@@ -308,8 +320,7 @@ describe('App UI', () => {
|
||||
|
||||
it('should display custom contextFileName with plural when set and count is > 1', async () => {
|
||||
mockSettings = createMockSettings({
|
||||
contextFileName: 'MY_NOTES.TXT',
|
||||
theme: 'Default',
|
||||
workspace: { contextFileName: 'MY_NOTES.TXT', theme: 'Default' },
|
||||
});
|
||||
mockConfig.getGeminiMdFileCount.mockReturnValue(3);
|
||||
mockConfig.getDebugMode.mockReturnValue(false);
|
||||
@@ -328,8 +339,7 @@ describe('App UI', () => {
|
||||
|
||||
it('should not display context file message if count is 0, even if contextFileName is set', async () => {
|
||||
mockSettings = createMockSettings({
|
||||
contextFileName: 'ANY_FILE.MD',
|
||||
theme: 'Default',
|
||||
workspace: { contextFileName: 'ANY_FILE.MD', theme: 'Default' },
|
||||
});
|
||||
mockConfig.getGeminiMdFileCount.mockReturnValue(0);
|
||||
mockConfig.getDebugMode.mockReturnValue(false);
|
||||
@@ -399,7 +409,9 @@ describe('App UI', () => {
|
||||
|
||||
it('should not display Tips component when hideTips is true', async () => {
|
||||
mockSettings = createMockSettings({
|
||||
hideTips: true,
|
||||
workspace: {
|
||||
hideTips: true,
|
||||
},
|
||||
});
|
||||
|
||||
const { unmount } = render(
|
||||
@@ -413,6 +425,24 @@ describe('App UI', () => {
|
||||
expect(vi.mocked(Tips)).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should show tips if system says show, but workspace and user settings say hide', async () => {
|
||||
mockSettings = createMockSettings({
|
||||
system: { hideTips: false },
|
||||
user: { hideTips: true },
|
||||
workspace: { hideTips: true },
|
||||
});
|
||||
|
||||
const { unmount } = render(
|
||||
<App
|
||||
config={mockConfig as unknown as ServerConfig}
|
||||
settings={mockSettings}
|
||||
/>,
|
||||
);
|
||||
currentUnmount = unmount;
|
||||
await Promise.resolve();
|
||||
expect(vi.mocked(Tips)).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
describe('when no theme is set', () => {
|
||||
let originalNoColor: string | undefined;
|
||||
|
||||
|
||||
@@ -29,6 +29,10 @@ describe('AuthDialog', () => {
|
||||
process.env.GEMINI_API_KEY = '';
|
||||
|
||||
const settings: LoadedSettings = new LoadedSettings(
|
||||
{
|
||||
settings: {},
|
||||
path: '',
|
||||
},
|
||||
{
|
||||
settings: {
|
||||
selectedAuthType: AuthType.USE_GEMINI,
|
||||
@@ -86,6 +90,12 @@ describe('AuthDialog', () => {
|
||||
settings: {},
|
||||
path: '',
|
||||
},
|
||||
{
|
||||
settings: {
|
||||
selectedAuthType: undefined,
|
||||
},
|
||||
path: '',
|
||||
},
|
||||
{
|
||||
settings: {},
|
||||
path: '',
|
||||
@@ -147,6 +157,10 @@ describe('AuthDialog', () => {
|
||||
it('should allow exiting when auth method is already selected', async () => {
|
||||
const onSelect = vi.fn();
|
||||
const settings: LoadedSettings = new LoadedSettings(
|
||||
{
|
||||
settings: {},
|
||||
path: '',
|
||||
},
|
||||
{
|
||||
settings: {
|
||||
selectedAuthType: AuthType.USE_GEMINI,
|
||||
|
||||
@@ -57,6 +57,7 @@ export function ThemeDialog({
|
||||
const scopeItems = [
|
||||
{ label: 'User Settings', value: SettingScope.User },
|
||||
{ label: 'Workspace Settings', value: SettingScope.Workspace },
|
||||
{ label: 'System Settings', value: SettingScope.System },
|
||||
];
|
||||
|
||||
const handleThemeSelect = (themeName: string) => {
|
||||
@@ -86,16 +87,21 @@ export function ThemeDialog({
|
||||
}
|
||||
});
|
||||
|
||||
const otherScopes = Object.values(SettingScope).filter(
|
||||
(scope) => scope !== selectedScope,
|
||||
);
|
||||
|
||||
const modifiedInOtherScopes = otherScopes.filter(
|
||||
(scope) => settings.forScope(scope).settings.theme !== undefined,
|
||||
);
|
||||
|
||||
let otherScopeModifiedMessage = '';
|
||||
const otherScope =
|
||||
selectedScope === SettingScope.User
|
||||
? SettingScope.Workspace
|
||||
: SettingScope.User;
|
||||
if (settings.forScope(otherScope).settings.theme !== undefined) {
|
||||
if (modifiedInOtherScopes.length > 0) {
|
||||
const modifiedScopesStr = modifiedInOtherScopes.join(', ');
|
||||
otherScopeModifiedMessage =
|
||||
settings.forScope(selectedScope).settings.theme !== undefined
|
||||
? `(Also modified in ${otherScope})`
|
||||
: `(Modified in ${otherScope})`;
|
||||
? `(Also modified in ${modifiedScopesStr})`
|
||||
: `(Modified in ${modifiedScopesStr})`;
|
||||
}
|
||||
|
||||
// Constants for calculating preview pane layout.
|
||||
|
||||
Reference in New Issue
Block a user