Files
claude-hud/tests/terminal.test.js
Anna Terek e5d384af35 fix: scale progress bars based on terminal width (#237)
* fix: scale progress bars based on terminal width

Resolves #231

Progress bars (coloredBar/quotaBar) were hardcoded to width 10,
causing line wrapping in narrow terminals.

Now bars scale dynamically based on process.stdout.columns:
- Wide terminal (>=100 cols): width 10 (default)
- Medium terminal (60-99 cols): width 6
- Narrow terminal (<60 cols): width 4

Affected files:
- src/render/session-line.ts (context bar + usage bars)
- src/render/lines/identity.ts (context bar in expanded layout)
- src/render/lines/usage.ts (quota bars in expanded layout)

* fix: also check COLUMNS env var as fallback for bar width

For cross-platform compatibility (zsh, macOS, Windows),
getBarWidth() now also checks process.env.COLUMNS when
process.stdout.columns is unavailable (non-TTY, piped output).

* refactor: extract getAdaptiveBarWidth to shared utility

- Remove duplicated getBarWidth() from 3 render files
- Add src/utils/terminal.ts with exported getAdaptiveBarWidth()
- Add Math.floor() for consistency with getTerminalWidth()
- All 3 render files now import from shared utility (DRY)

* test: add unit tests for getAdaptiveBarWidth

Tests cover:
- Narrow terminal (<60 cols) → 4
- Medium terminal (60-99 cols) → 6
- Wide terminal (>=100 cols) → 10
- Boundary values (59, 60, 99, 100)
- Non-TTY/piped output (undefined columns) → 10 (default)
- Fallback to COLUMNS env var
- No terminal info at all → 10 (default)

* style: simplify JSDoc to match project comment style

---------

Co-authored-by: Anna Terek <wabalabudabdab@users.noreply.github.com>
2026-03-20 11:41:40 +11:00

80 lines
2.8 KiB
JavaScript

import { test, describe, beforeEach, afterEach } from 'node:test';
import assert from 'node:assert/strict';
import { getAdaptiveBarWidth } from '../dist/utils/terminal.js';
describe('getAdaptiveBarWidth', () => {
let originalColumns;
let originalEnvColumns;
beforeEach(() => {
originalColumns = Object.getOwnPropertyDescriptor(process.stdout, 'columns');
originalEnvColumns = process.env.COLUMNS;
delete process.env.COLUMNS;
});
afterEach(() => {
if (originalColumns) {
Object.defineProperty(process.stdout, 'columns', originalColumns);
} else {
delete process.stdout.columns;
}
if (originalEnvColumns !== undefined) {
process.env.COLUMNS = originalEnvColumns;
} else {
delete process.env.COLUMNS;
}
});
test('returns 4 for narrow terminal (<60 cols)', () => {
Object.defineProperty(process.stdout, 'columns', { value: 40, configurable: true });
assert.equal(getAdaptiveBarWidth(), 4);
});
test('returns 4 for exactly 59 cols', () => {
Object.defineProperty(process.stdout, 'columns', { value: 59, configurable: true });
assert.equal(getAdaptiveBarWidth(), 4);
});
test('returns 6 for medium terminal (60-99 cols)', () => {
Object.defineProperty(process.stdout, 'columns', { value: 70, configurable: true });
assert.equal(getAdaptiveBarWidth(), 6);
});
test('returns 6 for exactly 60 cols', () => {
Object.defineProperty(process.stdout, 'columns', { value: 60, configurable: true });
assert.equal(getAdaptiveBarWidth(), 6);
});
test('returns 6 for exactly 99 cols', () => {
Object.defineProperty(process.stdout, 'columns', { value: 99, configurable: true });
assert.equal(getAdaptiveBarWidth(), 6);
});
test('returns 10 for wide terminal (>=100 cols)', () => {
Object.defineProperty(process.stdout, 'columns', { value: 120, configurable: true });
assert.equal(getAdaptiveBarWidth(), 10);
});
test('returns 10 for exactly 100 cols', () => {
Object.defineProperty(process.stdout, 'columns', { value: 100, configurable: true });
assert.equal(getAdaptiveBarWidth(), 10);
});
test('returns 10 when stdout.columns is undefined (non-TTY/piped)', () => {
Object.defineProperty(process.stdout, 'columns', { value: undefined, configurable: true });
assert.equal(getAdaptiveBarWidth(), 10);
});
test('falls back to COLUMNS env var when stdout.columns unavailable', () => {
Object.defineProperty(process.stdout, 'columns', { value: undefined, configurable: true });
process.env.COLUMNS = '70';
assert.equal(getAdaptiveBarWidth(), 6);
});
test('returns 10 when both stdout.columns and COLUMNS are unavailable', () => {
Object.defineProperty(process.stdout, 'columns', { value: undefined, configurable: true });
delete process.env.COLUMNS;
assert.equal(getAdaptiveBarWidth(), 10);
});
});