Files
ccs/tests/unit/utils/browser/browser-setup.test.ts
Tam Nhu Tran 4f6e61739c refactor(config): adopt config-loader-facade across the codebase
Issue #1161. Sweeps 127 files to import from
src/config/config-loader-facade.ts instead of unified-config-loader or
utils/config-manager directly.

WRITE callers (32 files): replaced raw saveUnifiedConfig /
mutateUnifiedConfig / updateUnifiedConfig calls with the facade's
cache-coherent wrappers saveConfig / mutateConfig / updateConfig. This
fixes a latent stale-cache window where direct writes through the
underlying loader bypassed the facade's memoization.

READ callers (95 files): mechanical import-path migration only —
function names unchanged because the facade re-exports them. No
behavior change.

Also updated:
- tests/unit/utils/browser/browser-setup.test.ts (DI interface rename)
- src/management/checks/image-analysis-check.ts (dynamic import rename)
- src/web-server/health-service.ts (dynamic require rename)
- src/ccs.ts (path prefix fix from sweep script)

After sweep: zero raw write callers remain outside src/config/. Direct
imports of config-manager remain only for symbols not in the facade
(getConfigPath, getCcsDirSource, etc). Behavior unchanged; full suite
passes 1824/1824.

Out of scope: switching loadOrCreateUnifiedConfig() callers to
getCachedConfig() — needs per-callsite cache-safety analysis. Tracked
as follow-up.

Refs #1161
2026-05-03 01:42:53 -04:00

216 lines
6.3 KiB
TypeScript

import { afterEach, describe, expect, it } from 'bun:test';
import { mkdtempSync, rmSync } from 'node:fs';
import { tmpdir } from 'node:os';
import { join } from 'node:path';
import type { UnifiedConfig } from '../../../../src/config/unified-config-types';
import { runBrowserSetup, type BrowserSetupDeps } from '../../../../src/utils/browser/browser-setup';
function createUnifiedConfig(userDataDir: string): UnifiedConfig {
return {
version: 12,
default: undefined,
profiles: {},
profile_targets: {},
copilot: {
enabled: false,
prompt: '',
command: '',
args: [],
env: {},
auto_install: false,
},
cursor: {
enabled: false,
model: '',
port: 3891,
daemon_mode: false,
auth: {
token: '',
},
},
websearch: {
enabled: false,
providers: {},
},
browser: {
claude: {
enabled: false,
user_data_dir: userDataDir,
devtools_port: 9222,
},
codex: {
enabled: true,
},
},
image_analysis: {
enabled: false,
providers: {},
},
global_env: {},
cliproxy_server: {
mode: 'local',
remote: {
enabled: false,
host: '',
port: 0,
protocol: 'http',
auth_token: '',
management_key: '',
},
local: {
port: 8085,
auto_start: true,
},
},
cliproxy_safety: {
concurrent_limit: 1,
cooldown_seconds: 0,
shared_responsibility: false,
},
quota_management: {
enabled: false,
},
thinking: {
mode: 'auto',
show_warnings: true,
},
official_channels: {
enabled: false,
selected: [],
unattended: false,
},
dashboard_auth: {
enabled: false,
users: [],
session_secret: '',
},
logging: {
enabled: true,
profile_starts: true,
delegation_calls: true,
proxy_requests: false,
quota_polls: false,
},
};
}
describe('browser setup', () => {
let tempDir = '';
afterEach(() => {
if (tempDir) {
rmSync(tempDir, { recursive: true, force: true });
tempDir = '';
}
});
it('enables Claude browser attach and prepares the managed default path', async () => {
tempDir = mkdtempSync(join(tmpdir(), 'ccs-browser-setup-'));
const config = createUnifiedConfig(join(tempDir, 'browser-profile'));
config.browser.claude.user_data_dir = '';
const deps: BrowserSetupDeps = {
getBrowserConfig: () => config.browser,
mutateConfig: (mutator) => {
mutator(config);
return config;
},
ensureBrowserMcp: () => true,
getBrowserStatus: async () =>
({
claude: {
enabled: true,
source: 'config',
overrideActive: false,
state: 'ready',
title: 'Claude Browser Attach is ready.',
detail: 'ready',
nextStep: 'Launch Claude.',
effectiveUserDataDir: config.browser.claude.user_data_dir,
recommendedUserDataDir: config.browser.claude.user_data_dir,
devtoolsPort: 9222,
managedMcpServerName: 'ccs-browser',
managedMcpServerPath: '/tmp/ccs-browser-server.cjs',
launchCommands: {
darwin: 'open -na "Google Chrome" --args',
linux: 'google-chrome --remote-debugging-port=9222',
win32: 'chrome.exe --remote-debugging-port=9222',
},
},
codex: {
enabled: true,
state: 'enabled',
title: 'Codex Browser Tools are enabled.',
detail: 'ready',
nextStep: 'Use Codex.',
serverName: 'ccs_browser',
supportsConfigOverrides: true,
binaryPath: '/usr/local/bin/codex',
},
}) as Awaited<ReturnType<BrowserSetupDeps['getBrowserStatus']>>,
};
const result = await runBrowserSetup(deps);
expect(result.configUpdated).toBe(true);
expect(result.createdUserDataDir).toBe(true);
expect(result.ready).toBe(true);
expect(config.browser.claude.enabled).toBe(true);
expect(config.browser.claude.user_data_dir).not.toBe('');
});
it('does not create a custom override path during setup', async () => {
tempDir = mkdtempSync(join(tmpdir(), 'ccs-browser-setup-'));
const config = createUnifiedConfig(join(tempDir, 'browser-profile'));
config.browser.claude.enabled = true;
const deps: BrowserSetupDeps = {
getBrowserConfig: () => config.browser,
mutateConfig: (mutator) => {
mutator(config);
return config;
},
ensureBrowserMcp: () => true,
getBrowserStatus: async () =>
({
claude: {
enabled: true,
source: 'CCS_BROWSER_USER_DATA_DIR',
overrideActive: true,
state: 'browser_not_running',
title: 'Claude Browser Attach could not find a running browser session.',
detail: 'override active',
nextStep: 'Run `ccs browser setup` to configure and start the managed browser session.',
effectiveUserDataDir: config.browser.claude.user_data_dir,
recommendedUserDataDir: config.browser.claude.user_data_dir,
devtoolsPort: 9222,
managedMcpServerName: 'ccs-browser',
managedMcpServerPath: '/tmp/ccs-browser-server.cjs',
launchCommands: {
darwin: 'open -na "Google Chrome" --args',
linux: 'google-chrome --remote-debugging-port=9222',
win32: 'chrome.exe --remote-debugging-port=9222',
},
},
codex: {
enabled: true,
state: 'enabled',
title: 'Codex Browser Tools are enabled.',
detail: 'ready',
nextStep: 'Use Codex.',
serverName: 'ccs_browser',
supportsConfigOverrides: true,
binaryPath: '/usr/local/bin/codex',
},
}) as Awaited<ReturnType<BrowserSetupDeps['getBrowserStatus']>>,
};
const result = await runBrowserSetup(deps);
expect(result.createdUserDataDir).toBe(false);
expect(result.overrideActive).toBe(false);
expect(result.ready).toBe(false);
expect(result.notes[0]).toContain('did not create the current browser user-data dir');
});
});