183 lines
6.9 KiB
JavaScript
183 lines
6.9 KiB
JavaScript
import { test } from 'node:test';
|
|
import assert from 'node:assert/strict';
|
|
import { renderIdentityLine } from '../dist/render/lines/identity.js';
|
|
|
|
function stripAnsi(str) {
|
|
return str
|
|
.replace(/\x1b\[[0-9;]*m/g, '')
|
|
.replace(/\x1b\][^\x07\x1b]*(?:\x07|\x1b\\)/g, '');
|
|
}
|
|
|
|
function baseContext() {
|
|
return {
|
|
stdin: {
|
|
model: { display_name: 'Opus' },
|
|
context_window: {
|
|
context_window_size: 200000,
|
|
current_usage: {
|
|
input_tokens: 10000,
|
|
cache_creation_input_tokens: 0,
|
|
cache_read_input_tokens: 0,
|
|
},
|
|
},
|
|
},
|
|
transcript: { tools: [], skills: [], mcpServers: [], agents: [], todos: [], sessionTokens: undefined },
|
|
claudeMdCount: 0,
|
|
rulesCount: 0,
|
|
mcpCount: 0,
|
|
hooksCount: 0,
|
|
sessionDuration: '',
|
|
gitStatus: null,
|
|
usageData: null,
|
|
memoryUsage: null,
|
|
config: {
|
|
lineLayout: 'compact',
|
|
showSeparators: false,
|
|
pathLevels: 1,
|
|
elementOrder: ['project', 'context', 'usage'],
|
|
gitStatus: { enabled: true, showDirty: true, showAheadBehind: false, showFileStats: false, branchOverflow: 'truncate', pushWarningThreshold: 0, pushCriticalThreshold: 0 },
|
|
display: { showModel: true, showProject: true, showContextBar: true, contextValue: 'percent', showConfigCounts: true, showCost: false, showDuration: true, showSpeed: false, showTokenBreakdown: true, showUsage: true, usageValue: 'percent', usageBarEnabled: false, showResetLabel: true, showTools: true, showSkills: false, showMcp: false, showAgents: true, showTodos: true, showSessionTokens: false, showSessionName: false, showClaudeCodeVersion: false, showMemoryUsage: false, showPromptCache: false, promptCacheTtlSeconds: 300, showOutputStyle: false, mergeGroups: [['context', 'usage']], autocompactBuffer: 'enabled', usageThreshold: 0, sevenDayThreshold: 80, environmentThreshold: 0, customLine: '' },
|
|
colors: {
|
|
context: 'green',
|
|
usage: 'brightBlue',
|
|
warning: 'yellow',
|
|
usageWarning: 'brightMagenta',
|
|
critical: 'red',
|
|
model: 'cyan',
|
|
project: 'yellow',
|
|
git: 'magenta',
|
|
gitBranch: 'cyan',
|
|
label: 'dim',
|
|
custom: 208,
|
|
},
|
|
},
|
|
};
|
|
}
|
|
|
|
test('renderIdentityLine shows percent by default', () => {
|
|
const ctx = baseContext();
|
|
const line = stripAnsi(renderIdentityLine(ctx));
|
|
assert.ok(line.includes('Context'));
|
|
assert.ok(line.includes('5%'));
|
|
});
|
|
|
|
test('renderIdentityLine shows context in tokens mode', () => {
|
|
const ctx = baseContext();
|
|
ctx.config.display.contextValue = 'tokens';
|
|
const line = stripAnsi(renderIdentityLine(ctx));
|
|
assert.ok(line.includes('10k/200k'));
|
|
});
|
|
|
|
test('renderIdentityLine shows context in both mode', () => {
|
|
const ctx = baseContext();
|
|
ctx.config.display.contextValue = 'both';
|
|
const line = stripAnsi(renderIdentityLine(ctx));
|
|
assert.ok(line.includes('5%'));
|
|
assert.ok(line.includes('10k/200k'));
|
|
});
|
|
|
|
test('renderIdentityLine shows context in remaining mode', () => {
|
|
const ctx = baseContext();
|
|
ctx.config.display.contextValue = 'remaining';
|
|
const line = stripAnsi(renderIdentityLine(ctx));
|
|
assert.ok(line.includes('95%'));
|
|
});
|
|
|
|
test('renderIdentityLine shows tokens mode with no context_window_size', () => {
|
|
const ctx = baseContext();
|
|
ctx.config.display.contextValue = 'tokens';
|
|
ctx.stdin.context_window.context_window_size = 0;
|
|
const line = stripAnsi(renderIdentityLine(ctx));
|
|
assert.ok(line.includes('10k'));
|
|
assert.ok(!line.includes('/'));
|
|
});
|
|
|
|
test('renderIdentityLine shows both mode with no context_window_size', () => {
|
|
const ctx = baseContext();
|
|
ctx.config.display.contextValue = 'both';
|
|
ctx.stdin.context_window.context_window_size = 0;
|
|
const line = stripAnsi(renderIdentityLine(ctx));
|
|
// Without a window size, both mode just shows percent
|
|
assert.match(line, /\d+%/);
|
|
});
|
|
|
|
test('renderIdentityLine hides context bar when showContextBar is false', () => {
|
|
const ctx = baseContext();
|
|
ctx.config.display.showContextBar = false;
|
|
const line = stripAnsi(renderIdentityLine(ctx));
|
|
assert.ok(line.includes('Context'));
|
|
// Should not have a bar (█ characters)
|
|
assert.ok(!line.includes('█'));
|
|
});
|
|
|
|
test('renderIdentityLine shows token breakdown when percent >= critical threshold', () => {
|
|
const ctx = baseContext();
|
|
ctx.stdin.context_window.current_usage.input_tokens = 180000;
|
|
ctx.stdin.context_window.current_usage.cache_creation_input_tokens = 5000;
|
|
ctx.stdin.context_window.current_usage.cache_read_input_tokens = 2000;
|
|
const line = stripAnsi(renderIdentityLine(ctx));
|
|
assert.ok(line.includes('in:'));
|
|
assert.ok(line.includes('cache:'));
|
|
});
|
|
|
|
test('renderIdentityLine hides token breakdown when showTokenBreakdown is false', () => {
|
|
const ctx = baseContext();
|
|
ctx.stdin.context_window.current_usage.input_tokens = 180000;
|
|
ctx.config.display.showTokenBreakdown = false;
|
|
const line = stripAnsi(renderIdentityLine(ctx));
|
|
assert.ok(!line.includes('in:'));
|
|
});
|
|
|
|
test('renderIdentityLine respects custom contextCriticalThreshold', () => {
|
|
const ctx = baseContext();
|
|
// 10k/200k = 5%, set threshold to 5 to trigger breakdown
|
|
ctx.config.display.contextCriticalThreshold = 5;
|
|
const line = stripAnsi(renderIdentityLine(ctx));
|
|
assert.ok(line.includes('in:'));
|
|
});
|
|
|
|
test('renderIdentityLine uses autoCompactWindow for token display', () => {
|
|
const ctx = baseContext();
|
|
ctx.config.display.autoCompactWindow = 100000;
|
|
ctx.config.display.contextValue = 'tokens';
|
|
const line = stripAnsi(renderIdentityLine(ctx));
|
|
// Should show tokens relative to autoCompactWindow (100k) not full window (200k)
|
|
assert.ok(line.includes('10k/100k'));
|
|
});
|
|
|
|
test('renderIdentityLine supports alignLabels parameter', () => {
|
|
const ctx = baseContext();
|
|
const lineNoAlign = stripAnsi(renderIdentityLine(ctx, false));
|
|
const lineAlign = stripAnsi(renderIdentityLine(ctx, true));
|
|
// Both should contain Context label
|
|
assert.ok(lineNoAlign.includes('Context'));
|
|
assert.ok(lineAlign.includes('Context'));
|
|
});
|
|
|
|
test('renderIdentityLine disables autocompact buffer when set to disabled', () => {
|
|
const ctx = baseContext();
|
|
ctx.config.display.autocompactBuffer = 'disabled';
|
|
const line = stripAnsi(renderIdentityLine(ctx));
|
|
assert.ok(line.includes('Context'));
|
|
// Should show raw percent
|
|
assert.ok(line.includes('5%'));
|
|
});
|
|
|
|
test('renderIdentityLine formats million-scale tokens correctly', () => {
|
|
const ctx = baseContext();
|
|
ctx.stdin.context_window.context_window_size = 1000000;
|
|
ctx.stdin.context_window.current_usage.input_tokens = 900000;
|
|
ctx.config.display.contextCriticalThreshold = 85;
|
|
ctx.config.display.contextValue = 'tokens';
|
|
const line = stripAnsi(renderIdentityLine(ctx));
|
|
assert.ok(line.includes('900k/1.0M'));
|
|
});
|
|
|
|
test('renderIdentityLine does not show breakdown when no usage data', () => {
|
|
const ctx = baseContext();
|
|
ctx.stdin.context_window.current_usage = null;
|
|
ctx.stdin.context_window.context_window_size = 200000;
|
|
const line = stripAnsi(renderIdentityLine(ctx));
|
|
assert.ok(line.includes('Context'));
|
|
});
|