/** * Copyright (c) 2019 The xterm.js authors. All rights reserved. * @license MIT */ import { test } from '@playwright/test'; import { deepStrictEqual, ok } from 'assert'; import { ITestContext, createTestContext, openTerminal, pollFor } from './TestUtils'; let ctx: ITestContext; test.beforeAll(async ({ browser }) => { ctx = await createTestContext(browser); await openTerminal(ctx); }); test.afterAll(async () => await ctx.page.close()); test.describe('InputHandler Integration Tests', () => { let recordedData: string[]; let recordDataDisposable: { dispose: () => void }; test.beforeAll(async () => { recordedData = []; recordDataDisposable = ctx.proxy.onData(d => recordedData.push(d)); }); test.afterAll(async () => { recordDataDisposable.dispose(); }); test.beforeEach(async () => { recordedData.length = 0; await ctx.proxy.resize(80, 24); }); test.describe('CSI', () => { test.beforeEach(async () => await ctx.proxy.reset()); test('CSI Ps @ - ICH: Insert Ps (Blank) Character(s) (default = 1)', async () => { // Default await ctx.proxy.write('foo\x1b[3D\x1b[@\n\r'); // Explicit await ctx.proxy.write('bar\x1b[3D\x1b[4@'); await pollFor(ctx.page, () => getLinesAsArray(2), [' foo', ' bar']); }); test('CSI Ps SP @ - SL: Shift left Ps columns(s) (default = 1), ECMA-48', async () => { // Default await ctx.proxy.write('abcdefg\x1b[ @'); await pollFor(ctx.page, () => getLinesAsArray(1), ['bcdefg']); // Explicit await ctx.proxy.reset(); await ctx.proxy.write('abcdefg\x1b[3 @'); await pollFor(ctx.page, () => getLinesAsArray(1), ['defg']); }); test('CSI Ps A - CUU: Cursor Up Ps Times (default = 1)', async () => { // Default await ctx.proxy.write('\n\n\n\n\x1b[Aa'); // Explicit await ctx.proxy.write('\x1b[2Ab'); await pollFor(ctx.page, () => getLinesAsArray(4), ['', ' b', '', 'a']); }); test('CSI Ps B - CUD: Cursor Down Ps Times (default = 1)', async () => { // Default await ctx.proxy.write('\x1b[Ba'); // Explicit await ctx.proxy.write('\x1b[2Bb'); await pollFor(ctx.page, () => getLinesAsArray(4), ['', 'a', '', ' b']); }); test('CSI Ps C - CUF: Cursor Forward Ps Times (default = 1)', async () => { // Default await ctx.proxy.write('\x1b[Ca'); // Explicit await ctx.proxy.write('\x1b[2Cb'); await pollFor(ctx.page, () => getLinesAsArray(1), [' a b']); }); test('CSI Ps D - CUB: Cursor Backward Ps Times (default = 1)', async () => { // Default await ctx.proxy.write('foo\x1b[Da'); // Explicit await ctx.proxy.write('\x1b[2Db'); await pollFor(ctx.page, () => getLinesAsArray(1), ['fba']); }); test('CSI Ps E - CNL: Cursor Next Line Ps Times (default = 1)', async () => { // Default await ctx.proxy.write('\x1b[Ea'); // Explicit await ctx.proxy.write('\x1b[2Eb'); await pollFor(ctx.page, () => getLinesAsArray(4), ['', 'a', '', 'b']); }); test('CSI Ps F - CPL: Cursor Preceding Line Ps Times (default = 1)', async () => { // Default await ctx.proxy.write('\n\n\n\n\x1b[Fa'); // Explicit await ctx.proxy.write('\x1b[2Fb'); await pollFor(ctx.page, () => getLinesAsArray(5), ['', 'b', '', 'a', '']); }); test('CSI Ps G - CHA: Cursor Character Absolute [column] (default = [row,1])', async () => { // Default await ctx.proxy.write('foo\x1b[Ga'); // Explicit await ctx.proxy.write('\x1b[10Gb'); await pollFor(ctx.page, () => getLinesAsArray(1), ['aoo b']); }); test('CSI Ps ; Ps H - CUP: Cursor Position [row;column] (default = [1,1])', async () => { // Default await ctx.proxy.write('foo\x1b[Ha'); // Explicit await ctx.proxy.write('\x1b[3;3Hb'); await pollFor(ctx.page, () => getLinesAsArray(3), ['aoo', '', ' b']); }); test('CSI Ps I - CHT: Cursor Forward Tabulation Ps tab stops (default = 1)', async () => { // Default await ctx.proxy.write('\x1b[Ia'); // Explicit await ctx.proxy.write('\n\r\x1b[2Ib'); await pollFor(ctx.page, () => getLinesAsArray(2), [' a', ' b']); }); test('CSI Ps J - ED: Erase in Display, VT100', async () => { const fixture = 'abc\n\rdef\n\rghi\x1b[2;2H'; // Default: Erase Below await ctx.proxy.resize(5, 5); await ctx.proxy.write(fixture + '\x1b[J'); await pollFor(ctx.page, () => getLinesAsArray(3), ['abc', 'd', '']); // 0: Erase Below await ctx.proxy.reset(); await ctx.proxy.write(fixture + '\x1b[0J'); await pollFor(ctx.page, () => getLinesAsArray(3), ['abc', 'd', '']); // 1: Erase Above await ctx.proxy.reset(); await ctx.proxy.write(fixture + '\x1b[1J'); await pollFor(ctx.page, () => getLinesAsArray(3), ['', ' f', 'ghi']); // 2: Erase Saved Lines (scrollback) await ctx.proxy.reset(); await ctx.proxy.write('1\n2\n3\n4\n5' + fixture + '\x1b[3J'); await pollFor(ctx.page, () => ctx.proxy.buffer.active.length, 5); await pollFor(ctx.page, () => getLinesAsArray(5), [' 4', ' 5', 'abc', 'def', 'ghi']); }); test('CSI ? Ps J - DECSED: Erase in Display, VT220', async () => { const fixture = 'abc\n\rdef\n\rghi\x1b[2;2H'; // Default: Erase Below await ctx.proxy.resize(5, 5); await ctx.proxy.write(fixture + '\x1b[?J'); await pollFor(ctx.page, () => getLinesAsArray(3), ['abc', 'd', '']); // 0: Erase Below await ctx.proxy.reset(); await ctx.proxy.write(fixture + '\x1b[?0J'); await pollFor(ctx.page, () => getLinesAsArray(3), ['abc', 'd', '']); // 1: Erase Above await ctx.proxy.reset(); await ctx.proxy.write(fixture + '\x1b[?1J'); await pollFor(ctx.page, () => getLinesAsArray(3), ['', ' f', 'ghi']); // 2: Erase Saved Lines (scrollback) await ctx.proxy.reset(); await ctx.proxy.write('1\n2\n3\n4\n5' + fixture + '\x1b[?3J'); await pollFor(ctx.page, () => ctx.proxy.buffer.active.length, 5); await pollFor(ctx.page, () => getLinesAsArray(5), [' 4', ' 5', 'abc', 'def', 'ghi']); }); test('CSI Ps K - EL: Erase in Line, VT100', async () => { const fixture = 'abcde\x1b[1;3H'; // Default: Erase to Right await ctx.proxy.write(fixture + '\x1b[K'); await pollFor(ctx.page, () => getLinesAsArray(1), ['ab']); // 0: Erase to Right await ctx.proxy.reset(); await ctx.proxy.write(fixture + '\x1b[0K'); await pollFor(ctx.page, () => getLinesAsArray(1), ['ab']); // 1: Erase to Left await ctx.proxy.reset(); await ctx.proxy.write(fixture + '\x1b[1K'); await pollFor(ctx.page, () => getLinesAsArray(1), [' de']); // 2: Erase All await ctx.proxy.reset(); await ctx.proxy.write(fixture + '\x1b[2K'); await pollFor(ctx.page, () => getLinesAsArray(1), ['']); }); test('CSI ? Ps K - DECSEL: Erase in Line, VT220', async () => { const fixture = 'abcde\x1b[1;3H'; // Default: Erase to Right await ctx.proxy.write(fixture + '\x1b[?K'); await pollFor(ctx.page, () => getLinesAsArray(1), ['ab']); // 0: Erase to Right await ctx.proxy.reset(); await ctx.proxy.write(fixture + '\x1b[?0K'); await pollFor(ctx.page, () => getLinesAsArray(1), ['ab']); // 1: Erase to Left await ctx.proxy.reset(); await ctx.proxy.write(fixture + '\x1b[?1K'); await pollFor(ctx.page, () => getLinesAsArray(1), [' de']); // 2: Erase All await ctx.proxy.reset(); await ctx.proxy.write(fixture + '\x1b[?2K'); await pollFor(ctx.page, () => getLinesAsArray(1), ['']); }); test('CSI Ps L - IL: Insert Ps Line(s) (default = 1)', async () => { // Default await ctx.proxy.write('foo\x1b[La'); // Explicit await ctx.proxy.write('\x1b[2Lb'); await pollFor(ctx.page, () => getLinesAsArray(4), ['b', '', 'a', 'foo']); }); test('CSI Ps M - DL: Delete Ps Line(s) (default = 1)', async () => { // Default await ctx.proxy.write('a\nb\x1b[1F\x1b[M'); // Explicit await ctx.proxy.write('\x1b[1Ed\ne\nf\x1b[2F\x1b[2M'); await pollFor(ctx.page, () => getLinesAsArray(5), [' b', ' f', '', '', '']); }); test('CSI Ps P - DCH: Delete Ps Character(s) (default = 1)', async () => { // Default await ctx.proxy.write('abc\x1b[1;1H\x1b[P'); // Explicit await ctx.proxy.write('\n\rdef\x1b[2;1H\x1b[2P'); await pollFor(ctx.page, () => getLinesAsArray(2), ['bc', 'f']); }); test.skip('CSI Pm # P - XTPUSHCOLORS: Push current dynamic- and ANSI-palette colors onto stack, xterm', async () => { // TODO: Implement }); test.skip('CSI Pm # Q - XTPOPCOLORS: Pop stack to set dynamic- and ANSI-palette colors, xterm', async () => { // TODO: Implement }); test.skip('CSI # R - XTREPORTCOLORS: Report the current entry on the palette stack, and the number of palettes stored on the stack, using the same form as XTPOPCOLOR (default = 0), xterm', async () => { // TODO: Implement }); test('CSI Ps S - SU: Scroll up Ps lines (default = 1), VT420, ECMA-48', async () => { await ctx.proxy.write('1\r\n2\r\n3\r\n4\r\n5'); await pollFor(ctx.page, () => getLinesAsArray(5), ['1', '2', '3', '4', '5']); await ctx.proxy.write('\x1b[S'); await pollFor(ctx.page, () => getLinesAsArray(5), ['2', '3', '4', '5', '']); await ctx.proxy.reset(); await ctx.proxy.write('1\r\n2\r\n3\r\n4\r\n5'); await ctx.proxy.write('\x1b[2S'); await pollFor(ctx.page, () => getLinesAsArray(5), ['3', '4', '5', '', '']); }); // This is intentionally not implemented here are image support lives within an addon // test.skip('CSI ? Pi ; Pa ; Pv S - XTSMGRAPHICS: Set or request graphics attribute, xterm', async () => { // }); test('CSI Ps T - SD: Scroll down Ps lines (default = 1), VT420', async () => { await ctx.proxy.write('1\r\n2\r\n3\r\n4\r\n5'); await pollFor(ctx.page, () => getLinesAsArray(5), ['1', '2', '3', '4', '5']); await ctx.proxy.write('\x1b[T'); await pollFor(ctx.page, () => getLinesAsArray(5), ['', '1', '2', '3', '4']); await ctx.proxy.reset(); await ctx.proxy.write('1\r\n2\r\n3\r\n4\r\n5'); await ctx.proxy.write('\x1b[2T'); await pollFor(ctx.page, () => getLinesAsArray(5), ['', '', '1', '2', '3']); }); test.skip('CSI Ps ; Ps ; Ps ; Ps ; Ps T - XTHIMOUSE: Initiate highlight mouse tracking (XTHIMOUSE), xterm', async () => { // TODO: Implement }); test.skip('CSI > Pm T - XTRMTITLE: Reset title mode features to default value, xterm', async () => { // TODO: Implement }); test('CSI Ps X - ECH: Erase Ps Character(s) (default = 1)', async () => { await ctx.proxy.write('abcdef\x1b[1;1H\x1b[X'); await pollFor(ctx.page, () => getLinesAsArray(1), [' bcdef']); await ctx.proxy.reset(); await ctx.proxy.write('abcdef\x1b[1;1H\x1b[3X'); await pollFor(ctx.page, () => getLinesAsArray(1), [' def']); }); test('CSI Ps Z - CBT: Cursor Backward Tabulation Ps tab stops (default = 1)', async () => { await ctx.proxy.write('\x1b[17Ga\x1b[17G\x1b[Zb'); await pollFor(ctx.page, () => getLinesAsArray(1), [' b a']); }); test('CSI Ps ^ - SD: Scroll down Ps lines (default = 1) (SD), ECMA-48', async () => { await ctx.proxy.write('1\r\n2\r\n3\r\n4\r\n5'); await pollFor(ctx.page, () => getLinesAsArray(5), ['1', '2', '3', '4', '5']); await ctx.proxy.write('\x1b[^'); await pollFor(ctx.page, () => getLinesAsArray(5), ['', '1', '2', '3', '4']); await ctx.proxy.reset(); await ctx.proxy.write('1\r\n2\r\n3\r\n4\r\n5'); await ctx.proxy.write('\x1b[2^'); await pollFor(ctx.page, () => getLinesAsArray(5), ['', '', '1', '2', '3']); }); test('CSI Ps ` - HPA: Character Position Absolute [column] (default = [row,1])', async () => { // Default await ctx.proxy.write('foo\x1b[`a'); // Explicit await ctx.proxy.write('\x1b[10`b'); await pollFor(ctx.page, () => getLinesAsArray(1), ['aoo b']); }); test('CSI Ps a - HPR: Character Position Relative (default = [row,col+1])', async () => { await ctx.proxy.write('a\x1b[2aB'); await pollFor(ctx.page, () => getLinesAsArray(1), ['a B']); }); test('CSI Ps b - REP: Repeat preceding character, ECMA48', async () => { // default to 1 await ctx.proxy.resize(10, 10); await ctx.proxy.write('#\x1b[b'); await ctx.proxy.writeln(''); await ctx.proxy.write('#\x1b[0b'); await ctx.proxy.writeln(''); await ctx.proxy.write('#\x1b[1b'); await ctx.proxy.writeln(''); await ctx.proxy.write('#\x1b[5b'); await pollFor(ctx.page, () => getLinesAsArray(4), ['##', '##', '##', '######']); await pollFor(ctx.page, () => getCursor(), { col: 6, row: 3 }); // repeat on fullwidth chars await ctx.proxy.reset(); await ctx.proxy.write('¥\x1b[8b'); await pollFor(ctx.page, () => getLinesAsArray(1), ['¥¥¥¥¥']); // change from xterm: repeat grapheme cluster await ctx.proxy.reset(); await ctx.proxy.write('e\u0301\x1b[2b'); await pollFor(ctx.page, () => getLinesAsArray(1), ['e\u0301e\u0301e\u0301']); // should wrap correctly await ctx.proxy.reset(); await ctx.proxy.write('#\x1b[15b'); await pollFor(ctx.page, () => getLinesAsArray(2), ['##########', '######']); // disable wrap around await ctx.proxy.reset(); await ctx.proxy.write('\x1b[?7l'); await ctx.proxy.write('#\x1b[15b'); await pollFor(ctx.page, () => getLinesAsArray(2), ['##########', '']); // any successful sequence should reset REP await ctx.proxy.reset(); await ctx.proxy.write('\x1b[?7h'); // re-enable wrap around await ctx.proxy.write('#\n\x1b[3b'); await ctx.proxy.write('#\r\x1b[3b'); await ctx.proxy.writeln(''); await ctx.proxy.write('abcdefg\x1b[3D\x1b[10b#\x1b[3b'); await pollFor(ctx.page, () => getLinesAsArray(3), ['#', ' #', 'abcd####']); }); test('CSI Ps c - ', async () => { await ctx.proxy.write('\x1b[c'); await pollFor(ctx.page, () => recordedData, ['\x1b[?1;2c']); }); test.skip('CSI = Ps c - ', async () => { // TODO: Implement }); test('CSI > Ps c - ', async () => { await ctx.proxy.write('\x1b[>c'); await pollFor(ctx.page, () => recordedData, ['\x1b[>0;276;0c']); }); test('CSI Ps d - VPA: Line Position Absolute [row] (default = [1,column])', async () => { // Default await ctx.proxy.write('\n\n\n \x1b[da'); // Explicit await ctx.proxy.write('\x1b[2d b'); await pollFor(ctx.page, () => getLinesAsArray(4), [' a', ' b', '', ' ']); }); test('CSI Ps e - VPR: Line Position Relative (default = 1)', async () => { // Default await ctx.proxy.write('\x1b[ea'); // Explicit await ctx.proxy.write('\x1b[2eb'); await pollFor(ctx.page, () => getLinesAsArray(4), ['', 'a', '', ' b']); }); test('CSI Ps ; Ps f - HVP: Horizontal and Vertical Position [row;column] (default = [1,1])', async () => { // Default await ctx.proxy.write('foo\x1b[fa'); // Explicit await ctx.proxy.write('\x1b[3;3fb'); await pollFor(ctx.page, () => getLinesAsArray(3), ['aoo', '', ' b']); }); test('CSI Ps g - TBC: Tab Clear (default = 0)', async () => { // Default: Clear tab stop at cursor position // Move to column 9 (first tab stop), clear it, go back to column 1, tab should skip to column 17 await ctx.proxy.write('\x1b[9G\x1b[g\x1b[1G\ta'); await pollFor(ctx.page, () => getLinesAsArray(1), [' a']); // Ps=3: Clear all tab stops await ctx.proxy.reset(); await ctx.proxy.write('\x1b[3g\ta'); // With all tabs cleared, tab moves to end of line await pollFor(ctx.page, () => getLinesAsArray(1), [' a']); }); test('CSI Ps h - SM: Set Mode', async () => { await ctx.proxy.write('\x1b[4h'); await pollFor(ctx.page, async () => (await ctx.proxy.modes).insertMode, true); await ctx.proxy.write('\x1b[20h'); await pollFor(ctx.page, async () => await ctx.proxy.getOption('convertEol'), true); }); test.describe('CSI ? Pm h - DECSET: Private Mode Set', () => { test('Ps = 1 - Application Cursor Keys (DECCKM), VT100', async () => { await ctx.proxy.write('\x1b[?1h'); await pollFor(ctx.page, async () => (await ctx.proxy.modes).applicationCursorKeysMode, true); recordedData.length = 0; await ctx.proxy.focus(); await ctx.page.keyboard.press('ArrowUp'); await pollFor(ctx.page, () => recordedData, ['\x1bOA']); }); test('Ps = 2 - Designate USASCII for character sets G0-G3 (DECANM), VT100, and set VT100 mode', async () => { await ctx.proxy.write('\x1b(0q\x1b[?2hq'); await pollFor(ctx.page, () => getLinesAsArray(1), ['─q']); }); test('Ps = 3 - 132 Column Mode (DECCOLM), VT100', async () => { const windowOptions = await ctx.proxy.getOption('windowOptions'); await ctx.proxy.setOption('windowOptions', { ...windowOptions, setWinLines: true }); await ctx.proxy.write('\x1b[?3h'); await pollFor(ctx.page, async () => await ctx.proxy.cols, 132); await ctx.proxy.resize(80, 24); await ctx.proxy.setOption('windowOptions', windowOptions); }); test('Ps = 4 - Smooth (Slow) Scroll (DECSCLM), VT100', async () => { await assertNoModeChange('\x1b[?4h'); }); test('Ps = 5 - Reverse Video (DECSCNM), VT100', async () => { await assertNoModeChange('\x1b[?5h'); }); test('Ps = 6 - Origin Mode (DECOM), VT100', async () => { await ctx.proxy.write('\x1b[?6h'); await pollFor(ctx.page, async () => (await ctx.proxy.modes).originMode, true); await pollFor(ctx.page, () => getCursor(), { col: 0, row: 0 }); await ctx.proxy.write('\x1b[2;3r'); await ctx.proxy.write('\x1b[1;1HX'); await pollFor(ctx.page, () => getLinesAsArray(3), ['', 'X', '']); }); test('Ps = 7 - Auto-Wrap Mode (DECAWM), VT100', async () => { await ctx.proxy.resize(5, 2); await ctx.proxy.write('\x1b[?7h'); await pollFor(ctx.page, async () => (await ctx.proxy.modes).wraparoundMode, true); await ctx.proxy.write('12345X'); await pollFor(ctx.page, () => getLinesAsArray(2), ['12345', 'X']); await ctx.proxy.reset(); await ctx.proxy.write('\x1b[?7l'); await pollFor(ctx.page, async () => (await ctx.proxy.modes).wraparoundMode, false); await ctx.proxy.write('12345X'); await pollFor(ctx.page, () => getLinesAsArray(1), ['1234X']); await ctx.proxy.resize(80, 24); }); test('Ps = 8 - Auto-Repeat Keys (DECARM), VT100', async () => { await assertNoModeChange('\x1b[?8h'); }); test('Ps = 9 - Send Mouse X & Y on button press', async () => { const selectionBefore = await dragSelection(); ok(selectionBefore > 0); await ctx.proxy.write('\x1b[?9h'); await pollFor(ctx.page, async () => (await ctx.proxy.modes).mouseTrackingMode, 'x10'); const selectionAfter = await dragSelection(); ok(selectionAfter === 0); }); test('Ps = 1 0 - Show toolbar (rxvt)', async () => { await assertNoModeChange('\x1b[?10h'); }); test('Ps = 1 2 - Start blinking cursor (AT&T 610)', async () => { const previousQuirks = await ctx.proxy.getOption('quirks'); const previousCursorBlink = await ctx.proxy.getOption('cursorBlink'); await ctx.proxy.setOption('quirks', { ...(previousQuirks ?? {}), allowSetCursorBlink: true }); await ctx.proxy.write('\x1b[?12h'); await pollFor(ctx.page, async () => await ctx.proxy.getOption('cursorBlink'), true); await ctx.proxy.setOption('quirks', previousQuirks); await ctx.proxy.setOption('cursorBlink', previousCursorBlink); }); test('Ps = 1 3 - Start blinking cursor (set only via resource or menu)', async () => { await assertNoModeChange('\x1b[?13h'); }); test('Ps = 1 4 - Enable XOR of blinking cursor control sequence and menu', async () => { await assertNoModeChange('\x1b[?14h'); }); test('Ps = 1 8 - Print Form Feed (DECPFF), VT220', async () => { await assertNoModeChange('\x1b[?18h'); }); test('Ps = 1 9 - Set print extent to full screen (DECPEX), VT220', async () => { await assertNoModeChange('\x1b[?19h'); }); test('Ps = 2 5 - Show cursor (DECTCEM), VT220', async () => { await ctx.proxy.write('\x1b[?25l'); await pollFor(ctx.page, async () => (await ctx.proxy.modes).showCursor, false); await ctx.proxy.write('\x1b[?25h'); await pollFor(ctx.page, async () => (await ctx.proxy.modes).showCursor, true); }); test('Ps = 3 0 - Show scrollbar (rxvt)', async () => { await assertNoModeChange('\x1b[?30h'); }); test('Ps = 3 5 - Enable font-shifting functions (rxvt)', async () => { await assertNoModeChange('\x1b[?35h'); }); test('Ps = 3 8 - Enter Tektronix mode (DECTEK), VT240, xterm', async () => { await assertNoModeChange('\x1b[?38h'); }); test('Ps = 4 0 - Allow 80 ⇒ 132 mode, xterm', async () => { await assertNoModeChange('\x1b[?40h'); }); test('Ps = 4 1 - more(1) fix (see curses resource)', async () => { await assertNoModeChange('\x1b[?41h'); }); test('Ps = 4 2 - Enable National Replacement Character sets (DECNRCM), VT220', async () => { await assertNoModeChange('\x1b[?42h'); }); test('Ps = 4 3 - Enable Graphic Expanded Print Mode (DECGEPM), VT340', async () => { await assertNoModeChange('\x1b[?43h'); }); test('Ps = 4 4 - Turn on margin bell, xterm', async () => { await assertNoModeChange('\x1b[?44h'); }); test('Ps = 4 4 - Enable Graphic Print Color Mode (DECGPCM), VT340', async () => { await assertNoModeChange('\x1b[?44h'); }); test('Ps = 4 5 - Reverse-wraparound mode (XTREVWRAP), xterm', async () => { await ctx.proxy.write('\x1b[?45h'); await pollFor(ctx.page, async () => (await ctx.proxy.modes).reverseWraparoundMode, true); await ctx.proxy.resize(5, 2); await ctx.proxy.write('\x1b[?7h'); await ctx.proxy.write('12345X'); await ctx.proxy.write('\r\bY'); await pollFor(ctx.page, () => getLinesAsArray(2), ['1234Y', 'X']); await ctx.proxy.resize(80, 24); }); test.skip('Ps = 4 5 - Enable Graphic Print Color Syntax (DECGPCS), VT340', async () => { }); test('Ps = 4 6 - Start logging (XTLOGGING), xterm', async () => { await assertNoModeChange('\x1b[?46h'); }); test('Ps = 4 6 - Graphic Print Background Mode, VT340', async () => { await assertNoModeChange('\x1b[?46h'); }); test('Ps = 4 7 - Use Alternate Screen Buffer, xterm', async () => { await ctx.proxy.write('main'); await pollFor(ctx.page, () => getLinesAsArray(1), ['main']); await ctx.proxy.write('\x1b[?47h'); await pollFor(ctx.page, async () => await ctx.proxy.buffer.active.type, 'alternate'); await pollFor(ctx.page, () => getLinesAsArray(1), ['']); await ctx.proxy.write('\x1b[Halt'); await pollFor(ctx.page, () => getLinesAsArray(1), ['alt']); await ctx.proxy.write('\x1b[?47l'); await pollFor(ctx.page, async () => await ctx.proxy.buffer.active.type, 'normal'); await pollFor(ctx.page, () => getLinesAsArray(1), ['main']); }); test.skip('Ps = 4 7 - Enable Graphic Rotated Print Mode (DECGRPM), VT340', async () => { }); test('Ps = 6 6 - Application keypad mode (DECNKM), VT320', async () => { await ctx.proxy.write('\x1b[?66h'); await pollFor(ctx.page, async () => (await ctx.proxy.modes).applicationKeypadMode, true); }); test('Ps = 6 7 - Backarrow key sends backspace (DECBKM), VT340, VT420', async () => { await assertNoModeChange('\x1b[?67h'); }); test('Ps = 6 9 - Enable left and right margin mode (DECLRMM), VT420 and up', async () => { await assertNoModeChange('\x1b[?69h'); }); // test.skip('Ps = 8 0 - Enable Sixel Display Mode (DECSDM), VT330, VT340, VT382', async () => { // }); test('Ps = 9 5 - Do not clear screen when DECCOLM is set/reset (DECNCSM), VT510 and up', async () => { await assertNoModeChange('\x1b[?95h'); }); test('Ps = 1 0 0 0 - Send Mouse X & Y on button press and release', async () => { const selectionBefore = await dragSelection(); ok(selectionBefore > 0); await ctx.proxy.write('\x1b[?1000h'); await pollFor(ctx.page, async () => (await ctx.proxy.modes).mouseTrackingMode, 'vt200'); const selectionAfter = await dragSelection(); ok(selectionAfter === 0); }); test('Ps = 1 0 0 1 - Use Hilite Mouse Tracking, xterm', async () => { await assertNoModeChange('\x1b[?1001h'); }); test('Ps = 1 0 0 2 - Use Cell Motion Mouse Tracking, xterm', async () => { const selectionBefore = await dragSelection(); ok(selectionBefore > 0); await ctx.proxy.write('\x1b[?1002h'); await pollFor(ctx.page, async () => (await ctx.proxy.modes).mouseTrackingMode, 'drag'); const selectionAfter = await dragSelection(); ok(selectionAfter === 0); }); test('Ps = 1 0 0 3 - Set Use All Motion (any event) Mouse Tracking', async () => { const coords: { left: number, top: number, bottom: number, right: number } = await ctx.page.evaluate(` (function() { const rect = window.term.element.getBoundingClientRect(); return { left: rect.left, top: rect.top, bottom: rect.bottom, right: rect.right }; })(); `); // Click and drag and ensure there is a selection await ctx.page.mouse.click((coords.left + coords.right) / 2, (coords.top + coords.bottom) / 2); await ctx.page.mouse.down(); await ctx.page.mouse.move((coords.left + coords.right) / 2, (coords.top + coords.bottom) / 4); ok((await ctx.proxy.getSelection()).length > 0, 'mouse events are off so there should be a selection'); await ctx.page.mouse.up(); // Clear selection await ctx.page.mouse.click((coords.left + coords.right) / 2, (coords.top + coords.bottom) / 2); await pollFor(ctx.page, async () => (await ctx.proxy.getSelection()).length, 0); // Enable mouse events await ctx.proxy.write('\x1b[?1003h'); // Click and drag and ensure there is no selection await ctx.page.mouse.click((coords.left + coords.right) / 2, (coords.top + coords.bottom) / 2); await ctx.page.mouse.down(); await ctx.page.mouse.move((coords.left + coords.right) / 2, (coords.top + coords.bottom) / 4); // mouse events are on so there should be no selection await pollFor(ctx.page, async () => (await ctx.proxy.getSelection()).length, 0); await ctx.page.mouse.up(); }); test('Ps = 1 0 0 4 - Send FocusIn/FocusOut events, xterm', async () => { await ctx.proxy.write('\x1b[?1004h'); await pollFor(ctx.page, async () => (await ctx.proxy.modes).sendFocusMode, true); await ctx.proxy.blur(); recordedData.length = 0; await ctx.proxy.focus(); await ctx.proxy.blur(); await pollFor(ctx.page, () => recordedData, ['\x1b[I', '\x1b[O']); }); test('Ps = 1 0 0 5 - Enable UTF-8 Mouse Mode, xterm', async () => { await ctx.proxy.write('\x1b[?1006h'); await pollFor(ctx.page, async () => await ctx.proxy.core.evaluate(([core]) => core.coreMouseService.activeEncoding), 'SGR'); await ctx.proxy.write('\x1b[?1005h'); await pollFor(ctx.page, async () => await ctx.proxy.core.evaluate(([core]) => core.coreMouseService.activeEncoding), 'SGR'); }); test('Ps = 1 0 0 6 - Enable SGR Mouse Mode, xterm', async () => { await ctx.proxy.write('\x1b[?1006h'); await pollFor(ctx.page, async () => await ctx.proxy.core.evaluate(([core]) => core.coreMouseService.activeEncoding), 'SGR'); }); test('Ps = 1 0 0 7 - Enable Alternate Scroll Mode, xterm', async () => { await assertNoModeChange('\x1b[?1007h'); }); test('Ps = 1 0 1 0 - Scroll to bottom on tty output (rxvt)', async () => { await assertNoModeChange('\x1b[?1010h'); }); test('Ps = 1 0 1 1 - Scroll to bottom on key press (rxvt)', async () => { await assertNoModeChange('\x1b[?1011h'); }); test('Ps = 1 0 1 5 - Enable urxvt Mouse Mode', async () => { await ctx.proxy.write('\x1b[?1006h'); await pollFor(ctx.page, async () => await ctx.proxy.core.evaluate(([core]) => core.coreMouseService.activeEncoding), 'SGR'); await ctx.proxy.write('\x1b[?1015h'); await pollFor(ctx.page, async () => await ctx.proxy.core.evaluate(([core]) => core.coreMouseService.activeEncoding), 'SGR'); }); test('Ps = 1 0 1 6 - Enable SGR Mouse PixelMode, xterm', async () => { await ctx.proxy.write('\x1b[?1016h'); await pollFor(ctx.page, async () => await ctx.proxy.core.evaluate(([core]) => core.coreMouseService.activeEncoding), 'SGR_PIXELS'); }); test('Ps = 1 0 3 4 - Interpret "meta" key, xterm', async () => { await assertNoModeChange('\x1b[?1034h'); }); test('Ps = 1 0 3 5 - Enable special modifiers for Alt and NumLock keys, xterm', async () => { await assertNoModeChange('\x1b[?1035h'); }); test('Ps = 1 0 3 6 - Send ESC when Meta modifies a key, xterm', async () => { await assertNoModeChange('\x1b[?1036h'); }); test('Ps = 1 0 3 7 - Send DEL from the editing-keypad Delete key, xterm', async () => { await assertNoModeChange('\x1b[?1037h'); }); test('Ps = 1 0 3 9 - Send ESC when Alt modifies a key, xterm', async () => { await assertNoModeChange('\x1b[?1039h'); }); test('Ps = 1 0 4 0 - Keep selection even if not highlighted, xterm', async () => { await assertNoModeChange('\x1b[?1040h'); }); test('Ps = 1 0 4 1 - Use the CLIPBOARD selection, xterm', async () => { await assertNoModeChange('\x1b[?1041h'); }); test('Ps = 1 0 4 2 - Enable Urgency window manager hint when Control-G is received, xterm', async () => { await assertNoModeChange('\x1b[?1042h'); }); test('Ps = 1 0 4 3 - Enable raising of the window when Control-G is received, xterm', async () => { await assertNoModeChange('\x1b[?1043h'); }); test('Ps = 1 0 4 4 - Reuse the most recent data copied to CLIPBOARD, xterm', async () => { await assertNoModeChange('\x1b[?1044h'); }); test('Ps = 1 0 4 5 - XTREVWRAP2: Extended Reverse-wraparound mode, xterm', async () => { await assertNoModeChange('\x1b[?1045h'); }); test('Ps = 1 0 4 6 - Enable switching to/from Alternate Screen Buffer, xterm', async () => { await assertNoModeChange('\x1b[?1046h'); }); test('Ps = 1 0 4 7 - Use Alternate Screen Buffer, xterm', async () => { await ctx.proxy.write('main'); await pollFor(ctx.page, () => getLinesAsArray(1), ['main']); await ctx.proxy.write('\x1b[?1047h'); await pollFor(ctx.page, async () => await ctx.proxy.buffer.active.type, 'alternate'); await pollFor(ctx.page, () => getLinesAsArray(1), ['']); await ctx.proxy.write('\x1b[Halt'); await pollFor(ctx.page, () => getLinesAsArray(1), ['alt']); await ctx.proxy.write('\x1b[?1047l'); await pollFor(ctx.page, async () => await ctx.proxy.buffer.active.type, 'normal'); await pollFor(ctx.page, () => getLinesAsArray(1), ['main']); }); test('Ps = 1 0 4 8 - Save cursor as in DECSC, xterm', async () => { await ctx.proxy.write('\x1b[4;5H'); await ctx.proxy.write('\x1b[?1048h'); await ctx.proxy.write('\x1b[1;1H'); await ctx.proxy.write('\x1b[?1048l'); await pollFor(ctx.page, () => getCursor(), { col: 4, row: 3 }); }); test('Ps = 1 0 4 9 - Save cursor as in DECSC, xterm', async () => { await ctx.proxy.write('main'); await pollFor(ctx.page, () => getLinesAsArray(1), ['main']); await ctx.proxy.write('\x1b[4;6H'); await ctx.proxy.write('\x1b[?1049h'); await pollFor(ctx.page, async () => await ctx.proxy.buffer.active.type, 'alternate'); await ctx.proxy.write('\x1b[Halt'); await pollFor(ctx.page, () => getLinesAsArray(1), ['alt']); await ctx.proxy.write('\x1b[?1049l'); await pollFor(ctx.page, async () => await ctx.proxy.buffer.active.type, 'normal'); await pollFor(ctx.page, () => getLinesAsArray(1), ['main']); await pollFor(ctx.page, () => getCursor(), { col: 5, row: 3 }); }); test('Ps = 1 0 5 0 - Set terminfo/termcap function-key mode, xterm', async () => { await assertNoModeChange('\x1b[?1050h'); }); test('Ps = 1 0 5 1 - Set Sun function-key mode, xterm', async () => { await assertNoModeChange('\x1b[?1051h'); }); test('Ps = 1 0 5 2 - Set HP function-key mode, xterm', async () => { await assertNoModeChange('\x1b[?1052h'); }); test('Ps = 1 0 5 3 - Set SCO function-key mode, xterm', async () => { await assertNoModeChange('\x1b[?1053h'); }); test('Ps = 1 0 6 0 - Set legacy keyboard emulation, i.e, X11R6, xterm', async () => { await assertNoModeChange('\x1b[?1060h'); }); test('Ps = 1 0 6 1 - Set VT220 keyboard emulation, xterm', async () => { await assertNoModeChange('\x1b[?1061h'); }); test('Ps = 2 0 0 1 - Enable readline mouse button-1, xterm', async () => { await assertNoModeChange('\x1b[?2001h'); }); test('Ps = 2 0 0 2 - Enable readline mouse button-2, xterm', async () => { await assertNoModeChange('\x1b[?2002h'); }); test('Ps = 2 0 0 3 - Enable readline mouse button-3, xterm', async () => { await assertNoModeChange('\x1b[?2003h'); }); test('Pm = 2 0 0 4, Set bracketed paste mode', async () => { if (ctx.browser.browserType().name() !== 'chromium') { test.skip(); return; } await pollFor(ctx.page, () => simulatePaste('foo'), 'foo'); await ctx.proxy.write('\x1b[?2004h'); await pollFor(ctx.page, () => simulatePaste('bar'), '\x1b[200~bar\x1b[201~'); await ctx.proxy.write('\x1b[?2004l'); await pollFor(ctx.page, () => simulatePaste('baz'), 'baz'); }); test('Ps = 2 0 0 5 - Enable readline character-quoting, xterm', async () => { await assertNoModeChange('\x1b[?2005h'); }); test('Ps = 2 0 0 6 - Enable readline newline pasting, xterm', async () => { await assertNoModeChange('\x1b[?2006h'); }); }); test.skip('CSI Ps i - MC: Media Copy', async () => { // TODO: Implement }); test.skip('CSI ? Ps i - MC: Media Copy, DEC-specified', async () => { // TODO: Implement }); test('CSI Pm l - RM: Reset Mode', async () => { await ctx.proxy.write('\x1b[4h\x1b[20h'); await pollFor(ctx.page, async () => (await ctx.proxy.modes).insertMode, true); await pollFor(ctx.page, async () => await ctx.proxy.getOption('convertEol'), true); await ctx.proxy.write('\x1b[4l\x1b[20l'); await pollFor(ctx.page, async () => (await ctx.proxy.modes).insertMode, false); await pollFor(ctx.page, async () => await ctx.proxy.getOption('convertEol'), false); }); test.describe('CSI ? Pm l - DECRST: DEC Private Mode Reset', async () => { test('Ps = 1 - Normal Cursor Keys (DECCKM), VT100.', async () => { await ctx.proxy.write('\x1b[?1h'); await pollFor(ctx.page, async () => (await ctx.proxy.modes).applicationCursorKeysMode, true); await ctx.proxy.write('\x1b[?1l'); await pollFor(ctx.page, async () => (await ctx.proxy.modes).applicationCursorKeysMode, false); }); test('Ps = 2 - Designate VT52 mode (DECANM), VT100.', async () => { await assertNoModeChange('\x1b[?2l'); }); test('Ps = 3 - 80 Column Mode (DECCOLM), VT100.', async () => { const windowOptions = await ctx.proxy.getOption('windowOptions'); await ctx.proxy.setOption('windowOptions', { ...windowOptions, setWinLines: true }); await ctx.proxy.write('\x1b[?3h'); await pollFor(ctx.page, async () => await ctx.proxy.cols, 132); await ctx.proxy.write('\x1b[?3l'); await pollFor(ctx.page, async () => await ctx.proxy.cols, 80); await ctx.proxy.resize(80, 24); await ctx.proxy.setOption('windowOptions', windowOptions); }); test('Ps = 4 - Jump (Fast) Scroll (DECSCLM), VT100.', async () => { await assertNoModeChange('\x1b[?4l'); }); test('Ps = 5 - Normal Video (DECSCNM), VT100.', async () => { await assertNoModeChange('\x1b[?5l'); }); test('Ps = 6 - Normal Cursor Mode (DECOM), VT100.', async () => { await ctx.proxy.write('\x1b[?6h'); await pollFor(ctx.page, async () => (await ctx.proxy.modes).originMode, true); await ctx.proxy.write('\x1b[?6l'); await pollFor(ctx.page, async () => (await ctx.proxy.modes).originMode, false); }); test('Ps = 7 - No Auto-Wrap Mode (DECAWM), VT100.', async () => { await ctx.proxy.resize(5, 2); await ctx.proxy.write('\x1b[?7h'); await pollFor(ctx.page, async () => (await ctx.proxy.modes).wraparoundMode, true); await ctx.proxy.write('\x1b[?7l'); await pollFor(ctx.page, async () => (await ctx.proxy.modes).wraparoundMode, false); await ctx.proxy.write('12345X'); await pollFor(ctx.page, () => getLinesAsArray(1), ['1234X']); await ctx.proxy.write('\x1b[?7h'); await ctx.proxy.reset(); await ctx.proxy.write('\x1b[?7h'); await pollFor(ctx.page, async () => (await ctx.proxy.modes).wraparoundMode, true); await ctx.proxy.write('12345X'); await pollFor(ctx.page, () => getLinesAsArray(2), ['12345', 'X']); await ctx.proxy.resize(80, 24); }); test('Ps = 8 - No Auto-Repeat Keys (DECARM), VT100.', async () => { await assertNoModeChange('\x1b[?8l'); }); test('Ps = 9 - Don\'t send Mouse X & Y on button press, xterm.', async () => { await ctx.proxy.write('\x1b[?9h'); await pollFor(ctx.page, async () => (await ctx.proxy.modes).mouseTrackingMode, 'x10'); await ctx.proxy.write('\x1b[?9l'); await pollFor(ctx.page, async () => (await ctx.proxy.modes).mouseTrackingMode, 'none'); }); test('Ps = 1 0 - Hide toolbar (rxvt).', async () => { await assertNoModeChange('\x1b[?10l'); }); test('Ps = 1 2 - Stop blinking cursor (AT&T 610).', async () => { const previousQuirks = await ctx.proxy.getOption('quirks'); const previousCursorBlink = await ctx.proxy.getOption('cursorBlink'); await ctx.proxy.setOption('quirks', { ...(previousQuirks ?? {}), allowSetCursorBlink: true }); await ctx.proxy.setOption('cursorBlink', true); await ctx.proxy.write('\x1b[?12l'); await pollFor(ctx.page, async () => await ctx.proxy.getOption('cursorBlink'), false); await ctx.proxy.setOption('quirks', previousQuirks); await ctx.proxy.setOption('cursorBlink', previousCursorBlink); }); test('Ps = 1 3 - Disable blinking cursor (reset only via resource or menu).', async () => { await assertNoModeChange('\x1b[?13l'); }); test('Ps = 1 4 - Disable XOR of blinking cursor control sequence and menu.', async () => { await assertNoModeChange('\x1b[?14l'); }); test('Ps = 1 8 - Don\'t Print Form Feed (DECPFF), VT220.', async () => { await assertNoModeChange('\x1b[?18l'); }); test('Ps = 1 9 - Limit print to scrolling region (DECPEX), VT220.', async () => { await assertNoModeChange('\x1b[?19l'); }); test('Ps = 2 5 - Hide cursor (DECTCEM), VT220.', async () => { await ctx.proxy.write('\x1b[?25h'); await pollFor(ctx.page, async () => (await ctx.proxy.modes).showCursor, true); await ctx.proxy.write('\x1b[?25l'); await pollFor(ctx.page, async () => (await ctx.proxy.modes).showCursor, false); }); test('Ps = 3 0 - Don\'t show scrollbar (rxvt).', async () => { await assertNoModeChange('\x1b[?30l'); }); test('Ps = 3 5 - Disable font-shifting functions (rxvt).', async () => { await assertNoModeChange('\x1b[?35l'); }); test('Ps = 4 0 - Disallow 80 ⇒ 132 mode, xterm.', async () => { await assertNoModeChange('\x1b[?40l'); }); test('Ps = 4 1 - No more(1) fix (see curses resource).', async () => { await assertNoModeChange('\x1b[?41l'); }); test('Ps = 4 2 - Disable National Replacement Character sets (DECNRCM), VT220.', async () => { await assertNoModeChange('\x1b[?42l'); }); test('Ps = 4 3 - Disable Graphic Expanded Print Mode (DECGEPM), VT340.', async () => { await assertNoModeChange('\x1b[?43l'); }); test('Ps = 4 4 - Turn off margin bell, xterm.', async () => { await assertNoModeChange('\x1b[?44l'); }); test('Ps = 4 4 - Disable Graphic Print Color Mode (DECGPCM), VT340.', async () => { await assertNoModeChange('\x1b[?44l'); }); test('Ps = 4 5 - No Reverse-wraparound mode (XTREVWRAP), xterm.', async () => { await ctx.proxy.write('\x1b[?45h'); await pollFor(ctx.page, async () => (await ctx.proxy.modes).reverseWraparoundMode, true); await ctx.proxy.write('\x1b[?45l'); await pollFor(ctx.page, async () => (await ctx.proxy.modes).reverseWraparoundMode, false); }); test.skip('Ps = 4 5 - Disable Graphic Print Color Syntax (DECGPCS), VT340.', async () => { }); test('Ps = 4 6 - Stop logging (XTLOGGING), xterm. This is normally disabled by a compile-time option.', async () => { await assertNoModeChange('\x1b[?46l'); }); test('Ps = 4 7 - Use Normal Screen Buffer, xterm.', async () => { await ctx.proxy.write('\x1b[?47h'); await pollFor(ctx.page, async () => await ctx.proxy.buffer.active.type, 'alternate'); await ctx.proxy.write('\x1b[?47l'); await pollFor(ctx.page, async () => await ctx.proxy.buffer.active.type, 'normal'); }); test.skip('Ps = 4 7 - Disable Graphic Rotated Print Mode (DECGRPM), VT340.', async () => { }); test('Ps = 6 6 - Numeric keypad mode (DECNKM), VT320.', async () => { await ctx.proxy.write('\x1b[?66h'); await pollFor(ctx.page, async () => (await ctx.proxy.modes).applicationKeypadMode, true); await ctx.proxy.write('\x1b[?66l'); await pollFor(ctx.page, async () => (await ctx.proxy.modes).applicationKeypadMode, false); }); test('Ps = 6 7 - Backarrow key sends delete (DECBKM), VT340, VT420. This sets the backarrowKey resource to "false".', async () => { await assertNoModeChange('\x1b[?67l'); }); test('Ps = 6 9 - Disable left and right margin mode (DECLRMM), VT420 and up.', async () => { await assertNoModeChange('\x1b[?69l'); }); // This is intentionally not implemented here are image support lives within an addon // test.skip('Ps = 8 0 - Disable Sixel Display Mode (DECSDM), VT330, VT340, VT382. Turns on "Sixel Scrolling". See the section Sixel Graphics and mode 8 4 5 2 .', async () => { // }); test('Ps = 9 5 - Clear screen when DECCOLM is set/reset (DECNCSM), VT510 and up.', async () => { await assertNoModeChange('\x1b[?95l'); }); test('Ps = 1 0 0 0 - Don\'t send Mouse X & Y on button press and release. See the section Mouse Tracking.', async () => { await ctx.proxy.write('\x1b[?1000h'); await pollFor(ctx.page, async () => (await ctx.proxy.modes).mouseTrackingMode, 'vt200'); await ctx.proxy.write('\x1b[?1000l'); await pollFor(ctx.page, async () => (await ctx.proxy.modes).mouseTrackingMode, 'none'); }); test('Ps = 1 0 0 1 - Don\'t use Hilite Mouse Tracking, xterm.', async () => { await assertNoModeChange('\x1b[?1001l'); }); test('Ps = 1 0 0 2 - Don\'t use Cell Motion Mouse Tracking, xterm. See the section Button-event tracking.', async () => { await ctx.proxy.write('\x1b[?1002h'); await pollFor(ctx.page, async () => (await ctx.proxy.modes).mouseTrackingMode, 'drag'); await ctx.proxy.write('\x1b[?1002l'); await pollFor(ctx.page, async () => (await ctx.proxy.modes).mouseTrackingMode, 'none'); }); test('Ps = 1 0 0 3 - Don\'t use All Motion Mouse Tracking, xterm. See the section Any-event tracking.', async () => { await ctx.proxy.write('\x1b[?1003h'); await pollFor(ctx.page, async () => (await ctx.proxy.modes).mouseTrackingMode, 'any'); await ctx.proxy.write('\x1b[?1003l'); await pollFor(ctx.page, async () => (await ctx.proxy.modes).mouseTrackingMode, 'none'); }); test('Ps = 1 0 0 4 - Don\'t send FocusIn/FocusOut events, xterm.', async () => { await ctx.proxy.write('\x1b[?1004h'); await pollFor(ctx.page, async () => (await ctx.proxy.modes).sendFocusMode, true); await ctx.proxy.write('\x1b[?1004l'); await pollFor(ctx.page, async () => (await ctx.proxy.modes).sendFocusMode, false); }); test('Ps = 1 0 0 5 - Disable UTF-8 Mouse Mode, xterm.', async () => { await ctx.proxy.write('\x1b[?1006h'); await pollFor(ctx.page, async () => await ctx.proxy.core.evaluate(([core]) => core.coreMouseService.activeEncoding), 'SGR'); await ctx.proxy.write('\x1b[?1005l'); await pollFor(ctx.page, async () => await ctx.proxy.core.evaluate(([core]) => core.coreMouseService.activeEncoding), 'SGR'); }); test('Ps = 1 0 0 6 - Disable SGR Mouse Mode, xterm.', async () => { await ctx.proxy.write('\x1b[?1006h'); await pollFor(ctx.page, async () => await ctx.proxy.core.evaluate(([core]) => core.coreMouseService.activeEncoding), 'SGR'); await ctx.proxy.write('\x1b[?1006l'); await pollFor(ctx.page, async () => await ctx.proxy.core.evaluate(([core]) => core.coreMouseService.activeEncoding), 'DEFAULT'); }); test('Ps = 1 0 0 7 - Disable Alternate Scroll Mode, xterm. This corresponds to the alternateScroll resource.', async () => { await assertNoModeChange('\x1b[?1007l'); }); test('Ps = 1 0 1 0 - Don\'t scroll to bottom on tty output (rxvt). This sets the scrollTtyOutput resource to "false".', async () => { await assertNoModeChange('\x1b[?1010l'); }); test('Ps = 1 0 1 1 - Don\'t scroll to bottom on key press (rxvt). This sets the scrollKey resource to "false".', async () => { await assertNoModeChange('\x1b[?1011l'); }); test('Ps = 1 0 1 5 - Disable urxvt Mouse Mode.', async () => { await ctx.proxy.write('\x1b[?1006h'); await pollFor(ctx.page, async () => await ctx.proxy.core.evaluate(([core]) => core.coreMouseService.activeEncoding), 'SGR'); await ctx.proxy.write('\x1b[?1015l'); await pollFor(ctx.page, async () => await ctx.proxy.core.evaluate(([core]) => core.coreMouseService.activeEncoding), 'SGR'); }); test('Ps = 1 0 1 6 - Disable SGR Mouse Pixel-Mode, xterm.', async () => { await ctx.proxy.write('\x1b[?1016h'); await pollFor(ctx.page, async () => await ctx.proxy.core.evaluate(([core]) => core.coreMouseService.activeEncoding), 'SGR_PIXELS'); await ctx.proxy.write('\x1b[?1016l'); await pollFor(ctx.page, async () => await ctx.proxy.core.evaluate(([core]) => core.coreMouseService.activeEncoding), 'DEFAULT'); }); test('Ps = 1 0 3 4 - Don\'t interpret "meta" key, xterm. This disables the eightBitInput resource.', async () => { await assertNoModeChange('\x1b[?1034l'); }); test('Ps = 1 0 3 5 - Disable special modifiers for Alt and NumLock keys, xterm. This disables the numLock resource.', async () => { await assertNoModeChange('\x1b[?1035l'); }); test('Ps = 1 0 3 6 - Don\'t send ESC when Meta modifies a key, xterm. This disables the metaSendsEscape resource.', async () => { await assertNoModeChange('\x1b[?1036l'); }); test('Ps = 1 0 3 7 - Send VT220 Remove from the editing-keypad Delete key, xterm.', async () => { await assertNoModeChange('\x1b[?1037l'); }); test('Ps = 1 0 3 9 - Don\'t send ESC when Alt modifies a key, xterm. This disables the altSendsEscape resource.', async () => { await assertNoModeChange('\x1b[?1039l'); }); test('Ps = 1 0 4 0 - Do not keep selection when not highlighted, xterm. This disables the keepSelection resource.', async () => { await assertNoModeChange('\x1b[?1040l'); }); test('Ps = 1 0 4 1 - Use the PRIMARY selection, xterm. This disables the selectToClipboard resource.', async () => { await assertNoModeChange('\x1b[?1041l'); }); test('Ps = 1 0 4 2 - Disable Urgency window manager hint when Control-G is received, xterm. This disables the bellIsUrgent resource.', async () => { await assertNoModeChange('\x1b[?1042l'); }); test('Ps = 1 0 4 3 - Disable raising of the window when Control- G is received, xterm. This disables the popOnBell resource.', async () => { await assertNoModeChange('\x1b[?1043l'); }); test('Ps = 1 0 4 5 - No Extended Reverse-wraparound mode (XTREVWRAP2), xterm.', async () => { await assertNoModeChange('\x1b[?1045l'); }); test('Ps = 1 0 4 6 - Disable switching to/from Alternate Screen Buffer, xterm. This works for terminfo-based systems, updating the titeInhibit resource. If currently using the Alternate Screen Buffer, xterm switches to the Normal Screen Buffer.', async () => { await assertNoModeChange('\x1b[?1046l'); }); test('Ps = 1 0 4 7 - Use Normal Screen Buffer, xterm. Clear the screen first if in the Alternate Screen Buffer. This may be disabled by the titeInhibit resource.', async () => { await ctx.proxy.write('\x1b[?1047h'); await pollFor(ctx.page, async () => await ctx.proxy.buffer.active.type, 'alternate'); await ctx.proxy.write('\x1b[?1047l'); await pollFor(ctx.page, async () => await ctx.proxy.buffer.active.type, 'normal'); }); test('Ps = 1 0 4 8 - Restore cursor as in DECRC, xterm. This may be disabled by the titeInhibit resource.', async () => { await ctx.proxy.write('\x1b[4;6H'); await ctx.proxy.write('\x1b[?1048h'); await ctx.proxy.write('\x1b[1;1H'); await ctx.proxy.write('\x1b[?1048l'); await pollFor(ctx.page, () => getCursor(), { col: 5, row: 3 }); }); test('Ps = 1 0 4 9 - Use Normal Screen Buffer and restore cursor as in DECRC, xterm. This may be disabled by the titeInhibit resource. This combines the effects of the 1 0 4 7 and 1 0 4 8 modes. Use this with terminfo-based applications rather than the 4 7 mode.', async () => { await ctx.proxy.write('\x1b[3;4H'); await ctx.proxy.write('\x1b[?1048h'); await ctx.proxy.write('\x1b[?47h'); await pollFor(ctx.page, async () => await ctx.proxy.buffer.active.type, 'alternate'); await ctx.proxy.write('\x1b[1;1H'); await ctx.proxy.write('\x1b[?1049l'); await pollFor(ctx.page, async () => await ctx.proxy.buffer.active.type, 'normal'); await pollFor(ctx.page, () => getCursor(), { col: 3, row: 2 }); }); test('Ps = 1 0 5 0 - Reset terminfo/termcap function-key mode, xterm.', async () => { await assertNoModeChange('\x1b[?1050l'); }); test('Ps = 1 0 5 1 - Reset Sun function-key mode, xterm.', async () => { await assertNoModeChange('\x1b[?1051l'); }); test('Ps = 1 0 5 2 - Reset HP function-key mode, xterm.', async () => { await assertNoModeChange('\x1b[?1052l'); }); test('Ps = 1 0 5 3 - Reset SCO function-key mode, xterm.', async () => { await assertNoModeChange('\x1b[?1053l'); }); test('Ps = 1 0 6 0 - Reset legacy keyboard emulation, i.e, X11R6, xterm.', async () => { await assertNoModeChange('\x1b[?1060l'); }); test('Ps = 1 0 6 1 - Reset keyboard emulation to Sun/PC style, xterm.', async () => { await assertNoModeChange('\x1b[?1061l'); }); test('Ps = 2 0 0 1 - Disable readline mouse button-1, xterm.', async () => { await assertNoModeChange('\x1b[?2001l'); }); test('Ps = 2 0 0 2 - Disable readline mouse button-2, xterm.', async () => { await assertNoModeChange('\x1b[?2002l'); }); test('Ps = 2 0 0 3 - Disable readline mouse button-3, xterm.', async () => { await assertNoModeChange('\x1b[?2003l'); }); test('Ps = 2 0 0 4 - Reset bracketed paste mode, xterm.', async () => { await ctx.proxy.write('\x1b[?2004h'); await pollFor(ctx.page, async () => (await ctx.proxy.modes).bracketedPasteMode, true); await ctx.proxy.write('\x1b[?2004l'); await pollFor(ctx.page, async () => (await ctx.proxy.modes).bracketedPasteMode, false); }); test('Ps = 2 0 0 5 - Disable readline character-quoting, xterm.', async () => { await assertNoModeChange('\x1b[?2005l'); }); test('Ps = 2 0 0 6 - Disable readline newline pasting, xterm.', async () => { await assertNoModeChange('\x1b[?2006l'); }); }); test.describe('CSI Pm m - SGR: Character Attributes', () => { test('Ps = 0 - Normal (default), VT100', async () => { await ctx.proxy.write('\x1b[1;3;4;5;7;8;9m#\x1b[0m@'); const cell0 = await (await ctx.proxy.buffer.active.getLine(0))!.getCell(0); ok(await cell0!.isBold()); ok(await cell0!.isItalic()); ok(await cell0!.isUnderline()); ok(await cell0!.isBlink()); ok(await cell0!.isInverse()); ok(await cell0!.isInvisible()); ok(await cell0!.isStrikethrough()); const cell1 = await (await ctx.proxy.buffer.active.getLine(0))!.getCell(1); deepStrictEqual(await cell1!.isAttributeDefault(), true); }); test('Ps = 1 - Bold, VT100', async () => { await ctx.proxy.write('\x1b[1m#'); const cell = await (await ctx.proxy.buffer.active.getLine(0))!.getCell(0); ok(await cell!.isBold()); }); test('Ps = 2 - Faint, decreased intensity, ECMA-48 2nd', async () => { await ctx.proxy.write('\x1b[2m#'); const cell = await (await ctx.proxy.buffer.active.getLine(0))!.getCell(0); ok(await cell!.isDim()); }); test('Ps = 3 - Italicized, ECMA-48 2nd', async () => { await ctx.proxy.write('\x1b[3m#'); const cell = await (await ctx.proxy.buffer.active.getLine(0))!.getCell(0); ok(await cell!.isItalic()); }); test('Ps = 4 - Underlined, VT100', async () => { await ctx.proxy.write('\x1b[4m#'); const cell = await (await ctx.proxy.buffer.active.getLine(0))!.getCell(0); ok(await cell!.isUnderline()); }); test('Ps = 5 - Blink, VT100', async () => { await ctx.proxy.write('\x1b[5m#'); const cell = await (await ctx.proxy.buffer.active.getLine(0))!.getCell(0); ok(await cell!.isBlink()); }); test('Ps = 7 - Inverse, VT100', async () => { await ctx.proxy.write('\x1b[7m#'); const cell = await (await ctx.proxy.buffer.active.getLine(0))!.getCell(0); ok(await cell!.isInverse()); }); test('Ps = 8 - Invisible, ECMA-48 2nd, VT300', async () => { await ctx.proxy.write('\x1b[8m#'); const cell = await (await ctx.proxy.buffer.active.getLine(0))!.getCell(0); ok(await cell!.isInvisible()); }); test('Ps = 9 - Crossed-out characters, ECMA-48 3rd', async () => { await ctx.proxy.write('\x1b[9m#'); const cell = await (await ctx.proxy.buffer.active.getLine(0))!.getCell(0); ok(await cell!.isStrikethrough()); }); test('Ps = 21 - Doubly-underlined, ECMA-48 3rd', async () => { await ctx.proxy.write('\x1b[21m#'); const cell = await (await ctx.proxy.buffer.active.getLine(0))!.getCell(0); ok(await cell!.isUnderline()); }); test('Ps = 22 - Normal (neither bold nor faint), ECMA-48 3rd', async () => { await ctx.proxy.write('\x1b[1;2m#\x1b[22m@'); const cell0 = await (await ctx.proxy.buffer.active.getLine(0))!.getCell(0); ok(await cell0!.isBold()); ok(await cell0!.isDim()); const cell1 = await (await ctx.proxy.buffer.active.getLine(0))!.getCell(1); deepStrictEqual(await cell1!.isBold(), 0); deepStrictEqual(await cell1!.isDim(), 0); }); test('Ps = 23 - Not italicized, ECMA-48 3rd', async () => { await ctx.proxy.write('\x1b[3m#\x1b[23m@'); const cell0 = await (await ctx.proxy.buffer.active.getLine(0))!.getCell(0); ok(await cell0!.isItalic()); const cell1 = await (await ctx.proxy.buffer.active.getLine(0))!.getCell(1); deepStrictEqual(await cell1!.isItalic(), 0); }); test('Ps = 24 - Not underlined, ECMA-48 3rd', async () => { await ctx.proxy.write('\x1b[4m#\x1b[24m@'); const cell0 = await (await ctx.proxy.buffer.active.getLine(0))!.getCell(0); ok(await cell0!.isUnderline()); const cell1 = await (await ctx.proxy.buffer.active.getLine(0))!.getCell(1); deepStrictEqual(await cell1!.isUnderline(), 0); }); test('Ps = 25 - Steady (not blinking), ECMA-48 3rd', async () => { await ctx.proxy.write('\x1b[5m#\x1b[25m@'); const cell0 = await (await ctx.proxy.buffer.active.getLine(0))!.getCell(0); ok(await cell0!.isBlink()); const cell1 = await (await ctx.proxy.buffer.active.getLine(0))!.getCell(1); deepStrictEqual(await cell1!.isBlink(), 0); }); test('Ps = 27 - Positive (not inverse), ECMA-48 3rd', async () => { await ctx.proxy.write('\x1b[7m#\x1b[27m@'); const cell0 = await (await ctx.proxy.buffer.active.getLine(0))!.getCell(0); ok(await cell0!.isInverse()); const cell1 = await (await ctx.proxy.buffer.active.getLine(0))!.getCell(1); deepStrictEqual(await cell1!.isInverse(), 0); }); test('Ps = 28 - Visible, ECMA-48 3rd, VT300', async () => { await ctx.proxy.write('\x1b[8m#\x1b[28m@'); const cell0 = await (await ctx.proxy.buffer.active.getLine(0))!.getCell(0); ok(await cell0!.isInvisible()); const cell1 = await (await ctx.proxy.buffer.active.getLine(0))!.getCell(1); deepStrictEqual(await cell1!.isInvisible(), 0); }); test('Ps = 29 - Not crossed-out, ECMA-48 3rd', async () => { await ctx.proxy.write('\x1b[9m#\x1b[29m@'); const cell0 = await (await ctx.proxy.buffer.active.getLine(0))!.getCell(0); ok(await cell0!.isStrikethrough()); const cell1 = await (await ctx.proxy.buffer.active.getLine(0))!.getCell(1); deepStrictEqual(await cell1!.isStrikethrough(), 0); }); test('Ps = 30 - Set foreground color to Black', async () => { await ctx.proxy.write('\x1b[30m#'); const cell = await (await ctx.proxy.buffer.active.getLine(0))!.getCell(0); deepStrictEqual(await cell!.isFgPalette(), true); deepStrictEqual(await cell!.getFgColor(), 0); }); test('Ps = 31 - Set foreground color to Red', async () => { await ctx.proxy.write('\x1b[31m#'); const cell = await (await ctx.proxy.buffer.active.getLine(0))!.getCell(0); deepStrictEqual(await cell!.isFgPalette(), true); deepStrictEqual(await cell!.getFgColor(), 1); }); test('Ps = 32 - Set foreground color to Green', async () => { await ctx.proxy.write('\x1b[32m#'); const cell = await (await ctx.proxy.buffer.active.getLine(0))!.getCell(0); deepStrictEqual(await cell!.isFgPalette(), true); deepStrictEqual(await cell!.getFgColor(), 2); }); test('Ps = 33 - Set foreground color to Yellow', async () => { await ctx.proxy.write('\x1b[33m#'); const cell = await (await ctx.proxy.buffer.active.getLine(0))!.getCell(0); deepStrictEqual(await cell!.isFgPalette(), true); deepStrictEqual(await cell!.getFgColor(), 3); }); test('Ps = 34 - Set foreground color to Blue', async () => { await ctx.proxy.write('\x1b[34m#'); const cell = await (await ctx.proxy.buffer.active.getLine(0))!.getCell(0); deepStrictEqual(await cell!.isFgPalette(), true); deepStrictEqual(await cell!.getFgColor(), 4); }); test('Ps = 35 - Set foreground color to Magenta', async () => { await ctx.proxy.write('\x1b[35m#'); const cell = await (await ctx.proxy.buffer.active.getLine(0))!.getCell(0); deepStrictEqual(await cell!.isFgPalette(), true); deepStrictEqual(await cell!.getFgColor(), 5); }); test('Ps = 36 - Set foreground color to Cyan', async () => { await ctx.proxy.write('\x1b[36m#'); const cell = await (await ctx.proxy.buffer.active.getLine(0))!.getCell(0); deepStrictEqual(await cell!.isFgPalette(), true); deepStrictEqual(await cell!.getFgColor(), 6); }); test('Ps = 37 - Set foreground color to White', async () => { await ctx.proxy.write('\x1b[37m#'); const cell = await (await ctx.proxy.buffer.active.getLine(0))!.getCell(0); deepStrictEqual(await cell!.isFgPalette(), true); deepStrictEqual(await cell!.getFgColor(), 7); }); test('Ps = 39 - Set foreground color to default, ECMA-48 3rd', async () => { await ctx.proxy.write('\x1b[31m#\x1b[39m@'); const cell0 = await (await ctx.proxy.buffer.active.getLine(0))!.getCell(0); deepStrictEqual(await cell0!.isFgPalette(), true); deepStrictEqual(await cell0!.getFgColor(), 1); const cell1 = await (await ctx.proxy.buffer.active.getLine(0))!.getCell(1); deepStrictEqual(await cell1!.isFgDefault(), true); }); test('Ps = 40 - Set background color to Black', async () => { await ctx.proxy.write('\x1b[40m#'); const cell = await (await ctx.proxy.buffer.active.getLine(0))!.getCell(0); deepStrictEqual(await cell!.isBgPalette(), true); deepStrictEqual(await cell!.getBgColor(), 0); }); test('Ps = 41 - Set background color to Red', async () => { await ctx.proxy.write('\x1b[41m#'); const cell = await (await ctx.proxy.buffer.active.getLine(0))!.getCell(0); deepStrictEqual(await cell!.isBgPalette(), true); deepStrictEqual(await cell!.getBgColor(), 1); }); test('Ps = 42 - Set background color to Green', async () => { await ctx.proxy.write('\x1b[42m#'); const cell = await (await ctx.proxy.buffer.active.getLine(0))!.getCell(0); deepStrictEqual(await cell!.isBgPalette(), true); deepStrictEqual(await cell!.getBgColor(), 2); }); test('Ps = 43 - Set background color to Yellow', async () => { await ctx.proxy.write('\x1b[43m#'); const cell = await (await ctx.proxy.buffer.active.getLine(0))!.getCell(0); deepStrictEqual(await cell!.isBgPalette(), true); deepStrictEqual(await cell!.getBgColor(), 3); }); test('Ps = 44 - Set background color to Blue', async () => { await ctx.proxy.write('\x1b[44m#'); const cell = await (await ctx.proxy.buffer.active.getLine(0))!.getCell(0); deepStrictEqual(await cell!.isBgPalette(), true); deepStrictEqual(await cell!.getBgColor(), 4); }); test('Ps = 45 - Set background color to Magenta', async () => { await ctx.proxy.write('\x1b[45m#'); const cell = await (await ctx.proxy.buffer.active.getLine(0))!.getCell(0); deepStrictEqual(await cell!.isBgPalette(), true); deepStrictEqual(await cell!.getBgColor(), 5); }); test('Ps = 46 - Set background color to Cyan', async () => { await ctx.proxy.write('\x1b[46m#'); const cell = await (await ctx.proxy.buffer.active.getLine(0))!.getCell(0); deepStrictEqual(await cell!.isBgPalette(), true); deepStrictEqual(await cell!.getBgColor(), 6); }); test('Ps = 47 - Set background color to White', async () => { await ctx.proxy.write('\x1b[47m#'); const cell = await (await ctx.proxy.buffer.active.getLine(0))!.getCell(0); deepStrictEqual(await cell!.isBgPalette(), true); deepStrictEqual(await cell!.getBgColor(), 7); }); test('Ps = 49 - Set background color to default, ECMA-48 3rd', async () => { await ctx.proxy.write('\x1b[41m#\x1b[49m@'); const cell0 = await (await ctx.proxy.buffer.active.getLine(0))!.getCell(0); deepStrictEqual(await cell0!.isBgPalette(), true); deepStrictEqual(await cell0!.getBgColor(), 1); const cell1 = await (await ctx.proxy.buffer.active.getLine(0))!.getCell(1); deepStrictEqual(await cell1!.isBgDefault(), true); }); test('Ps = 90 - Set foreground color to bright Black', async () => { await ctx.proxy.write('\x1b[90m#'); const cell = await (await ctx.proxy.buffer.active.getLine(0))!.getCell(0); deepStrictEqual(await cell!.isFgPalette(), true); deepStrictEqual(await cell!.getFgColor(), 8); }); test('Ps = 91 - Set foreground color to bright Red', async () => { await ctx.proxy.write('\x1b[91m#'); const cell = await (await ctx.proxy.buffer.active.getLine(0))!.getCell(0); deepStrictEqual(await cell!.isFgPalette(), true); deepStrictEqual(await cell!.getFgColor(), 9); }); test('Ps = 92 - Set foreground color to bright Green', async () => { await ctx.proxy.write('\x1b[92m#'); const cell = await (await ctx.proxy.buffer.active.getLine(0))!.getCell(0); deepStrictEqual(await cell!.isFgPalette(), true); deepStrictEqual(await cell!.getFgColor(), 10); }); test('Ps = 93 - Set foreground color to bright Yellow', async () => { await ctx.proxy.write('\x1b[93m#'); const cell = await (await ctx.proxy.buffer.active.getLine(0))!.getCell(0); deepStrictEqual(await cell!.isFgPalette(), true); deepStrictEqual(await cell!.getFgColor(), 11); }); test('Ps = 94 - Set foreground color to bright Blue', async () => { await ctx.proxy.write('\x1b[94m#'); const cell = await (await ctx.proxy.buffer.active.getLine(0))!.getCell(0); deepStrictEqual(await cell!.isFgPalette(), true); deepStrictEqual(await cell!.getFgColor(), 12); }); test('Ps = 95 - Set foreground color to bright Magenta', async () => { await ctx.proxy.write('\x1b[95m#'); const cell = await (await ctx.proxy.buffer.active.getLine(0))!.getCell(0); deepStrictEqual(await cell!.isFgPalette(), true); deepStrictEqual(await cell!.getFgColor(), 13); }); test('Ps = 96 - Set foreground color to bright Cyan', async () => { await ctx.proxy.write('\x1b[96m#'); const cell = await (await ctx.proxy.buffer.active.getLine(0))!.getCell(0); deepStrictEqual(await cell!.isFgPalette(), true); deepStrictEqual(await cell!.getFgColor(), 14); }); test('Ps = 97 - Set foreground color to bright White', async () => { await ctx.proxy.write('\x1b[97m#'); const cell = await (await ctx.proxy.buffer.active.getLine(0))!.getCell(0); deepStrictEqual(await cell!.isFgPalette(), true); deepStrictEqual(await cell!.getFgColor(), 15); }); test('Ps = 100 - Set background color to bright Black', async () => { await ctx.proxy.write('\x1b[100m#'); const cell = await (await ctx.proxy.buffer.active.getLine(0))!.getCell(0); deepStrictEqual(await cell!.isBgPalette(), true); deepStrictEqual(await cell!.getBgColor(), 8); }); test('Ps = 101 - Set background color to bright Red', async () => { await ctx.proxy.write('\x1b[101m#'); const cell = await (await ctx.proxy.buffer.active.getLine(0))!.getCell(0); deepStrictEqual(await cell!.isBgPalette(), true); deepStrictEqual(await cell!.getBgColor(), 9); }); test('Ps = 102 - Set background color to bright Green', async () => { await ctx.proxy.write('\x1b[102m#'); const cell = await (await ctx.proxy.buffer.active.getLine(0))!.getCell(0); deepStrictEqual(await cell!.isBgPalette(), true); deepStrictEqual(await cell!.getBgColor(), 10); }); test('Ps = 103 - Set background color to bright Yellow', async () => { await ctx.proxy.write('\x1b[103m#'); const cell = await (await ctx.proxy.buffer.active.getLine(0))!.getCell(0); deepStrictEqual(await cell!.isBgPalette(), true); deepStrictEqual(await cell!.getBgColor(), 11); }); test('Ps = 104 - Set background color to bright Blue', async () => { await ctx.proxy.write('\x1b[104m#'); const cell = await (await ctx.proxy.buffer.active.getLine(0))!.getCell(0); deepStrictEqual(await cell!.isBgPalette(), true); deepStrictEqual(await cell!.getBgColor(), 12); }); test('Ps = 105 - Set background color to bright Magenta', async () => { await ctx.proxy.write('\x1b[105m#'); const cell = await (await ctx.proxy.buffer.active.getLine(0))!.getCell(0); deepStrictEqual(await cell!.isBgPalette(), true); deepStrictEqual(await cell!.getBgColor(), 13); }); test('Ps = 106 - Set background color to bright Cyan', async () => { await ctx.proxy.write('\x1b[106m#'); const cell = await (await ctx.proxy.buffer.active.getLine(0))!.getCell(0); deepStrictEqual(await cell!.isBgPalette(), true); deepStrictEqual(await cell!.getBgColor(), 14); }); test('Ps = 107 - Set background color to bright White', async () => { await ctx.proxy.write('\x1b[107m#'); const cell = await (await ctx.proxy.buffer.active.getLine(0))!.getCell(0); deepStrictEqual(await cell!.isBgPalette(), true); deepStrictEqual(await cell!.getBgColor(), 15); }); test('Ps = 38:2:Pi:Pr:Pg:Pb - Set foreground color using RGB values (colon separator)', async () => { await ctx.proxy.write('\x1b[38:2::171:205:239m#'); const cell = await (await ctx.proxy.buffer.active.getLine(0))!.getCell(0); deepStrictEqual(await cell!.isFgRGB(), true); deepStrictEqual(await cell!.getFgColor(), 0xabcdef); }); test('Ps = 38:5:Ps - Set foreground color to Ps using indexed color (colon separator)', async () => { await ctx.proxy.write('\x1b[38:5:123m#'); const cell = await (await ctx.proxy.buffer.active.getLine(0))!.getCell(0); deepStrictEqual(await cell!.isFgPalette(), true); deepStrictEqual(await cell!.getFgColor(), 123); }); test('Ps = 48:2:Pi:Pr:Pg:Pb - Set background color using RGB values (colon separator)', async () => { await ctx.proxy.write('\x1b[48:2::18:52:86m#'); const cell = await (await ctx.proxy.buffer.active.getLine(0))!.getCell(0); deepStrictEqual(await cell!.isBgRGB(), true); deepStrictEqual(await cell!.getBgColor(), 0x123456); }); test('Ps = 48:5:Ps - Set background color to Ps using indexed color (colon separator)', async () => { await ctx.proxy.write('\x1b[48:5:200m#'); const cell = await (await ctx.proxy.buffer.active.getLine(0))!.getCell(0); deepStrictEqual(await cell!.isBgPalette(), true); deepStrictEqual(await cell!.getBgColor(), 200); }); test('Ps = 38;2;Pr;Pg;Pb - Set foreground color using RGB values (semicolon separator)', async () => { await ctx.proxy.write('\x1b[38;2;171;205;239m#'); const cell = await (await ctx.proxy.buffer.active.getLine(0))!.getCell(0); deepStrictEqual(await cell!.isFgRGB(), true); deepStrictEqual(await cell!.getFgColor(), 0xabcdef); }); test('Ps = 48;2;Pr;Pg;Pb - Set background color using RGB values (semicolon separator)', async () => { await ctx.proxy.write('\x1b[48;2;18;52;86m#'); const cell = await (await ctx.proxy.buffer.active.getLine(0))!.getCell(0); deepStrictEqual(await cell!.isBgRGB(), true); deepStrictEqual(await cell!.getBgColor(), 0x123456); }); }); test.skip('CSI > Pp [; Pv] m - XTMODKEYS: Set/reset key modifier options, xterm', () => { // TODO: Implement }); test.skip('CSI ? Pp m - XTQMODKEYS: Query key modifier options, xterm', () => { // TODO: Implement }); test.describe('CSI Ps n - DSR: Device Status Report', () => { test('Status Report - CSI 5 n', async () => { await ctx.proxy.write('\x1b[5n'); deepStrictEqual(recordedData, ['\x1b[0n']); }); test('Report Cursor Position (CPR) - CSI 6 n', async () => { await ctx.proxy.write('\n\nfoo'); await pollFor(ctx.page, async () => [ await ctx.proxy.buffer.active.cursorY, await ctx.proxy.buffer.active.cursorX ], [2, 3]); await ctx.proxy.write('\x1b[6n'); deepStrictEqual(recordedData, ['\x1b[3;4R']); }); test('Report Cursor Position (DECXCPR) - CSI ? 6 n', async () => { await ctx.proxy.write('\n\nfoo'); await pollFor(ctx.page, async () => [ await ctx.proxy.buffer.active.cursorY, await ctx.proxy.buffer.active.cursorX ], [2, 3]); await ctx.proxy.write('\x1b[?6n'); deepStrictEqual(recordedData, ['\x1b[?3;4R']); }); }); test.skip('CSI > Ps n - Disable key modifier options, xterm', () => { // TODO: Implement }); test.describe('CSI ? Ps n - DECDSR: Device Status Report (DEC-specific)', () => { test('Color Scheme Query - CSI ? 996 n (dark theme)', async () => { // Default theme has dark background (#000000) and light foreground (#ffffff) await ctx.proxy.write('\x1b[?996n'); deepStrictEqual(recordedData, ['\x1b[?997;1n']); }); test('Color Scheme Query - CSI ? 996 n (light theme)', async () => { recordedData.length = 0; await ctx.page.evaluate(`window.term.options.theme = { background: '#ffffff', foreground: '#000000' }`); await ctx.proxy.write('\x1b[?996n'); deepStrictEqual(recordedData, ['\x1b[?997;2n']); // Restore default theme await ctx.page.evaluate(`window.term.options.theme = { background: '#000000', foreground: '#ffffff' }`); }); test('Color Scheme Query disabled via vtExtensions.colorSchemeQuery', async () => { recordedData.length = 0; await ctx.page.evaluate(`window.term.options.vtExtensions = { colorSchemeQuery: false }`); await ctx.proxy.write('\x1b[?996n'); deepStrictEqual(recordedData, []); // Re-enable await ctx.page.evaluate(`window.term.options.vtExtensions = { colorSchemeQuery: true }`); }); }); test.skip('CSI > Ps p - XTSMPOINTER: Set resource value pointerMode, xterm', () => { // TODO: Implement }); test('CSI ! p - DECSTR: Soft terminal reset, VT220 and up.', async () => { const rows = await ctx.proxy.rows; await ctx.proxy.write('\x1b[4h\x1b[?6h\x1b[3;5r'); await pollFor(ctx.page, async () => (await ctx.proxy.modes).insertMode, true); await pollFor(ctx.page, async () => (await ctx.proxy.modes).originMode, true); await ctx.proxy.write('\x1b[!p'); await pollFor(ctx.page, async () => (await ctx.proxy.modes).insertMode, false); await pollFor(ctx.page, async () => (await ctx.proxy.modes).originMode, false); await pollFor(ctx.page, () => ctx.page.evaluate(`({ top: window.term._core._bufferService.buffer.scrollTop, bottom: window.term._core._bufferService.buffer.scrollBottom })`), { top: 0, bottom: rows - 1 }); }); test.skip('CSI Pl ; Pc " p - DECSCL: Set conformance level, VT220 and up.', () => { // TODO: Implement }); test('CSI Ps $ p - DECRQM: Request ANSI mode', async () => { await ctx.proxy.write('\x1b[4h'); recordedData.length = 0; await ctx.proxy.write('\x1b[4$p'); deepStrictEqual(recordedData, ['\x1b[4;1$y']); await ctx.proxy.write('\x1b[4l'); recordedData.length = 0; await ctx.proxy.write('\x1b[4$p'); deepStrictEqual(recordedData, ['\x1b[4;2$y']); await ctx.proxy.write('\x1b[20h'); recordedData.length = 0; await ctx.proxy.write('\x1b[20$p'); deepStrictEqual(recordedData, ['\x1b[20;1$y']); await ctx.proxy.write('\x1b[20l'); }); test('CSI ? Ps $ p - Request DEC private mode (DECRQM).', async () => { await ctx.proxy.write('\x1b[?1h'); recordedData.length = 0; await ctx.proxy.write('\x1b[?1$p'); deepStrictEqual(recordedData, ['\x1b[?1;1$y']); await ctx.proxy.write('\x1b[?1l'); recordedData.length = 0; await ctx.proxy.write('\x1b[?1$p'); deepStrictEqual(recordedData, ['\x1b[?1;2$y']); }); test.skip('CSI [Pm] # p - Push video attributes onto stack (XTPUSHSGR), xterm. This is an alias for CSI # { , used to work around language limitations of C#.', async () => { // TODO: Implement }); test('CSI > Ps q - Report xterm name and version (XTVERSION).', async () => { await ctx.proxy.write('\x1b[>q'); ok(recordedData.length === 1); ok(recordedData[0].startsWith('\x1bP>|xterm.js(')); ok(recordedData[0].endsWith('\x1b\\')); }); test.skip('CSI Ps q - Load LEDs (DECLL), VT100.', async () => { // TODO: Implement }); test('CSI Ps SP q - Set cursor style (DECSCUSR), VT520.', async () => { const getCursorMode = async () => ctx.proxy.core.evaluate(([core]) => { const modes = core.coreService.decPrivateModes; return { style: modes.cursorStyle, blink: modes.cursorBlink }; }); await ctx.proxy.write('\x1b[1 q'); deepStrictEqual(await getCursorMode(), { style: 'block', blink: true }); await ctx.proxy.write('\x1b[2 q'); deepStrictEqual(await getCursorMode(), { style: 'block', blink: false }); await ctx.proxy.write('\x1b[3 q'); deepStrictEqual(await getCursorMode(), { style: 'underline', blink: true }); await ctx.proxy.write('\x1b[4 q'); deepStrictEqual(await getCursorMode(), { style: 'underline', blink: false }); await ctx.proxy.write('\x1b[5 q'); deepStrictEqual(await getCursorMode(), { style: 'bar', blink: true }); await ctx.proxy.write('\x1b[6 q'); deepStrictEqual(await getCursorMode(), { style: 'bar', blink: false }); await ctx.proxy.write('\x1b[0 q'); deepStrictEqual(await getCursorMode(), { style: undefined, blink: undefined }); }); test('CSI Ps " q - Select character protection attribute (DECSCA), VT220.', async () => { await ctx.proxy.write('\x1b[1"q'); await ctx.proxy.write('PROT'); await ctx.proxy.write('\x1b[2"q'); await ctx.proxy.write('open'); await pollFor(ctx.page, () => getLinesAsArray(1), ['PROTopen']); await ctx.proxy.write('\x1b[1;1H\x1b[?2K'); await pollFor(ctx.page, () => getLinesAsArray(1), ['PROT']); }); test.skip('CSI # q - Pop video attributes from stack (XTPOPSGR), xterm.', async () => { // TODO: Implement }); test('CSI Ps ; Ps r - Set Scrolling Region [top;bottom] (default = full size of window) (DECSTBM), VT100.', async () => { const rows = await ctx.proxy.rows; await ctx.proxy.write('\x1b[2;4r'); await pollFor(ctx.page, () => ctx.page.evaluate(`({ top: window.term._core._bufferService.buffer.scrollTop, bottom: window.term._core._bufferService.buffer.scrollBottom })`), { top: 1, bottom: 3 }); await ctx.proxy.write('\x1b[r'); await pollFor(ctx.page, () => ctx.page.evaluate(`({ top: window.term._core._bufferService.buffer.scrollTop, bottom: window.term._core._bufferService.buffer.scrollBottom })`), { top: 0, bottom: rows - 1 }); }); test.skip('CSI ? Pm r - Restore DEC Private Mode Values (XTRESTORE), xterm.', async () => { // TODO: Implement }); test.skip('CSI Pt ; Pl ; Pb ; Pr ; Pm $ r - Change Attributes in Rectangular Area (DECCARA), VT400 and up.', async () => { // TODO: Implement }); test('CSI s - Save cursor, available only when DECLRMM is disabled (SCOSC, also ANSI.SYS).', async () => { await ctx.proxy.write('\x1b[3;4H'); await ctx.proxy.write('\x1b[s'); await ctx.proxy.write('\x1b[1;1H'); await ctx.proxy.write('\x1b[u'); await pollFor(ctx.page, () => getCursor(), { col: 3, row: 2 }); }); test.skip('CSI Pl ; Pr s - Set left and right margins (DECSLRM), VT420 and up.', async () => { // TODO: Implement }); test.skip('CSI > Ps s - Set/reset shift-escape options (XTSHIFTESCAPE), xterm.', async () => { // TODO: Implement }); test.skip('CSI ? Pm s - Save DEC Private Mode Values (XTSAVE), xterm. Ps values are the same as for DECSET.', async () => { // TODO: Implement }); test.skip('CSI > Pm t - This xterm control sets one or more features of the title modes (XTSMTITLE), xterm.', async () => { // TODO: Implement }); test.skip('CSI Ps SP t - Set warning-bell volume (DECSWBV), VT520.', async () => { // TODO: Implement }); test.skip('CSI Pt ; Pl ; Pb ; Pr ; Pm $ t - Reverse Attributes in Rectangular Area (DECRARA), VT400 and up.', async () => { // TODO: Implement }); test('CSI u - Restore cursor (SCORC, also ANSI.SYS).', async () => { await ctx.proxy.write('\x1b[4;6H'); await ctx.proxy.write('\x1b[s'); await ctx.proxy.write('\x1b[1;1H'); await ctx.proxy.write('\x1b[u'); await pollFor(ctx.page, () => getCursor(), { col: 5, row: 3 }); }); test.skip('CSI Ps SP u - Set margin-bell volume (DECSMBV), VT520.', async () => { // TODO: Implement }); test.skip('CSI Pt ; Pl ; Pb ; Pr ; Pp ; Pt ; Pl ; Pp $ v - Copy Rectangular Area (DECCRA), VT400 and up.', async () => { // TODO: Implement }); test.skip('CSI Ps $ w - Request presentation state report (DECRQPSR), VT320 and up.', async () => { // TODO: Implement }); test.skip('CSI Pt ; Pl ; Pb ; Pr \' w - Enable Filter Rectangle (DECEFR), VT420 and up.', async () => { // TODO: Implement }); test.skip('CSI Ps x - Request Terminal Parameters (DECREQTPARM).', async () => { // TODO: Implement }); test.skip('CSI Ps * x - Select Attribute Change Extent (DECSACE), VT420 and up.', async () => { // TODO: Implement }); test.skip('CSI Pc ; Pt ; Pl ; Pb ; Pr $ x - Fill Rectangular Area (DECFRA), VT420 and up.', async () => { // TODO: Implement }); test.skip('CSI Ps # y - Select checksum extension (XTCHECKSUM), xterm.', async () => { // TODO: Implement }); test.skip('CSI Pi ; Pg ; Pt ; Pl ; Pb ; Pr * y - Request Checksum of Rectangular Area (DECRQCRA), VT420 and up.', async () => { // TODO: Implement }); test.skip('CSI Ps ; Pu \' z - Enable Locator Reporting (DECELR).', async () => { // TODO: Implement }); test.skip('CSI Pt ; Pl ; Pb ; Pr $ z - Erase Rectangular Area (DECERA), VT400 and up.', async () => { // TODO: Implement }); test.skip('CSI Pm \' { - Select Locator Events (DECSLE).', async () => { // TODO: Implement }); test.skip('CSI [Pm] # { Push video attributes onto stack (XTPUSHSGR), xterm.', async () => { // TODO: Implement }); test.skip('CSI Pt ; Pl ; Pb ; Pr $ { - Selective Erase Rectangular Area (DECSERA), VT400 and up.', async () => { // TODO: Implement }); test.skip('CSI Pt ; Pl ; Pb ; Pr # | - Report selected graphic rendition (XTREPORTSGR), xterm.', async () => { // TODO: Implement }); test.skip('CSI Ps $ | - Select columns per page (DECSCPP), VT340.', async () => { // TODO: Implement }); test.skip('CSI Ps \' | - Request Locator Position (DECRQLP).', async () => { // TODO: Implement }); test.skip('CSI Ps * | - Select number of lines per screen (DECSNLS), VT420 and up.', async () => { // TODO: Implement }); test.skip('CSI # } - Pop video attributes from stack (XTPOPSGR), xterm.', async () => { // TODO: Implement }); test('CSI Ps \' } - Insert Ps Column(s) (default = 1) (DECIC), VT420 and up.', async () => { await ctx.proxy.resize(5, 5); await ctx.proxy.write('12345'.repeat(6)); await ctx.proxy.write('\x1b[3;3H'); await ctx.proxy.write('\x1b[\'}'); await pollFor(ctx.page, () => getLinesAsArray(6), ['12345', '12 34', '12 34', '12 34', '12 34', '12 34']); }); test.skip('CSI Ps $ } - Select active status display (DECSASD), VT320 and up.', async () => { // TODO: Implement }); test('CSI Ps \' ~ - Delete Ps Column(s) (default = 1) (DECDC), VT420 and up.', async () => { await ctx.proxy.resize(5, 5); await ctx.proxy.write('12345'.repeat(6)); await ctx.proxy.write('\x1b[3;3H'); await ctx.proxy.write('\x1b[\'~'); await pollFor(ctx.page, () => getLinesAsArray(6), ['12345', '1245', '1245', '1245', '1245', '1245']); }); test.skip('CSI Ps $ ~ - Select status line type (DECSSDT), VT320 and up.', async () => { // TODO: Implement }); test.describe('CSI Ps ; Ps ; Ps t - Window Options', () => { test('should be disabled by default', async () => { await ctx.proxy.write('\x1b[14t'); await ctx.proxy.write('\x1b[16t'); await ctx.proxy.write('\x1b[18t'); await ctx.proxy.write('\x1b[20t'); await ctx.proxy.write('\x1b[21t'); deepStrictEqual(recordedData, []); }); test('14 - GetWinSizePixels', async () => { await ctx.proxy.setOption('windowOptions', { getWinSizePixels: true }); await ctx.proxy.write('\x1b[14t'); const d = await getDimensions(); deepStrictEqual(recordedData, [`\x1b[4;${d.height};${d.width}t`]); }); test('16 - GetCellSizePixels', async () => { await ctx.proxy.setOption('windowOptions', { getCellSizePixels: true }); await ctx.proxy.write('\x1b[16t'); const d = await getDimensions(); deepStrictEqual(recordedData, [`\x1b[6;${d.cellHeight};${d.cellWidth}t`]); }); }); }); test.describe('OSC', () => { test.describe('OSC 4', () => { test('query single color', async () => { await ctx.proxy.write('\x1b]4;0;?\x07'); deepStrictEqual(recordedData, ['\x1b]4;0;rgb:2e2e/3434/3636\x1b\\']); await ctx.proxy.write('\x1b]4;77;?\x07'); deepStrictEqual(recordedData, ['\x1b]4;0;rgb:2e2e/3434/3636\x1b\\', '\x1b]4;77;rgb:5f5f/d7d7/5f5f\x1b\\']); }); test('query multiple colors', async () => { await ctx.proxy.write('\x1b]4;0;?;77;?\x07'); deepStrictEqual(recordedData, ['\x1b]4;0;rgb:2e2e/3434/3636\x1b\\', '\x1b]4;77;rgb:5f5f/d7d7/5f5f\x1b\\']); }); test('set & query single color', async () => { await ctx.proxy.write('\x1b]4;0;?\x07'); const restore = [...recordedData]; deepStrictEqual(recordedData, restore); // set new color & query await ctx.proxy.write('\x1b]4;0;rgb:01/02/03\x07\x1b]4;0;?\x07'); deepStrictEqual(recordedData, [restore[0], '\x1b]4;0;rgb:0101/0202/0303\x1b\\']); // restore should set old color await ctx.proxy.write(restore[0] + '\x1b]4;0;?\x07'); deepStrictEqual(recordedData, [restore[0], '\x1b]4;0;rgb:0101/0202/0303\x1b\\', restore[0]]); }); test('query & set colors mixed', async () => { await ctx.proxy.write('\x1b]4;0;?;77;?\x07'); const restore = [...recordedData]; recordedData.length = 0; // mixed call - change 0, query 43, change 77 await ctx.proxy.write('\x1b]4;0;rgb:01/02/03;43;?;77;#aabbcc\x07'); deepStrictEqual(recordedData, ['\x1b]4;43;rgb:0000/d7d7/afaf\x1b\\']); recordedData.length = 0; // query new values for 0 + 77 await ctx.proxy.write('\x1b]4;0;?;77;?\x07'); deepStrictEqual(recordedData, ['\x1b]4;0;rgb:0101/0202/0303\x1b\\', '\x1b]4;77;rgb:aaaa/bbbb/cccc\x1b\\']); recordedData.length = 0; // restore old values for 0 + 77 await ctx.proxy.write(restore[0] + restore[1] + '\x1b]4;0;?;77;?\x07'); deepStrictEqual(recordedData, restore); }); }); test.describe('OSC 4 & 104', () => { test('change & restore single color', async () => { // test for some random color slots for (const i of [0, 43, 77, 255]) { await ctx.proxy.write(`\x1b]4;${i};?\x07`); const restore = [...recordedData]; await ctx.proxy.write(`\x1b]4;${i};rgb:01/02/03\x07\x1b]4;${i};?\x07`); deepStrictEqual(recordedData, [restore[0], `\x1b]4;${i};rgb:0101/0202/0303\x1b\\`]); // restore slot color await ctx.proxy.write(`\x1b]104;${i}\x07\x1b]4;${i};?\x07`); deepStrictEqual(recordedData, [restore[0], `\x1b]4;${i};rgb:0101/0202/0303\x1b\\`, restore[0]]); recordedData.length = 0; } }); test('restore multiple at once', async () => { // change 3 random slots await ctx.proxy.write(`\x1b]4;0;?;43;?;77;?\x07`); const restore = [...recordedData]; recordedData.length = 0; await ctx.proxy.write(`\x1b]4;0;rgb:01/02/03;43;#aabbcc;77;#123456\x07`); // restore specific slots await ctx.proxy.write(`\x1b]104;0;43;77\x07` + `\x1b]4;0;?;43;?;77;?\x07`); deepStrictEqual(recordedData, restore); }); test('restore full table', async () => { // change 3 random slots await ctx.proxy.write(`\x1b]4;0;?;43;?;77;?\x07`); const restore = [...recordedData]; recordedData.length = 0; await ctx.proxy.write(`\x1b]4;0;rgb:01/02/03;43;#aabbcc;77;#123456\x07`); // restore all await ctx.proxy.write(`\x1b]104\x07` + `\x1b]4;0;?;43;?;77;?\x07`); deepStrictEqual(recordedData, restore); }); }); test.describe('OSC 10 & 11 + 110 | 111 | 112', () => { test('query FG color', async () => { await ctx.proxy.write('\x1b]10;?\x07'); deepStrictEqual(recordedData, ['\x1b]10;rgb:ffff/ffff/ffff\x1b\\']); }); test('query BG color', async () => { await ctx.proxy.write('\x1b]11;?\x07'); deepStrictEqual(recordedData, ['\x1b]11;rgb:0000/0000/0000\x1b\\']); }); test('query FG & BG color in one call', async () => { await ctx.proxy.write('\x1b]10;?;?\x07'); deepStrictEqual(recordedData, ['\x1b]10;rgb:ffff/ffff/ffff\x1b\\', '\x1b]11;rgb:0000/0000/0000\x1b\\']); }); test('set & query FG', async () => { await ctx.proxy.write('\x1b]10;rgb:1/2/3\x07\x1b]10;?\x07'); deepStrictEqual(recordedData, ['\x1b]10;rgb:1111/2222/3333\x1b\\']); await ctx.proxy.write('\x1b]10;#ffffff\x07\x1b]10;?\x07'); deepStrictEqual(recordedData, ['\x1b]10;rgb:1111/2222/3333\x1b\\', '\x1b]10;rgb:ffff/ffff/ffff\x1b\\']); }); test('set & query BG', async () => { await ctx.proxy.write('\x1b]11;rgb:1/2/3\x07\x1b]11;?\x07'); deepStrictEqual(recordedData, ['\x1b]11;rgb:1111/2222/3333\x1b\\']); await ctx.proxy.write('\x1b]11;#000000\x07\x1b]11;?\x07'); deepStrictEqual(recordedData, ['\x1b]11;rgb:1111/2222/3333\x1b\\', '\x1b]11;rgb:0000/0000/0000\x1b\\']); }); test('set & query cursor color', async () => { await ctx.proxy.write('\x1b]12;rgb:1/2/3\x07\x1b]12;?\x07'); deepStrictEqual(recordedData, ['\x1b]12;rgb:1111/2222/3333\x1b\\']); await ctx.proxy.write('\x1b]12;#ffffff\x07\x1b]12;?\x07'); deepStrictEqual(recordedData, ['\x1b]12;rgb:1111/2222/3333\x1b\\', '\x1b]12;rgb:ffff/ffff/ffff\x1b\\']); }); test('set & query FG & BG color in one call', async () => { await ctx.proxy.write('\x1b]10;#123456;rgb:aa/bb/cc\x07\x1b]10;?;?\x07'); deepStrictEqual(recordedData, ['\x1b]10;rgb:1212/3434/5656\x1b\\', '\x1b]11;rgb:aaaa/bbbb/cccc\x1b\\']); await ctx.proxy.write('\x1b]10;#ffffff;#000000\x07'); }); test('OSC 110: restore FG color', async () => { await ctx.proxy.write('\x1b]10;rgb:1/2/3\x07\x1b]10;?\x07'); deepStrictEqual(recordedData, ['\x1b]10;rgb:1111/2222/3333\x1b\\']); recordedData.length = 0; // restore await ctx.proxy.write('\x1b]110\x07\x1b]10;?\x07'); deepStrictEqual(recordedData, ['\x1b]10;rgb:ffff/ffff/ffff\x1b\\']); }); test('OSC 111: restore BG color', async () => { await ctx.proxy.write('\x1b]11;rgb:1/2/3\x07\x1b]11;?\x07'); deepStrictEqual(recordedData, ['\x1b]11;rgb:1111/2222/3333\x1b\\']); recordedData.length = 0; // restore await ctx.proxy.write('\x1b]111\x07\x1b]11;?\x07'); deepStrictEqual(recordedData, ['\x1b]11;rgb:0000/0000/0000\x1b\\']); }); test('OSC 112: restore cursor color', async () => { await ctx.proxy.write('\x1b]12;rgb:1/2/3\x07\x1b]12;?\x07'); deepStrictEqual(recordedData, ['\x1b]12;rgb:1111/2222/3333\x1b\\']); recordedData.length = 0; // restore await ctx.proxy.write('\x1b]112\x07\x1b]12;?\x07'); deepStrictEqual(recordedData, ['\x1b]12;rgb:ffff/ffff/ffff\x1b\\']); }); }); }); test.describe('ESC', () => { test.describe('DECRC: Save cursor, ESC 7', () => { test('should save the absolute cursor position so resizing restores to the correct position', async () => { await ctx.proxy.resize(10, 2); await ctx.proxy.write('1\n\r2\n\r3\n\r4\n\r5'); await ctx.proxy.write('\x1b7\x1b[?47h'); await ctx.proxy.resize(10, 4); await ctx.proxy.write('\x1b[?47l\x1b8'); await pollFor(ctx.page, () => getCursor(), { col: 1, row: 3 }); }); }); }); }); async function getModeSnapshot(): Promise { return { modes: await ctx.proxy.modes, cursorBlink: await ctx.proxy.getOption('cursorBlink'), cols: await ctx.proxy.cols, rows: await ctx.proxy.rows, bufferType: await ctx.proxy.buffer.active.type, mouseProtocol: await ctx.proxy.core.evaluate(([core]) => core.coreMouseService.activeProtocol), mouseEncoding: await ctx.proxy.core.evaluate(([core]) => core.coreMouseService.activeEncoding) }; } async function assertNoModeChange(sequence: string): Promise { const before = await getModeSnapshot(); await ctx.proxy.write(sequence); deepStrictEqual(await getModeSnapshot(), before); } async function getLinesAsArray(count: number, start: number = 0): Promise { let text = ''; for (let i = start; i < start + count; i++) { text += `window.term.buffer.active.getLine(${i}).translateToString(true),`; } return await ctx.page.evaluate(`[${text}]`); } async function simulatePaste(text: string): Promise { const id = Math.floor(Math.random() * 1000000); await ctx.page.evaluate(` (function() { window.disposable_${id} = window.term.onData(e => window.result_${id} = e); const clipboardData = new DataTransfer(); clipboardData.setData('text/plain', '${text}'); window.term.textarea.dispatchEvent(new ClipboardEvent('paste', { clipboardData })); })(); `); const result = await ctx.page.evaluate(`window.result_${id}`); await ctx.page.evaluate(`window.disposable_${id}.dispose()`); return result; } async function dragSelection(): Promise { await ctx.proxy.clearSelection(); const coords: { left: number, top: number, bottom: number, right: number } = await ctx.page.evaluate(` (function() { const rect = window.term.element.getBoundingClientRect(); return { left: rect.left, top: rect.top, bottom: rect.bottom, right: rect.right }; })(); `); await ctx.page.mouse.click((coords.left + coords.right) / 2, (coords.top + coords.bottom) / 2); await ctx.page.mouse.down(); await ctx.page.mouse.move((coords.left + coords.right) / 2, (coords.top + coords.bottom) / 4); await ctx.page.mouse.up(); return (await ctx.proxy.getSelection()).length; } async function getCursor(): Promise<{ col: number, row: number }> { return { col: await ctx.proxy.buffer.active.cursorX, row: await ctx.proxy.buffer.active.cursorY }; } async function getDimensions(): Promise { const dim = await ctx.proxy.dimensions; return { cellWidth: dim!.css.cell.width.toFixed(0), cellHeight: dim!.css.cell.height.toFixed(0), width: dim!.css.canvas.width.toFixed(0), height: dim!.css.canvas.height.toFixed(0) }; }