mirror of
https://github.com/jarrodwatts/claude-hud.git
synced 2026-05-01 09:32:39 +00:00
* fix: use percentage-based autocompact buffer with config toggle
- Change hardcoded 45k buffer to 22.5% of context window
- Scales correctly for enterprise windows (>200k)
- Add `display.autocompactBuffer` config option ('enabled' | 'disabled')
- Default 'enabled' preserves current behavior (buffered %)
- 'disabled' shows raw % for users with autocompact off
Fixes #48, #16
Related: #4, #30, #43, #49
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* test: add autocompactBuffer tests for renderSessionLine
Address review feedback:
- Add tests verifying autocompactBuffer: 'enabled' uses buffered %
- Add tests verifying autocompactBuffer: 'disabled' uses raw %
- Add autocompactBuffer check to loadConfig test
- Update baseContext() to include autocompactBuffer
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* docs: add clickable GitHub URLs to CHANGELOG credits
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* feat: add debug log for autocompactBuffer mode
When DEBUG=claude-hud is set and autocompactBuffer='disabled',
logs both raw and buffered percentages to help troubleshoot mismatches.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
202 lines
7.1 KiB
JavaScript
202 lines
7.1 KiB
JavaScript
import { test, describe, beforeEach, afterEach } from 'node:test';
|
|
import assert from 'node:assert/strict';
|
|
import { loadConfig, DEFAULT_CONFIG, getConfigPath } from '../dist/config.js';
|
|
import * as path from 'node:path';
|
|
import * as os from 'node:os';
|
|
import * as fs from 'node:fs';
|
|
|
|
// Helper to create a temporary config for testing
|
|
function createTempConfig(configData) {
|
|
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'claude-hud-test-'));
|
|
const configDir = path.join(tempDir, '.claude', 'plugins', 'claude-hud');
|
|
fs.mkdirSync(configDir, { recursive: true });
|
|
const configPath = path.join(configDir, 'config.json');
|
|
fs.writeFileSync(configPath, JSON.stringify(configData));
|
|
return { tempDir, configPath, cleanup: () => fs.rmSync(tempDir, { recursive: true, force: true }) };
|
|
}
|
|
|
|
test('loadConfig returns valid config structure', async () => {
|
|
const config = await loadConfig();
|
|
// Should return valid pathLevels (1, 2, or 3)
|
|
assert.ok([1, 2, 3].includes(config.pathLevels), 'pathLevels should be 1, 2, or 3');
|
|
// Should have gitStatus object with expected properties
|
|
assert.equal(typeof config.gitStatus, 'object');
|
|
assert.equal(typeof config.gitStatus.enabled, 'boolean');
|
|
assert.equal(typeof config.gitStatus.showDirty, 'boolean');
|
|
assert.equal(typeof config.gitStatus.showAheadBehind, 'boolean');
|
|
// Should have display object with expected properties
|
|
assert.equal(typeof config.display, 'object');
|
|
assert.equal(typeof config.display.showModel, 'boolean');
|
|
assert.equal(typeof config.display.showContextBar, 'boolean');
|
|
});
|
|
|
|
test('getConfigPath returns correct path', () => {
|
|
const configPath = getConfigPath();
|
|
const homeDir = os.homedir();
|
|
assert.equal(configPath, path.join(homeDir, '.claude', 'plugins', 'claude-hud', 'config.json'));
|
|
});
|
|
|
|
test('DEFAULT_CONFIG has correct structure', () => {
|
|
assert.equal(DEFAULT_CONFIG.pathLevels, 1);
|
|
assert.equal(typeof DEFAULT_CONFIG.gitStatus, 'object');
|
|
assert.equal(DEFAULT_CONFIG.gitStatus.enabled, true);
|
|
});
|
|
|
|
test('pathLevels can be 1, 2, or 3', () => {
|
|
assert.ok([1, 2, 3].includes(DEFAULT_CONFIG.pathLevels));
|
|
});
|
|
|
|
// DEFAULT_CONFIG comprehensive tests
|
|
describe('DEFAULT_CONFIG', () => {
|
|
test('has layout set to default', () => {
|
|
assert.equal(DEFAULT_CONFIG.layout, 'default');
|
|
});
|
|
|
|
test('has all gitStatus properties', () => {
|
|
assert.equal(DEFAULT_CONFIG.gitStatus.enabled, true);
|
|
assert.equal(DEFAULT_CONFIG.gitStatus.showDirty, true);
|
|
assert.equal(DEFAULT_CONFIG.gitStatus.showAheadBehind, false);
|
|
});
|
|
|
|
test('has all display properties', () => {
|
|
assert.equal(DEFAULT_CONFIG.display.showModel, true);
|
|
assert.equal(DEFAULT_CONFIG.display.showContextBar, true);
|
|
assert.equal(DEFAULT_CONFIG.display.showConfigCounts, true);
|
|
assert.equal(DEFAULT_CONFIG.display.showDuration, true);
|
|
assert.equal(DEFAULT_CONFIG.display.showTokenBreakdown, true);
|
|
assert.equal(DEFAULT_CONFIG.display.showTools, true);
|
|
assert.equal(DEFAULT_CONFIG.display.showAgents, true);
|
|
assert.equal(DEFAULT_CONFIG.display.showTodos, true);
|
|
});
|
|
});
|
|
|
|
// Layout validation tests
|
|
describe('layout validation', () => {
|
|
test('accepts default layout', () => {
|
|
assert.equal(DEFAULT_CONFIG.layout, 'default');
|
|
});
|
|
|
|
test('layout must be one of valid values', () => {
|
|
const validLayouts = ['default', 'condensed', 'separators'];
|
|
assert.ok(validLayouts.includes(DEFAULT_CONFIG.layout));
|
|
});
|
|
});
|
|
|
|
// PathLevels validation tests
|
|
describe('pathLevels validation', () => {
|
|
test('pathLevels is 1 by default', () => {
|
|
assert.equal(DEFAULT_CONFIG.pathLevels, 1);
|
|
});
|
|
|
|
test('pathLevels must be 1, 2, or 3', () => {
|
|
const validLevels = [1, 2, 3];
|
|
assert.ok(validLevels.includes(DEFAULT_CONFIG.pathLevels));
|
|
});
|
|
});
|
|
|
|
// Git status configuration tests
|
|
describe('gitStatus configuration', () => {
|
|
test('enabled is true by default', () => {
|
|
assert.equal(DEFAULT_CONFIG.gitStatus.enabled, true);
|
|
});
|
|
|
|
test('showDirty is true by default', () => {
|
|
assert.equal(DEFAULT_CONFIG.gitStatus.showDirty, true);
|
|
});
|
|
|
|
test('showAheadBehind is false by default', () => {
|
|
assert.equal(DEFAULT_CONFIG.gitStatus.showAheadBehind, false);
|
|
});
|
|
});
|
|
|
|
// Display configuration tests
|
|
describe('display configuration', () => {
|
|
test('all boolean display options are booleans', () => {
|
|
const { autocompactBuffer, ...booleanOptions } = DEFAULT_CONFIG.display;
|
|
Object.values(booleanOptions).forEach(value => {
|
|
assert.equal(typeof value, 'boolean');
|
|
});
|
|
});
|
|
|
|
test('all boolean display options default to true', () => {
|
|
const { autocompactBuffer, ...booleanOptions } = DEFAULT_CONFIG.display;
|
|
Object.values(booleanOptions).forEach(value => {
|
|
assert.equal(value, true);
|
|
});
|
|
});
|
|
|
|
test('autocompactBuffer defaults to enabled', () => {
|
|
assert.equal(DEFAULT_CONFIG.display.autocompactBuffer, 'enabled');
|
|
});
|
|
|
|
test('has exactly 10 display options', () => {
|
|
const optionCount = Object.keys(DEFAULT_CONFIG.display).length;
|
|
assert.equal(optionCount, 10);
|
|
});
|
|
});
|
|
|
|
// Config loading behavior tests
|
|
describe('loadConfig behavior', () => {
|
|
test('returns complete config with all required fields', async () => {
|
|
const config = await loadConfig();
|
|
|
|
// Check top-level fields exist
|
|
assert.ok('layout' in config);
|
|
assert.ok('pathLevels' in config);
|
|
assert.ok('gitStatus' in config);
|
|
assert.ok('display' in config);
|
|
|
|
// Check gitStatus fields
|
|
assert.ok('enabled' in config.gitStatus);
|
|
assert.ok('showDirty' in config.gitStatus);
|
|
assert.ok('showAheadBehind' in config.gitStatus);
|
|
|
|
// Check display fields
|
|
assert.ok('showModel' in config.display);
|
|
assert.ok('showContextBar' in config.display);
|
|
assert.ok('showConfigCounts' in config.display);
|
|
assert.ok('showDuration' in config.display);
|
|
assert.ok('showTokenBreakdown' in config.display);
|
|
assert.ok('showUsage' in config.display);
|
|
assert.ok('autocompactBuffer' in config.display);
|
|
assert.ok('showTools' in config.display);
|
|
assert.ok('showAgents' in config.display);
|
|
assert.ok('showTodos' in config.display);
|
|
});
|
|
|
|
test('returns valid layout value', async () => {
|
|
const config = await loadConfig();
|
|
const validLayouts = ['default', 'condensed', 'separators'];
|
|
assert.ok(validLayouts.includes(config.layout));
|
|
});
|
|
|
|
test('returns valid pathLevels value', async () => {
|
|
const config = await loadConfig();
|
|
assert.ok([1, 2, 3].includes(config.pathLevels));
|
|
});
|
|
});
|
|
|
|
// Config path tests
|
|
describe('getConfigPath', () => {
|
|
test('returns path under home directory', () => {
|
|
const configPath = getConfigPath();
|
|
const homeDir = os.homedir();
|
|
assert.ok(configPath.startsWith(homeDir));
|
|
});
|
|
|
|
test('returns path with .claude directory', () => {
|
|
const configPath = getConfigPath();
|
|
assert.ok(configPath.includes('.claude'));
|
|
});
|
|
|
|
test('returns path with plugins/claude-hud', () => {
|
|
const configPath = getConfigPath();
|
|
assert.ok(configPath.includes(path.join('plugins', 'claude-hud')));
|
|
});
|
|
|
|
test('returns path ending with config.json', () => {
|
|
const configPath = getConfigPath();
|
|
assert.ok(configPath.endsWith('config.json'));
|
|
});
|
|
});
|