import { getTestRunner } from './runtime.ts'; import { Ansi, KeyCommand, readKey } from './ansi.ts'; import { Document, Row } from './document.ts'; import Buffer from './buffer.ts'; import { chars, ctrl_key, is_ascii, is_control, noop, ord, strlen, truncate, } from './utils.ts'; import { Editor } from './editor.ts'; import { defaultTerminalSize } from './termios.ts'; const t = await getTestRunner(); const testObj = { 'ANSI::ANSI utils': { 'Ansi.moveCursor': () => { t.assertEquals(Ansi.moveCursor(1, 2), '\x1b[2;3H'); }, 'Ansi.moveCursorForward': () => { t.assertEquals(Ansi.moveCursorForward(2), '\x1b[2C'); }, 'Ansi.moveCursorDown': () => { t.assertEquals(Ansi.moveCursorDown(7), '\x1b[7B'); }, }, 'ANSI::readKey()': { 'readKey passthrough': () => { // Ignore unhandled escape sequences t.assertEquals(readKey('\x1b[]'), '\x1b[]'); // Pass explicitly mapped values right through t.assertEquals(readKey(KeyCommand.ArrowUp), KeyCommand.ArrowUp); t.assertEquals(readKey(KeyCommand.Home), KeyCommand.Home); t.assertEquals(readKey(KeyCommand.Delete), KeyCommand.Delete); }, 'readKey Home': () => { ['\x1b[1~', '\x1b[7~', '\x1b[H', '\x1bOH'].forEach((code) => { t.assertEquals(readKey(code), KeyCommand.Home); }); }, 'readKey End': () => { ['\x1b[4~', '\x1b[8~', '\x1b[F', '\x1bOF'].forEach((code) => { t.assertEquals(readKey(code), KeyCommand.End); }); }, }, Buffer: { 'Buffer exists': () => { const b = new Buffer(); t.assertInstanceOf(b, Buffer); t.assertEquals(b.strlen(), 0); }, 'Buffer.appendLine': () => { const b = new Buffer(); // Carriage return and line feed b.appendLine(); t.assertEquals(b.strlen(), 2); b.clear(); t.assertEquals(b.strlen(), 0); b.appendLine('foo'); t.assertEquals(b.strlen(), 5); }, 'Buffer.append': () => { const b = new Buffer(); b.append('foobar'); t.assertEquals(b.strlen(), 6); b.clear(); b.append('foobar', 3); t.assertEquals(b.strlen(), 3); }, 'Buffer.flush': async () => { const b = new Buffer(); b.appendLine('foobarbaz' + Ansi.ClearLine); t.assertEquals(b.strlen(), 14); await b.flush(); t.assertEquals(b.strlen(), 0); }, }, Document: { 'Document.empty': () => { const doc = Document.empty(); t.assertEquals(doc.numRows, 0); t.assertTrue(doc.isEmpty()); t.assertEquals(doc.row(0), null); }, 'Document.appendRow': () => { const doc = Document.empty(); doc.appendRow('foobar'); t.assertEquals(doc.numRows, 1); t.assertFalse(doc.isEmpty()); t.assertInstanceOf(doc.row(0), Row); }, }, 'Document::Row': { 'new Row': () => { const row = new Row(); t.assertEquals(row.toString(), ''); }, }, Editor: { 'new Editor': () => { const e = new Editor(defaultTerminalSize); t.assertInstanceOf(e, Editor); }, }, 'Util::Misc fns': { 'noop fn': () => { t.assertExists(noop); t.assertEquals(noop(), undefined); }, }, 'Util::String fns': { 'ord() returns 256 on invalid string': () => { t.assertEquals(ord(''), 256); }, 'ord() returns number on valid string': () => { t.assertEquals(ord('a'), 97); }, 'chars() properly splits strings into unicode characters': () => { t.assertEquals(chars('😺😸😹'), ['😺', '😸', '😹']); }, 'ctrl_key() returns expected values': () => { const ctrl_a = ctrl_key('a'); t.assertTrue(is_control(ctrl_a)); t.assertEquals(ctrl_a, String.fromCodePoint(0x01)); const invalid = ctrl_key('😺'); t.assertFalse(is_control(invalid)); t.assertEquals(invalid, '😺'); }, 'is_ascii() properly discerns ascii chars': () => { t.assertTrue(is_ascii('asjyverkjhsdf1928374')); t.assertFalse(is_ascii('😺acalskjsdf')); t.assertFalse(is_ascii('ab😺ac')); }, 'is_control() works as expected': () => { t.assertFalse(is_control('abc')); t.assertTrue(is_control(String.fromCodePoint(0x01))); t.assertFalse(is_control('😺')); }, 'strlen() returns expected length for ascii strings': () => { t.assertEquals(strlen('abc'), 'abc'.length); }, 'strlen() returns expected length for multibyte characters': () => { t.assertEquals(strlen('😺😸😹'), 3); t.assertNotEquals('😺😸😹'.length, strlen('😺😸😹')); // Skin tone modifier + base character t.assertEquals(strlen('🀰🏼'), 2); t.assertNotEquals('🀰🏼'.length, strlen('🀰🏼')); // This has 4 sub-characters, and 3 zero-width-joiners t.assertEquals(strlen('πŸ‘¨β€πŸ‘©β€πŸ‘§β€πŸ‘¦'), 7); t.assertNotEquals('πŸ‘¨β€πŸ‘©β€πŸ‘§β€πŸ‘¦'.length, strlen('πŸ‘¨β€πŸ‘©β€πŸ‘§β€πŸ‘¦')); }, 'truncate() shortens strings': () => { t.assertEquals(truncate('😺😸😹', 1), '😺'); t.assertEquals(truncate('😺😸😹', 5), '😺😸😹'); t.assertEquals(truncate('πŸ‘¨β€πŸ‘©β€πŸ‘§β€πŸ‘¦', 5), 'πŸ‘¨β€πŸ‘©β€πŸ‘§'); }, }, }; t.testSuite(testObj);