CoolFace
Datasetpublic

basant307/AI_Governance_Project

sourceHugging Faceapache-2.0updated 2mo agoView on Hugging Face
0likes45downloads
osc.test.ts281 linesDownload Raw Back to utils
1/**2 * @license3 * Copyright 2026 Qwen Team4 * SPDX-License-Identifier: Apache-2.05 */6 7import { describe, it, expect, beforeEach, afterEach } from 'vitest';8import {9  detectTerminal,10  osc,11  wrapForMultiplexer,12  oscITerm2Notify,13  oscKittyNotify,14  oscGhosttyNotify,15  sanitizeOscPayload,16  encodeKittyPayload,17  OSC,18  BEL,19  ST,20} from './osc.js';21 22describe('detectTerminal', () => {23  const originalEnv = { ...process.env };24 25  beforeEach(() => {26    delete process.env['TERM_PROGRAM'];27    delete process.env['TERM'];28    delete process.env['KITTY_WINDOW_ID'];29  });30 31  afterEach(() => {32    process.env = { ...originalEnv };33  });34 35  it('detects iTerm.app via TERM_PROGRAM', () => {36    process.env['TERM_PROGRAM'] = 'iTerm.app';37    expect(detectTerminal()).toBe('iTerm.app');38  });39 40  it('detects kitty via TERM_PROGRAM', () => {41    process.env['TERM_PROGRAM'] = 'kitty';42    expect(detectTerminal()).toBe('kitty');43  });44 45  it('detects ghostty via TERM_PROGRAM', () => {46    process.env['TERM_PROGRAM'] = 'ghostty';47    expect(detectTerminal()).toBe('ghostty');48  });49 50  it('detects Apple_Terminal via TERM_PROGRAM', () => {51    process.env['TERM_PROGRAM'] = 'Apple_Terminal';52    expect(detectTerminal()).toBe('Apple_Terminal');53  });54 55  it('detects kitty via TERM=xterm-kitty when TERM_PROGRAM is absent', () => {56    process.env['TERM'] = 'xterm-kitty';57    expect(detectTerminal()).toBe('kitty');58  });59 60  it('detects ghostty via TERM=xterm-ghostty when TERM_PROGRAM is absent', () => {61    process.env['TERM'] = 'xterm-ghostty';62    expect(detectTerminal()).toBe('ghostty');63  });64 65  it('detects kitty via KITTY_WINDOW_ID as fallback', () => {66    process.env['KITTY_WINDOW_ID'] = '1';67    expect(detectTerminal()).toBe('kitty');68  });69 70  it('returns unknown when no terminal is detected', () => {71    expect(detectTerminal()).toBe('unknown');72  });73 74  it('TERM_PROGRAM takes priority over TERM', () => {75    process.env['TERM'] = 'xterm-ghostty';76    process.env['TERM_PROGRAM'] = 'iTerm.app';77    expect(detectTerminal()).toBe('iTerm.app');78  });79 80  it('falls back to TERM when TERM_PROGRAM is not a known terminal', () => {81    process.env['TERM'] = 'xterm-ghostty';82    process.env['TERM_PROGRAM'] = 'some-unknown-terminal';83    expect(detectTerminal()).toBe('ghostty');84  });85 86  it('KITTY_WINDOW_ID is last resort fallback', () => {87    process.env['TERM'] = 'xterm-256color';88    process.env['KITTY_WINDOW_ID'] = '1';89    expect(detectTerminal()).toBe('kitty');90  });91});92 93describe('osc', () => {94  const originalEnv = { ...process.env };95 96  afterEach(() => {97    process.env = { ...originalEnv };98  });99 100  it('builds OSC sequence with BEL terminator for non-kitty', () => {101    process.env['TERM_PROGRAM'] = 'iTerm.app';102    delete process.env['TERM'];103    const result = osc(9, 'hello');104    expect(result).toBe(`\x1b]9;hello${BEL}`);105  });106 107  it('builds OSC sequence with ST terminator for kitty', () => {108    process.env['TERM'] = 'xterm-kitty';109    delete process.env['TERM_PROGRAM'];110    delete process.env['STY'];111    const result = osc(99, 'test');112    expect(result).toBe(`\x1b]99;test${ST}`);113  });114 115  it('falls back to BEL for kitty inside screen to avoid ST conflict with DCS wrapper', () => {116    process.env['TERM'] = 'xterm-kitty';117    delete process.env['TERM_PROGRAM'];118    process.env['STY'] = '12345.pts-0.host';119    const result = osc(99, 'test');120    expect(result).toBe(`\x1b]99;test${BEL}`);121  });122 123  it('joins multiple parts with semicolons', () => {124    process.env['TERM_PROGRAM'] = 'ghostty';125    delete process.env['TERM'];126    const result = osc(777, 'notify', 'Title', 'Body');127    expect(result).toBe(`\x1b]777;notify;Title;Body${BEL}`);128  });129});130 131describe('wrapForMultiplexer', () => {132  const originalEnv = { ...process.env };133 134  afterEach(() => {135    process.env = { ...originalEnv };136  });137 138  it('returns sequence unchanged outside multiplexer', () => {139    delete process.env['TMUX'];140    delete process.env['STY'];141    const seq = `\x1b]9;hello${BEL}`;142    expect(wrapForMultiplexer(seq)).toBe(seq);143  });144 145  it('wraps in DCS passthrough for tmux with ESC doubling', () => {146    process.env['TMUX'] = '/tmp/tmux-1000/default,12345,0';147    delete process.env['STY'];148    const seq = `\x1b]9;hello${BEL}`;149    // ESC bytes in payload should be doubled150    expect(wrapForMultiplexer(seq)).toBe(151      `\x1bPtmux;\x1b\x1b]9;hello${BEL}\x1b\\`,152    );153  });154 155  it('wraps in DCS passthrough for screen', () => {156    delete process.env['TMUX'];157    process.env['STY'] = '12345.pts-0.host';158    const seq = `\x1b]9;hello${BEL}`;159    expect(wrapForMultiplexer(seq)).toBe(`\x1bP${seq}\x1b\\`);160  });161});162 163describe('oscITerm2Notify', () => {164  const originalEnv = { ...process.env };165 166  afterEach(() => {167    process.env = { ...originalEnv };168  });169 170  it('formats notification with title and message', () => {171    process.env['TERM_PROGRAM'] = 'iTerm.app';172    delete process.env['TERM'];173    const result = oscITerm2Notify('Qwen Code', 'Hello');174    expect(result).toContain('Qwen Code:\nHello');175  });176 177  it('formats notification without title', () => {178    process.env['TERM_PROGRAM'] = 'iTerm.app';179    delete process.env['TERM'];180    const result = oscITerm2Notify('', 'Hello');181    expect(result).toContain('Hello');182    expect(result).not.toContain(':');183  });184});185 186describe('encodeKittyPayload', () => {187  it('base64-encodes ASCII text', () => {188    expect(encodeKittyPayload('Title')).toBe(189      Buffer.from('Title', 'utf8').toString('base64'),190    );191  });192 193  it('base64-encodes UTF-8 text', () => {194    expect(encodeKittyPayload('你好')).toBe(195      Buffer.from('你好', 'utf8').toString('base64'),196    );197  });198});199 200describe('oscKittyNotify', () => {201  const originalEnv = { ...process.env };202 203  afterEach(() => {204    process.env = { ...originalEnv };205  });206 207  it('returns three-step protocol sequences with base64-encoded payloads', () => {208    process.env['TERM'] = 'xterm-kitty';209    delete process.env['TERM_PROGRAM'];210    delete process.env['STY'];211    const seqs = oscKittyNotify('Title', 'Body', 42);212    expect(seqs).toHaveLength(3);213    const b64Title = Buffer.from('Title', 'utf8').toString('base64');214    const b64Body = Buffer.from('Body', 'utf8').toString('base64');215    // Step 1: title with e=1 (base64)216    expect(seqs[0]).toContain('i=42:d=0:p=title:e=1');217    expect(seqs[0]).toContain(b64Title);218    expect(seqs[0]).not.toContain('Title');219    // Step 2: body with e=1 (base64)220    expect(seqs[1]).toContain('i=42:p=body:e=1');221    expect(seqs[1]).toContain(b64Body);222    expect(seqs[1]).not.toContain('Body');223    // Step 3: activate (no payload to encode)224    expect(seqs[2]).toContain('i=42:d=1:a=focus');225  });226});227 228describe('oscGhosttyNotify', () => {229  const originalEnv = { ...process.env };230 231  afterEach(() => {232    process.env = { ...originalEnv };233  });234 235  it('formats OSC 777 notification', () => {236    process.env['TERM_PROGRAM'] = 'ghostty';237    delete process.env['TERM'];238    const result = oscGhosttyNotify('Title', 'Body');239    expect(result).toBe(`\x1b]${OSC.GHOSTTY};notify;Title;Body${BEL}`);240  });241});242 243describe('sanitizeOscPayload', () => {244  it('strips ESC character', () => {245    expect(sanitizeOscPayload('hello\x1bworld')).toBe('helloworld');246  });247 248  it('strips BEL character', () => {249    expect(sanitizeOscPayload('hello\x07world')).toBe('helloworld');250  });251 252  it('strips all C0 control characters', () => {253    expect(sanitizeOscPayload('a\x00b\x01c\x1fd')).toBe('abcd');254  });255 256  it('preserves normal text', () => {257    expect(sanitizeOscPayload('Hello World! 你好')).toBe('Hello World! 你好');258  });259 260  it('strips C1 control characters', () => {261    expect(sanitizeOscPayload('hello\x9cworld')).toBe('helloworld');262  });263});264 265describe('osc sanitizes payloads', () => {266  const originalEnv = { ...process.env };267 268  afterEach(() => {269    process.env = { ...originalEnv };270  });271 272  it('strips control characters from string parts', () => {273    process.env['TERM_PROGRAM'] = 'ghostty';274    delete process.env['TERM'];275    const result = osc(OSC.GHOSTTY, 'notify', 'Title\x1b]hack', 'Body\x07end');276    expect(result).not.toContain('\x1b]hack');277    expect(result).toContain('Title]hack');278    expect(result).toContain('Bodyend');279  });280});281 
basant307/AI_Governance_Project · CoolFace