Files
OpenTerminal/tests/hl-split-smoke.mjs
T
Bill e08e1cfec4 feat(highlight): preset overhaul, categories, stats, theme colours, per-host profiles
Rules & engine:
- 22 presets (was 11): split status into okstate/warnstate/badstate, add delop,
  createop, danger, secret, level, exitcode, percent, http; status words are
  case-insensitive and cover the ✓ ✔ ✅ ✗ ✘ ✖ ❌ ⚠ symbol set
- value bands: the first number in a match picks the colour
  (percent: <20% red / 20-50% yellow / 50-80% light green / >=80% green)
- optional per-rule `caseInsensitive`, `category`, `bands`; load-time
  `refreshBuiltinRules` upgrades untouched built-in patterns in place

Settings page:
- three-way master switch `highlightMode` (all / basic / off); `basic` runs only
  the five safety+status rules and `off` empties the rule set rather than
  bypassing HighlightStream (which would drop the held tail)
- category column + grouping (`highlightGroupByCategory`), per-rule hit/duration
  stats (`highlightStats`, opt-in sink, snapshot once a second), theme-following
  colours (`highlightThemeColors`, hue-bucket mapping onto the ANSI palette),
  import/export JSON envelope, live preview through the real engine
- named rule subsets bound per host (`highlightPerHost` + `highlightProfiles`
  + `SshConnection.highlightProfileId`); empty ruleIds = every rule

Tests: new tests/hl-rules.mjs (word boundaries, case flag, negative words,
bands, import/export, preview, basic mode, categories, stats, theme colours,
profiles) + profile round-trip in tests/settings-store.mjs
2026-09-23 11:14:31 +08:00

61 lines
2.2 KiB
JavaScript

// Split-chunk regression test for HighlightStream.
// Builds the engine + TerminalView-free stream, feeds a kimi-like banner in
// deliberately nasty chunk splits, and requires byte-identical output vs the
// single-chunk reference.
import { compileRules, applyHighlights, HighlightStream } from '../src/renderer/src/terminal/highlightEngine.ts'
import { DEFAULT_HIGHLIGHT_RULES } from '../src/shared/settings.ts'
const rules = compileRules(DEFAULT_HIGHLIGHT_RULES)
// kimi-like: truecolor SGR, OSC title, box drawing, per-line repaints
const banner =
'\x1b]0;kimi — session\x07' +
'\x1b[38;2;79;168;255m╭───────╮\x1b[0m\r\n' +
'\x1b[38;2;136;136;136mWelcome to Kimi Code!\x1b[0m \x1b[38;2;79;168;255m│\x1b[0m send /help\r\n' +
'\x1b[38;2;224;224;224mModel: MiniMax-M3 0.41.038 ERROR 99% https://example.com/x\x1b[0m\r\n'
const reference = applyHighlights(banner, rules)
// every possible single split point + a few double splits
let failed = 0
const splitAt = (s, i) => [s.slice(0, i), s.slice(i)]
for (let i = 1; i < banner.length - 1; i++) {
const [a, b] = splitAt(banner, i)
const stream = new HighlightStream(rules)
const out = stream.push(a) + stream.push(b) + stream.flush()
if (out !== reference) {
failed++
if (failed <= 3) {
console.log(`MISMATCH at split ${i}:`)
console.log(' got: ' + JSON.stringify(out.slice(0, 120)))
console.log(' expected: ' + JSON.stringify(reference.slice(0, 120)))
}
}
}
// double splits at 1/3 and 2/3, plus a pathological 1-byte-per-push feed
{
const i = Math.floor(banner.length / 3)
const j = Math.floor((banner.length * 2) / 3)
const stream = new HighlightStream(rules)
const out = stream.push(banner.slice(0, i)) + stream.push(banner.slice(i, j)) + stream.push(banner.slice(j)) + stream.flush()
if (out !== reference) failed++
}
{
const stream = new HighlightStream(rules)
let out = ''
for (const ch of banner) out += stream.push(ch)
out += stream.flush()
if (out !== reference) failed++
}
if (failed === 0) {
console.log('SPLIT-SMOKE PASS: all split points byte-identical to single-chunk reference')
process.exit(0)
} else {
console.log(`SPLIT-SMOKE FAIL: ${failed} mismatches`)
process.exit(1)
}