And a bit more test refactoring and cleanup

This commit is contained in:
Timothy Warren 2023-11-16 21:22:24 -05:00
parent 2aaf1c678b
commit a1aa189e11
7 changed files with 86 additions and 83 deletions

View File

@ -52,8 +52,7 @@ deno-test:
# Create test coverage report with deno # Create test coverage report with deno
deno-coverage: deno-coverage:
deno test --allow-all --coverage=.deno-cover ./coverage.sh
deno coverage --unstable-ffi .deno-cover
# Run with deno # Run with deno
deno-run file="": deno-run file="":

View File

@ -27,8 +27,6 @@ const BunTestBase: ITestBase = {
assertStrictEquals: (actual: unknown, expected: unknown) => assertStrictEquals: (actual: unknown, expected: unknown) =>
expect(actual).toBe(expected), expect(actual).toBe(expected),
assertTrue: (actual: boolean) => expect(actual).toBe(true), assertTrue: (actual: boolean) => expect(actual).toBe(true),
test,
testGroup: describe,
testSuite, testSuite,
}; };

View File

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

View File

@ -2,7 +2,7 @@ import Ansi, { KeyCommand } from './ansi.ts';
import Buffer from './buffer.ts'; import Buffer from './buffer.ts';
import Document from './document.ts'; import Document from './document.ts';
import { IPoint, ITerminalSize, logToFile, VERSION } from './mod.ts'; import { IPoint, ITerminalSize, logToFile, VERSION } from './mod.ts';
import { ctrl_key } from './utils.ts'; import { ctrlKey } from './utils.ts';
export class Editor { export class Editor {
/** /**
@ -52,7 +52,7 @@ export class Editor {
*/ */
public processKeyPress(input: string): boolean { public processKeyPress(input: string): boolean {
switch (input) { switch (input) {
case ctrl_key('q'): case ctrlKey('q'):
this.clearScreen().then(() => {}); this.clearScreen().then(() => {});
return false; return false;

View File

@ -22,7 +22,5 @@ export interface ITestBase {
assertNotEquals(actual: unknown, expected: unknown): void; assertNotEquals(actual: unknown, expected: unknown): void;
assertStrictEquals(actual: unknown, expected: unknown): void; assertStrictEquals(actual: unknown, expected: unknown): void;
assertTrue(actual: boolean): void; assertTrue(actual: boolean): void;
test(name: string, fn: () => void, timeout?: number): void;
testGroup(name: string, fn: () => void): void;
testSuite(testObj: any): void; testSuite(testObj: any): void;
} }

View File

@ -45,7 +45,7 @@ export function strlen(s: string): number {
* *
* @param char - string to check * @param char - string to check
*/ */
export function is_ascii(char: string): boolean { export function isAscii(char: string): boolean {
return chars(char).every((char) => ord(char) < 0x80); return chars(char).every((char) => ord(char) < 0x80);
} }
@ -54,9 +54,9 @@ export function is_ascii(char: string): boolean {
* *
* @param char - a one character string to check * @param char - a one character string to check
*/ */
export function is_control(char: string): boolean { export function isControl(char: string): boolean {
const code = ord(char); const code = ord(char);
return is_ascii(char) && (code === 0x7f || code < 0x20); return isAscii(char) && (code === 0x7f || code < 0x20);
} }
/** /**
@ -64,9 +64,9 @@ export function is_control(char: string): boolean {
* *
* @param char - a one character string * @param char - a one character string
*/ */
export function ctrl_key(char: string): string { export function ctrlKey(char: string): string {
// This is the normal use case, of course // This is the normal use case, of course
if (is_ascii(char)) { if (isAscii(char)) {
const point = ord(char); const point = ord(char);
return String.fromCodePoint(point & 0x1f); return String.fromCodePoint(point & 0x1f);
} }

View File

@ -34,8 +34,6 @@ const DenoTestBase: ITestBase = {
throw new AssertionError(`actual: "${actual}" expected to be false"`); throw new AssertionError(`actual: "${actual}" expected to be false"`);
} }
}, },
test: Deno.test,
testGroup: (_name: string, fn) => fn(),
testSuite, testSuite,
}; };