mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-03-16 17:11:04 -07:00
Improve code coverage for cli package (#13724)
This commit is contained in:
@@ -0,0 +1,52 @@
|
||||
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
||||
|
||||
exports[`InlineMarkdownRenderer > RenderInline > handles nested/complex markdown gracefully (best effort) 1`] = `
|
||||
"Bold *Italic
|
||||
*"
|
||||
`;
|
||||
|
||||
exports[`InlineMarkdownRenderer > RenderInline > renders bold text correctly 1`] = `
|
||||
"Hello
|
||||
World"
|
||||
`;
|
||||
|
||||
exports[`InlineMarkdownRenderer > RenderInline > renders inline code correctly 1`] = `
|
||||
"Hello
|
||||
World"
|
||||
`;
|
||||
|
||||
exports[`InlineMarkdownRenderer > RenderInline > renders italic text correctly 1`] = `
|
||||
"Hello
|
||||
World"
|
||||
`;
|
||||
|
||||
exports[`InlineMarkdownRenderer > RenderInline > renders links correctly 1`] = `"Google (https://google.com)"`;
|
||||
|
||||
exports[`InlineMarkdownRenderer > RenderInline > renders mixed markdown correctly 1`] = `
|
||||
"Bold
|
||||
and
|
||||
Italic
|
||||
and
|
||||
Code
|
||||
and
|
||||
Link (https://example.com)"
|
||||
`;
|
||||
|
||||
exports[`InlineMarkdownRenderer > RenderInline > renders plain text correctly 1`] = `"Hello World"`;
|
||||
|
||||
exports[`InlineMarkdownRenderer > RenderInline > renders raw URLs correctly 1`] = `
|
||||
"Visit
|
||||
https://google.com"
|
||||
`;
|
||||
|
||||
exports[`InlineMarkdownRenderer > RenderInline > renders strikethrough text correctly 1`] = `
|
||||
"Hello
|
||||
World"
|
||||
`;
|
||||
|
||||
exports[`InlineMarkdownRenderer > RenderInline > renders underline correctly 1`] = `
|
||||
"Hello
|
||||
World"
|
||||
`;
|
||||
|
||||
exports[`InlineMarkdownRenderer > RenderInline > respects defaultColor prop 1`] = `"Hello"`;
|
||||
@@ -0,0 +1,65 @@
|
||||
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
||||
|
||||
exports[`TableRenderer > handles empty rows 1`] = `
|
||||
"
|
||||
┌──────┬──────┬────────┐
|
||||
│ Name │ Role │ Status │
|
||||
├──────┼──────┼────────┤
|
||||
└──────┴──────┴────────┘
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`TableRenderer > handles markdown content in cells 1`] = `
|
||||
"
|
||||
┌───────┬──────────┬────────┐
|
||||
│ Name │ Role │ Status │
|
||||
├───────┼──────────┼────────┤
|
||||
│ Alice │ Engineer │ Active │
|
||||
└───────┴──────────┴────────┘
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`TableRenderer > handles rows with missing cells 1`] = `
|
||||
"
|
||||
┌───────┬──────────┬────────┐
|
||||
│ Name │ Role │ Status │
|
||||
├───────┼──────────┼────────┤
|
||||
│ Alice │ Engineer │
|
||||
│ Bob │
|
||||
└───────┴──────────┴────────┘
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`TableRenderer > renders a simple table correctly 1`] = `
|
||||
"
|
||||
┌─────────┬──────────┬──────────┐
|
||||
│ Name │ Role │ Status │
|
||||
├─────────┼──────────┼──────────┤
|
||||
│ Alice │ Engineer │ Active │
|
||||
│ Bob │ Designer │ Inactive │
|
||||
│ Charlie │ Manager │ Active │
|
||||
└─────────┴──────────┴──────────┘
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`TableRenderer > truncates content when terminal width is small 1`] = `
|
||||
"
|
||||
┌────────┬─────────┬─────────┐
|
||||
│ Name │ Role │ Status │
|
||||
├────────┼─────────┼─────────┤
|
||||
│ Alice │ Engi... │ Active │
|
||||
│ Bob │ Desi... │ Inac... │
|
||||
│ Cha... │ Manager │ Active │
|
||||
└────────┴─────────┴─────────┘
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`TableRenderer > truncates long markdown content correctly 1`] = `
|
||||
"
|
||||
┌───────────────────────────┬─────┬────┐
|
||||
│ Name │ Rol │ St │
|
||||
├───────────────────────────┼─────┼────┤
|
||||
│ Alice with a very long... │ Eng │ Ac │
|
||||
└───────────────────────────┴─────┴────┘
|
||||
"
|
||||
`;
|
||||
@@ -0,0 +1,24 @@
|
||||
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
||||
|
||||
exports[`terminalSetup > configureVSCodeStyle > should create new keybindings file if none exists 1`] = `
|
||||
[
|
||||
{
|
||||
"args": {
|
||||
"text": "\\
|
||||
",
|
||||
},
|
||||
"command": "workbench.action.terminal.sendSequence",
|
||||
"key": "ctrl+enter",
|
||||
"when": "terminalFocus",
|
||||
},
|
||||
{
|
||||
"args": {
|
||||
"text": "\\
|
||||
",
|
||||
},
|
||||
"command": "workbench.action.terminal.sendSequence",
|
||||
"key": "shift+enter",
|
||||
"when": "terminalFocus",
|
||||
},
|
||||
]
|
||||
`;
|
||||
@@ -0,0 +1,20 @@
|
||||
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
||||
|
||||
exports[`ui-sizing > calculateMainAreaWidth > should match snapshot for interpolation range 1`] = `
|
||||
{
|
||||
"100": 95,
|
||||
"104": 98,
|
||||
"108": 101,
|
||||
"112": 104,
|
||||
"116": 107,
|
||||
"120": 110,
|
||||
"124": 113,
|
||||
"128": 116,
|
||||
"132": 119,
|
||||
"80": 78,
|
||||
"84": 82,
|
||||
"88": 85,
|
||||
"92": 88,
|
||||
"96": 92,
|
||||
}
|
||||
`;
|
||||
145
packages/cli/src/ui/utils/kittyProtocolDetector.test.ts
Normal file
145
packages/cli/src/ui/utils/kittyProtocolDetector.test.ts
Normal file
@@ -0,0 +1,145 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2025 Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
||||
|
||||
// Mock dependencies
|
||||
const mocks = vi.hoisted(() => ({
|
||||
writeSync: vi.fn(),
|
||||
enableKittyKeyboardProtocol: vi.fn(),
|
||||
disableKittyKeyboardProtocol: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock('node:fs', () => ({
|
||||
writeSync: mocks.writeSync,
|
||||
}));
|
||||
|
||||
vi.mock('@google/gemini-cli-core', () => ({
|
||||
enableKittyKeyboardProtocol: mocks.enableKittyKeyboardProtocol,
|
||||
disableKittyKeyboardProtocol: mocks.disableKittyKeyboardProtocol,
|
||||
}));
|
||||
|
||||
describe('kittyProtocolDetector', () => {
|
||||
let originalStdin: NodeJS.ReadStream & { fd?: number };
|
||||
let originalStdout: NodeJS.WriteStream & { fd?: number };
|
||||
let stdinListeners: Record<string, (data: Buffer) => void> = {};
|
||||
|
||||
// Module functions
|
||||
let detectAndEnableKittyProtocol: typeof import('./kittyProtocolDetector.js').detectAndEnableKittyProtocol;
|
||||
let isKittyProtocolEnabled: typeof import('./kittyProtocolDetector.js').isKittyProtocolEnabled;
|
||||
let enableSupportedProtocol: typeof import('./kittyProtocolDetector.js').enableSupportedProtocol;
|
||||
|
||||
beforeEach(async () => {
|
||||
vi.resetModules();
|
||||
vi.resetAllMocks();
|
||||
vi.useFakeTimers();
|
||||
|
||||
const mod = await import('./kittyProtocolDetector.js');
|
||||
detectAndEnableKittyProtocol = mod.detectAndEnableKittyProtocol;
|
||||
isKittyProtocolEnabled = mod.isKittyProtocolEnabled;
|
||||
enableSupportedProtocol = mod.enableSupportedProtocol;
|
||||
|
||||
// Mock process.stdin and stdout
|
||||
originalStdin = process.stdin;
|
||||
originalStdout = process.stdout;
|
||||
|
||||
stdinListeners = {};
|
||||
|
||||
Object.defineProperty(process, 'stdin', {
|
||||
value: {
|
||||
isTTY: true,
|
||||
isRaw: false,
|
||||
setRawMode: vi.fn(),
|
||||
on: vi.fn((event, handler) => {
|
||||
stdinListeners[event] = handler;
|
||||
}),
|
||||
removeListener: vi.fn(),
|
||||
},
|
||||
configurable: true,
|
||||
});
|
||||
|
||||
Object.defineProperty(process, 'stdout', {
|
||||
value: {
|
||||
isTTY: true,
|
||||
fd: 1,
|
||||
},
|
||||
configurable: true,
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
Object.defineProperty(process, 'stdin', { value: originalStdin });
|
||||
Object.defineProperty(process, 'stdout', { value: originalStdout });
|
||||
vi.useRealTimers();
|
||||
});
|
||||
|
||||
it('should resolve immediately if not TTY', async () => {
|
||||
Object.defineProperty(process.stdin, 'isTTY', { value: false });
|
||||
await detectAndEnableKittyProtocol();
|
||||
expect(mocks.writeSync).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should enable protocol if response indicates support', async () => {
|
||||
const promise = detectAndEnableKittyProtocol();
|
||||
|
||||
// Simulate response
|
||||
expect(stdinListeners['data']).toBeDefined();
|
||||
|
||||
// Send progressive enhancement response
|
||||
stdinListeners['data'](Buffer.from('\x1b[?u'));
|
||||
|
||||
// Send device attributes response
|
||||
stdinListeners['data'](Buffer.from('\x1b[?c'));
|
||||
|
||||
await promise;
|
||||
|
||||
expect(mocks.enableKittyKeyboardProtocol).toHaveBeenCalled();
|
||||
expect(isKittyProtocolEnabled()).toBe(true);
|
||||
});
|
||||
|
||||
it('should not enable protocol if timeout occurs', async () => {
|
||||
const promise = detectAndEnableKittyProtocol();
|
||||
|
||||
// Fast forward time past timeout
|
||||
vi.advanceTimersByTime(300);
|
||||
|
||||
await promise;
|
||||
|
||||
expect(mocks.enableKittyKeyboardProtocol).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should wait longer if progressive enhancement received but not attributes', async () => {
|
||||
const promise = detectAndEnableKittyProtocol();
|
||||
|
||||
// Send progressive enhancement response
|
||||
stdinListeners['data'](Buffer.from('\x1b[?u'));
|
||||
|
||||
// Should not resolve yet
|
||||
vi.advanceTimersByTime(300); // Original timeout passed
|
||||
|
||||
// Send device attributes response late
|
||||
stdinListeners['data'](Buffer.from('\x1b[?c'));
|
||||
|
||||
await promise;
|
||||
|
||||
expect(mocks.enableKittyKeyboardProtocol).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should handle re-enabling protocol', async () => {
|
||||
// First, simulate successful detection to set kittySupported = true
|
||||
const promise = detectAndEnableKittyProtocol();
|
||||
stdinListeners['data'](Buffer.from('\x1b[?u'));
|
||||
stdinListeners['data'](Buffer.from('\x1b[?c'));
|
||||
await promise;
|
||||
|
||||
// Reset mocks to clear previous calls
|
||||
mocks.enableKittyKeyboardProtocol.mockClear();
|
||||
|
||||
// Now test re-enabling
|
||||
enableSupportedProtocol();
|
||||
expect(mocks.enableKittyKeyboardProtocol).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
161
packages/cli/src/ui/utils/terminalSetup.test.ts
Normal file
161
packages/cli/src/ui/utils/terminalSetup.test.ts
Normal file
@@ -0,0 +1,161 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2025 Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
||||
import { terminalSetup, VSCODE_SHIFT_ENTER_SEQUENCE } from './terminalSetup.js';
|
||||
|
||||
// Mock dependencies
|
||||
const mocks = vi.hoisted(() => ({
|
||||
exec: vi.fn(),
|
||||
mkdir: vi.fn(),
|
||||
readFile: vi.fn(),
|
||||
writeFile: vi.fn(),
|
||||
copyFile: vi.fn(),
|
||||
homedir: vi.fn(),
|
||||
platform: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock('node:child_process', () => ({
|
||||
exec: mocks.exec,
|
||||
execFile: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock('node:fs', () => ({
|
||||
promises: {
|
||||
mkdir: mocks.mkdir,
|
||||
readFile: mocks.readFile,
|
||||
writeFile: mocks.writeFile,
|
||||
copyFile: mocks.copyFile,
|
||||
},
|
||||
}));
|
||||
|
||||
vi.mock('node:os', () => ({
|
||||
homedir: mocks.homedir,
|
||||
platform: mocks.platform,
|
||||
}));
|
||||
|
||||
vi.mock('./kittyProtocolDetector.js', () => ({
|
||||
isKittyProtocolEnabled: vi.fn().mockReturnValue(false),
|
||||
}));
|
||||
|
||||
describe('terminalSetup', () => {
|
||||
const originalEnv = process.env;
|
||||
|
||||
beforeEach(() => {
|
||||
vi.resetAllMocks();
|
||||
process.env = { ...originalEnv };
|
||||
|
||||
// Default mocks
|
||||
mocks.homedir.mockReturnValue('/home/user');
|
||||
mocks.platform.mockReturnValue('darwin');
|
||||
mocks.mkdir.mockResolvedValue(undefined);
|
||||
mocks.copyFile.mockResolvedValue(undefined);
|
||||
mocks.exec.mockImplementation((cmd, cb) => cb(null, { stdout: '' }));
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
process.env = originalEnv;
|
||||
});
|
||||
|
||||
describe('detectTerminal', () => {
|
||||
it('should detect VS Code from env var', async () => {
|
||||
process.env['TERM_PROGRAM'] = 'vscode';
|
||||
const result = await terminalSetup();
|
||||
expect(result.message).toContain('VS Code');
|
||||
});
|
||||
|
||||
it('should detect Cursor from env var', async () => {
|
||||
process.env['CURSOR_TRACE_ID'] = 'some-id';
|
||||
const result = await terminalSetup();
|
||||
expect(result.message).toContain('Cursor');
|
||||
});
|
||||
|
||||
it('should detect Windsurf from env var', async () => {
|
||||
process.env['VSCODE_GIT_ASKPASS_MAIN'] = '/path/to/windsurf/askpass';
|
||||
const result = await terminalSetup();
|
||||
expect(result.message).toContain('Windsurf');
|
||||
});
|
||||
|
||||
it('should detect from parent process', async () => {
|
||||
mocks.platform.mockReturnValue('linux');
|
||||
mocks.exec.mockImplementation((cmd, cb) => {
|
||||
cb(null, { stdout: 'code\n' });
|
||||
});
|
||||
|
||||
const result = await terminalSetup();
|
||||
expect(result.message).toContain('VS Code');
|
||||
});
|
||||
});
|
||||
|
||||
describe('configureVSCodeStyle', () => {
|
||||
it('should create new keybindings file if none exists', async () => {
|
||||
process.env['TERM_PROGRAM'] = 'vscode';
|
||||
mocks.readFile.mockRejectedValue(new Error('ENOENT'));
|
||||
|
||||
const result = await terminalSetup();
|
||||
|
||||
expect(result.success).toBe(true);
|
||||
expect(mocks.writeFile).toHaveBeenCalled();
|
||||
|
||||
const writtenContent = JSON.parse(mocks.writeFile.mock.calls[0][1]);
|
||||
expect(writtenContent).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should append to existing keybindings', async () => {
|
||||
process.env['TERM_PROGRAM'] = 'vscode';
|
||||
mocks.readFile.mockResolvedValue('[]');
|
||||
|
||||
const result = await terminalSetup();
|
||||
|
||||
expect(result.success).toBe(true);
|
||||
const writtenContent = JSON.parse(mocks.writeFile.mock.calls[0][1]);
|
||||
expect(writtenContent).toHaveLength(2); // Shift+Enter and Ctrl+Enter
|
||||
});
|
||||
|
||||
it('should not modify if bindings already exist', async () => {
|
||||
process.env['TERM_PROGRAM'] = 'vscode';
|
||||
const existingBindings = [
|
||||
{
|
||||
key: 'shift+enter',
|
||||
command: 'workbench.action.terminal.sendSequence',
|
||||
args: { text: VSCODE_SHIFT_ENTER_SEQUENCE },
|
||||
},
|
||||
{
|
||||
key: 'ctrl+enter',
|
||||
command: 'workbench.action.terminal.sendSequence',
|
||||
args: { text: VSCODE_SHIFT_ENTER_SEQUENCE },
|
||||
},
|
||||
];
|
||||
mocks.readFile.mockResolvedValue(JSON.stringify(existingBindings));
|
||||
|
||||
const result = await terminalSetup();
|
||||
|
||||
expect(result.success).toBe(true);
|
||||
expect(mocks.writeFile).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should fail gracefully if json is invalid', async () => {
|
||||
process.env['TERM_PROGRAM'] = 'vscode';
|
||||
mocks.readFile.mockResolvedValue('{ invalid json');
|
||||
|
||||
const result = await terminalSetup();
|
||||
|
||||
expect(result.success).toBe(false);
|
||||
expect(result.message).toContain('invalid JSON');
|
||||
});
|
||||
|
||||
it('should handle comments in JSON', async () => {
|
||||
process.env['TERM_PROGRAM'] = 'vscode';
|
||||
const jsonWithComments = '// This is a comment\n[]';
|
||||
mocks.readFile.mockResolvedValue(jsonWithComments);
|
||||
|
||||
const result = await terminalSetup();
|
||||
|
||||
expect(result.success).toBe(true);
|
||||
expect(mocks.writeFile).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -204,35 +204,6 @@ async function configureVSCodeStyle(
|
||||
args: { text: VSCODE_SHIFT_ENTER_SEQUENCE },
|
||||
};
|
||||
|
||||
// Check if ANY shift+enter or ctrl+enter bindings already exist
|
||||
const existingShiftEnter = keybindings.find((kb) => {
|
||||
const binding = kb as { key?: string };
|
||||
return binding.key === 'shift+enter';
|
||||
});
|
||||
|
||||
const existingCtrlEnter = keybindings.find((kb) => {
|
||||
const binding = kb as { key?: string };
|
||||
return binding.key === 'ctrl+enter';
|
||||
});
|
||||
|
||||
if (existingShiftEnter || existingCtrlEnter) {
|
||||
const messages: string[] = [];
|
||||
if (existingShiftEnter) {
|
||||
messages.push(`- Shift+Enter binding already exists`);
|
||||
}
|
||||
if (existingCtrlEnter) {
|
||||
messages.push(`- Ctrl+Enter binding already exists`);
|
||||
}
|
||||
return {
|
||||
success: false,
|
||||
message:
|
||||
`Existing keybindings detected. Will not modify to avoid conflicts.\n` +
|
||||
messages.join('\n') +
|
||||
'\n' +
|
||||
`Please check and modify manually if needed: ${keybindingsFile}`,
|
||||
};
|
||||
}
|
||||
|
||||
// Check if our specific bindings already exist
|
||||
const hasOurShiftEnter = keybindings.some((kb) => {
|
||||
const binding = kb as {
|
||||
@@ -260,22 +231,55 @@ async function configureVSCodeStyle(
|
||||
);
|
||||
});
|
||||
|
||||
if (!hasOurShiftEnter || !hasOurCtrlEnter) {
|
||||
if (!hasOurShiftEnter) keybindings.unshift(shiftEnterBinding);
|
||||
if (!hasOurCtrlEnter) keybindings.unshift(ctrlEnterBinding);
|
||||
|
||||
await fs.writeFile(keybindingsFile, JSON.stringify(keybindings, null, 4));
|
||||
return {
|
||||
success: true,
|
||||
message: `Added Shift+Enter and Ctrl+Enter keybindings to ${terminalName}.\nModified: ${keybindingsFile}`,
|
||||
requiresRestart: true,
|
||||
};
|
||||
} else {
|
||||
if (hasOurShiftEnter && hasOurCtrlEnter) {
|
||||
return {
|
||||
success: true,
|
||||
message: `${terminalName} keybindings already configured.`,
|
||||
};
|
||||
}
|
||||
|
||||
// Check if ANY shift+enter or ctrl+enter bindings already exist (that are NOT ours)
|
||||
const existingShiftEnter = keybindings.find((kb) => {
|
||||
const binding = kb as { key?: string };
|
||||
return binding.key === 'shift+enter';
|
||||
});
|
||||
|
||||
const existingCtrlEnter = keybindings.find((kb) => {
|
||||
const binding = kb as { key?: string };
|
||||
return binding.key === 'ctrl+enter';
|
||||
});
|
||||
|
||||
if (existingShiftEnter || existingCtrlEnter) {
|
||||
const messages: string[] = [];
|
||||
// Only report conflict if it's not our binding (though we checked above, partial matches might exist)
|
||||
if (existingShiftEnter && !hasOurShiftEnter) {
|
||||
messages.push(`- Shift+Enter binding already exists`);
|
||||
}
|
||||
if (existingCtrlEnter && !hasOurCtrlEnter) {
|
||||
messages.push(`- Ctrl+Enter binding already exists`);
|
||||
}
|
||||
|
||||
if (messages.length > 0) {
|
||||
return {
|
||||
success: false,
|
||||
message:
|
||||
`Existing keybindings detected. Will not modify to avoid conflicts.\n` +
|
||||
messages.join('\n') +
|
||||
'\n' +
|
||||
`Please check and modify manually if needed: ${keybindingsFile}`,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
if (!hasOurShiftEnter) keybindings.unshift(shiftEnterBinding);
|
||||
if (!hasOurCtrlEnter) keybindings.unshift(ctrlEnterBinding);
|
||||
|
||||
await fs.writeFile(keybindingsFile, JSON.stringify(keybindings, null, 4));
|
||||
return {
|
||||
success: true,
|
||||
message: `Added Shift+Enter and Ctrl+Enter keybindings to ${terminalName}.\nModified: ${keybindingsFile}`,
|
||||
requiresRestart: true,
|
||||
};
|
||||
} catch (error) {
|
||||
return {
|
||||
success: false,
|
||||
|
||||
71
packages/cli/src/ui/utils/ui-sizing.test.ts
Normal file
71
packages/cli/src/ui/utils/ui-sizing.test.ts
Normal file
@@ -0,0 +1,71 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2025 Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import { describe, it, expect, vi } from 'vitest';
|
||||
import { calculateMainAreaWidth } from './ui-sizing.js';
|
||||
import { type LoadedSettings } from '../../config/settings.js';
|
||||
|
||||
// Mock dependencies
|
||||
const mocks = vi.hoisted(() => ({
|
||||
isAlternateBufferEnabled: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock('../hooks/useAlternateBuffer.js', () => ({
|
||||
isAlternateBufferEnabled: mocks.isAlternateBufferEnabled,
|
||||
}));
|
||||
|
||||
describe('ui-sizing', () => {
|
||||
const createSettings = (useFullWidth?: boolean): LoadedSettings =>
|
||||
({
|
||||
merged: {
|
||||
ui: {
|
||||
useFullWidth,
|
||||
},
|
||||
},
|
||||
}) as unknown as LoadedSettings;
|
||||
|
||||
describe('calculateMainAreaWidth', () => {
|
||||
it.each([
|
||||
// width, useFullWidth, alternateBuffer, expected
|
||||
[80, true, false, 80],
|
||||
[100, true, false, 100],
|
||||
[80, true, true, 79], // -1 for alternate buffer
|
||||
[100, true, true, 99],
|
||||
|
||||
// Default behavior (useFullWidth undefined or true)
|
||||
[100, undefined, false, 100],
|
||||
|
||||
// useFullWidth: false (Smart sizing)
|
||||
[80, false, false, 78], // 98% of 80
|
||||
[132, false, false, 119], // 90% of 132
|
||||
[200, false, false, 180], // 90% of 200 (>= 132)
|
||||
|
||||
// Interpolation check
|
||||
[106, false, false, 100], // Approx middle
|
||||
])(
|
||||
'should return %i when width=%i, useFullWidth=%s, altBuffer=%s',
|
||||
(width, useFullWidth, altBuffer, expected) => {
|
||||
mocks.isAlternateBufferEnabled.mockReturnValue(altBuffer);
|
||||
const settings = createSettings(useFullWidth);
|
||||
|
||||
expect(calculateMainAreaWidth(width, settings)).toBe(expected);
|
||||
},
|
||||
);
|
||||
|
||||
it('should match snapshot for interpolation range', () => {
|
||||
mocks.isAlternateBufferEnabled.mockReturnValue(false);
|
||||
const settings = createSettings(false);
|
||||
|
||||
const results: Record<number, number> = {};
|
||||
// Test range from 80 to 132
|
||||
for (let w = 80; w <= 132; w += 4) {
|
||||
results[w] = calculateMainAreaWidth(w, settings);
|
||||
}
|
||||
|
||||
expect(results).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user