Use node apis for test setup, refactor a bunch of runtime stuff
All checks were successful
timw4mail/scroll/pipeline/head This commit looks good

This commit is contained in:
Timothy Warren 2024-07-11 17:27:49 -04:00
parent b2169cf54b
commit 2c21bf0c9b
14 changed files with 155 additions and 171 deletions

View File

@ -3,10 +3,9 @@
*/ */
import { IRuntime, RunTimeType } from '../common/runtime.ts'; import { IRuntime, RunTimeType } from '../common/runtime.ts';
import TerminalIO from '../tsx/terminal_io.ts'; import { process } from '../common/runtime/node.ts';
import FileIO from '../tsx/file_io.ts'; import TerminalIO from '../common/runtime/terminal_io.ts';
import FileIO from '../common/runtime/file_io.ts';
import process from 'node:process';
/** /**
* The Bun Runtime implementation * The Bun Runtime implementation

View File

@ -1,34 +1,19 @@
/** /**
* Adapt the bun test interface to the shared testing interface * Adapt the bun test interface to the shared testing interface
*/ */
import { deepStrictEqual, notStrictEqual, strictEqual } from 'node:assert';
import { describe, test } from 'bun:test'; import { describe, test } from 'bun:test';
import { ITestBase } from '../common/types.ts'; import AbstractTestBase from '../common/runtime/test_base.ts';
class BunTestBase extends AbstractTestBase {
export function testSuite(testObj: any) { public static testSuite(testObj: any): void {
Object.keys(testObj).forEach((group) => { Object.keys(testObj).forEach((group) => {
describe(group, () => { describe(group, () => {
const groupObj = testObj[group]; const groupObj = testObj[group];
Object.keys(groupObj).forEach((testName) => { Object.keys(groupObj).forEach((testName) => {
test(testName, groupObj[testName]); test(testName, groupObj[testName]);
});
}); });
}); });
}); }
} }
const BunTestBase: ITestBase = {
assertEquivalent: (actual: unknown, expected: unknown) =>
deepStrictEqual(actual, expected),
assertExists: (actual: unknown) => notStrictEqual(actual, undefined),
assertFalse: (actual: boolean) => strictEqual(actual, false),
assertInstanceOf: (actual: unknown, expectedType: any) =>
strictEqual(actual instanceof expectedType, true),
assertNotEquals: (actual: unknown, expected: unknown) =>
notStrictEqual(actual, expected),
assertEquals: (actual: unknown, expected: unknown) =>
strictEqual(actual, expected),
assertTrue: (actual: boolean) => strictEqual(actual, true),
testSuite,
};
export default BunTestBase; export default BunTestBase;

View File

@ -1,7 +1,7 @@
import Row from './row.ts'; import Row from './row.ts';
import { arrayInsert, maxAdd, minSub } from './fns.ts'; import { arrayInsert, maxAdd, minSub } from './fns.ts';
import Option, { None, Some } from './option.ts'; import Option, { None, Some } from './option.ts';
import { getRuntime, logDebug } from './runtime.ts'; import { getRuntime, logDebug, logWarning } from './runtime.ts';
import { Position, SearchDirection } from './types.ts'; import { Position, SearchDirection } from './types.ts';
export class Document { export class Document {
@ -17,7 +17,7 @@ export class Document {
this.dirty = false; this.dirty = false;
} }
get numRows(): number { public get numRows(): number {
return this.#rows.length; return this.#rows.length;
} }
@ -66,6 +66,10 @@ export class Document {
direction: SearchDirection = SearchDirection.Forward, direction: SearchDirection = SearchDirection.Forward,
): Option<Position> { ): Option<Position> {
if (at.y >= this.numRows) { if (at.y >= this.numRows) {
logWarning('Trying to search beyond the end of the current file', {
at,
document: this,
});
return None; return None;
} }

View File

@ -1,6 +1,6 @@
import process from 'node:process'; import { process } from './runtime/node.ts';
import { readKey } from './fns.ts'; import { readKey } from './fns.ts';
import { getRuntime, logError } from './runtime.ts'; import { getRuntime, logError, logWarning } from './runtime.ts';
import Editor from './editor.ts'; import Editor from './editor.ts';
export async function main() { export async function main() {
@ -43,7 +43,9 @@ export async function main() {
for await (const char of term.inputLoop()) { for await (const char of term.inputLoop()) {
const parsed = readKey(char); const parsed = readKey(char);
if (char.length === 0 || parsed.length === 0) { if (char.length === 0 || parsed.length === 0) {
continue; logWarning('Empty input returned from runtime input loop');
return;
} }
// Process input // Process input

View File

@ -1,7 +1,7 @@
/** /**
* Functions/Methods that depend on the current runtime to function * Functions/Methods that depend on the current runtime to function
*/ */
import process from 'node:process'; import { process } from './runtime/node.ts';
import Ansi from './ansi.ts'; import Ansi from './ansi.ts';
import { IRuntime, ITerminalSize, ITestBase } from './types.ts'; import { IRuntime, ITerminalSize, ITestBase } from './types.ts';
import { noop } from './fns.ts'; import { noop } from './fns.ts';

View File

@ -1,9 +1,9 @@
import { appendFile, readFile, writeFile } from 'node:fs/promises'; import { appendFile, readFile, writeFile } from 'node:fs/promises';
import { resolve } from 'node:path'; import { resolve } from 'node:path';
import { IFileIO } from '../common/runtime.ts'; import { IFileIO } from '../runtime.ts';
const TsxFileIO: IFileIO = { const CommonFileIO: IFileIO = {
openFile: async function (path: string): Promise<string> { openFile: async function (path: string): Promise<string> {
const filePath = resolve(path); const filePath = resolve(path);
const contents = await readFile(filePath, { encoding: 'utf8' }); const contents = await readFile(filePath, { encoding: 'utf8' });
@ -22,4 +22,4 @@ const TsxFileIO: IFileIO = {
}, },
}; };
export default TsxFileIO; export default CommonFileIO;

View File

@ -0,0 +1,3 @@
import * as process from 'node:process';
export { process };

View File

@ -1,12 +1,11 @@
import process from 'node:process'; import { process } from './node.ts';
import { readKey } from '../fns.ts';
import { ITerminal, ITerminalSize } from '../types.ts';
import { readKey } from '../common/fns.ts'; const CommonTerminalIO: ITerminal = {
import { ITerminal, ITerminalSize } from '../common/types.ts';
const TsxTerminalIO: ITerminal = {
argv: (process.argv.length > 2) ? process.argv.slice(2) : [], argv: (process.argv.length > 2) ? process.argv.slice(2) : [],
inputLoop: async function* (): AsyncGenerator<Uint8Array, null> { inputLoop: async function* (): AsyncGenerator<Uint8Array, null> {
yield (await TsxTerminalIO.readStdinRaw()) ?? new Uint8Array(0); yield (await CommonTerminalIO.readStdinRaw()) ?? new Uint8Array(0);
return null; return null;
}, },
@ -19,7 +18,7 @@ const TsxTerminalIO: ITerminal = {
}); });
}, },
readStdin: async function (): Promise<string | null> { readStdin: async function (): Promise<string | null> {
const raw = await TsxTerminalIO.readStdinRaw(); const raw = await CommonTerminalIO.readStdinRaw();
return readKey(raw ?? new Uint8Array(0)); return readKey(raw ?? new Uint8Array(0));
}, },
readStdinRaw: function (): Promise<Uint8Array | null> { readStdinRaw: function (): Promise<Uint8Array | null> {
@ -38,4 +37,4 @@ const TsxTerminalIO: ITerminal = {
}, },
}; };
export default TsxTerminalIO; export default CommonTerminalIO;

View File

@ -0,0 +1,88 @@
/**
* Adapt the node test interface to the shared testing interface
*/
import { deepStrictEqual, notStrictEqual, strictEqual } from 'node:assert';
import Option from '../option.ts';
export interface ITestBase {
assertEquivalent(actual: unknown, expected: unknown): void;
assertExists(actual: unknown): void;
assertInstanceOf(actual: unknown, expectedType: any): void;
assertNotEquals(actual: unknown, expected: unknown): void;
assertEquals(actual: unknown, expected: unknown): void;
assertTrue(actual: boolean): void;
assertFalse(actual: boolean): void;
assertSome<T>(actual: Option<T>): void;
assertNone<T>(actual: Option<T>): void;
/**
* Convert the nested test object into a test suite for the current runtime
*/
testSuite(testObj: any): void;
}
abstract class AbstractTestBase implements Partial<ITestBase> {
/**
* The values (often objects) have all the same property values
*/
public static assertEquivalent(actual: unknown, expected: unknown): void {
return deepStrictEqual(actual, expected);
}
/**
* The value is not null or undefined
*/
public static assertExists(actual: unknown): void {
return notStrictEqual(actual, undefined);
}
/**
* `actual` is an object implementing `expectedType`
*/
public static assertInstanceOf(actual: unknown, expectedType: any): void {
return strictEqual(actual instanceof expectedType, true);
}
/**
* The values are not exactly equal (Different instance, type, value, etc)
*/
public static assertNotEquals(actual: unknown, expected: unknown): void {
return notStrictEqual(actual, expected);
}
/**
* The values are exactly the same
*/
public static assertEquals(actual: unknown, expected: unknown): void {
return strictEqual(actual, expected);
}
/**
* The value is true
*/
public static assertTrue(actual: boolean): void {
return strictEqual(actual, true);
}
/**
* The value is false
*/
public static assertFalse(actual: boolean): void {
return strictEqual(actual, false);
}
/**
* The value is a `Some` type `Option`
*/
public static assertSome<T>(actual: Option<T>): void {
return AbstractTestBase.assertTrue(actual.isSome());
}
/**
* The value is a `None` type `Option`
*/
public static assertNone<T>(actual: Option<T>): void {
return AbstractTestBase.assertTrue(actual.isNone());
}
}
export default AbstractTestBase;

View File

@ -1,6 +1,7 @@
import { RunTimeType } from './runtime.ts'; import { RunTimeType } from './runtime.ts';
export { Position } from './position.ts'; export { Position } from './position.ts';
export type { ITestBase } from './runtime/test_base.ts';
/** /**
* The size of terminal in rows and columns * The size of terminal in rows and columns
@ -128,66 +129,3 @@ export interface IRuntime {
*/ */
exit(code?: number): void; exit(code?: number): void;
} }
// ----------------------------------------------------------------------------
// Testing
// ----------------------------------------------------------------------------
/**
* The shared test interface, so tests can be run by both runtimes
*/
export interface ITestBase {
/**
* The values (often objects) have all the same property values
*
* @param actual
* @param expected
*/
assertEquivalent(actual: unknown, expected: unknown): void;
/**
* The value is not null or undefined
*
* @param actual
*/
assertExists(actual: unknown): void;
/**
* The value is false
*
* @param actual
*/
assertFalse(actual: boolean): void;
/**
* `actual` is an object implementing `expectedType`
*
* @param actual
* @param expectedType
*/
assertInstanceOf(actual: unknown, expectedType: any): void;
/**
* The values are not exactly equal (Different instance, type, value, etc)
*
* @param actual
* @param expected
*/
assertNotEquals(actual: unknown, expected: unknown): void;
/**
* The values are exactly the same
*
* @param actual
* @param expected
*/
assertEquals(actual: unknown, expected: unknown): void;
/**
* The value is true
*
* @param actual
*/
assertTrue(actual: boolean): void;
testSuite(testObj: any): void;
}

View File

@ -1,31 +1,15 @@
import { ITestBase } from '../common/types.ts'; // @ts-ignore The import exists, but tsc complains
import { stdAssert } from './deps.ts'; import { test } from 'node:test';
const { import AbstractTestBase from '../common/runtime/test_base.ts';
assertEquals, class DenoTestBase extends AbstractTestBase {
assertExists, public static testSuite(testObj: any) {
assertInstanceOf, Object.keys(testObj).forEach((group) => {
assertNotEquals, const groupObj = testObj[group];
assertStrictEquals, Object.keys(groupObj).forEach((testName) => {
} = stdAssert; test(testName, groupObj[testName]);
});
export function testSuite(testObj: any) {
Object.keys(testObj).forEach((group) => {
const groupObj = testObj[group];
Object.keys(groupObj).forEach((testName) => {
Deno.test(testName, groupObj[testName]);
}); });
}); }
} }
const DenoTestBase: ITestBase = {
assertEquivalent: assertEquals,
assertExists,
assertInstanceOf,
assertNotEquals,
assertEquals: assertStrictEquals,
assertTrue: (actual: boolean) => assertStrictEquals(actual, true),
assertFalse: (actual: boolean) => assertStrictEquals(actual, false),
testSuite,
};
export default DenoTestBase; export default DenoTestBase;

View File

@ -2,17 +2,16 @@
* The main entrypoint when using Tsx as the runtime * The main entrypoint when using Tsx as the runtime
*/ */
import { IRuntime, RunTimeType } from '../common/runtime.ts'; import { IRuntime, RunTimeType } from '../common/runtime.ts';
import TsxTerminalIO from './terminal_io.ts'; import { process } from '../common/runtime/node.ts';
import TsxFileIO from './file_io.ts'; import TsxTerminalIO from '../common/runtime/terminal_io.ts';
import FileIO from '../common/runtime/file_io.ts';
import process from 'node:process';
/** /**
* The Tsx Runtime implementation * The Tsx Runtime implementation
*/ */
const TsxRuntime: IRuntime = { const TsxRuntime: IRuntime = {
name: RunTimeType.Tsx, name: RunTimeType.Tsx,
file: TsxFileIO, file: FileIO,
term: TsxTerminalIO, term: TsxTerminalIO,
onEvent: (eventName: string, handler) => process.on(eventName, handler), onEvent: (eventName: string, handler) => process.on(eventName, handler),
onExit: (cb: () => void): void => { onExit: (cb: () => void): void => {

View File

@ -1,39 +1,21 @@
// @ts-nocheck: Bun is used for typescript checks, bun does not have 'node:assert' or 'node:test'
/** /**
* Adapt the node test interface to the shared testing interface * Adapt the node test interface to the shared testing interface
*/ */
import { // @ts-ignore The import exists, but tsc complains
deepStrictEqual,
notStrictEqual,
strictEqual,
} from 'node:assert/strict';
import { describe, it } from 'node:test'; import { describe, it } from 'node:test';
import { ITestBase } from '../common/types.ts'; import AbstractTestBase from '../common/runtime/test_base.ts';
export function testSuite(testObj: any) { class TsxTestBase extends AbstractTestBase {
Object.keys(testObj).forEach((group) => { public static testSuite(testObj: any): void {
describe(group, () => { Object.keys(testObj).forEach((group) => {
const groupObj = testObj[group]; describe(group, () => {
Object.keys(groupObj).forEach((testName) => { const groupObj = testObj[group];
it(testName, groupObj[testName]); Object.keys(groupObj).forEach((testName) => {
it(testName, groupObj[testName]);
});
}); });
}); });
}); }
} }
const TsxTestBase: ITestBase = {
assertEquivalent: (actual: unknown, expected: unknown) =>
deepStrictEqual(actual, expected),
assertExists: (actual: unknown) => notStrictEqual(actual, undefined),
assertFalse: (actual: boolean) => strictEqual(actual, false),
assertInstanceOf: (actual: unknown, expectedType: any) =>
strictEqual(actual instanceof expectedType, true),
assertNotEquals: (actual: unknown, expected: unknown) =>
notStrictEqual(actual, expected),
assertEquals: (actual: unknown, expected: unknown) =>
strictEqual(actual, expected),
assertTrue: (actual: boolean) => strictEqual(actual, true),
testSuite,
};
export default TsxTestBase; export default TsxTestBase;

View File

@ -13,7 +13,8 @@
"skipLibCheck": true, "skipLibCheck": true,
"composite": true, "composite": true,
"downlevelIteration": true, "downlevelIteration": true,
"allowSyntheticDefaultImports": true "esModuleInterop": false,
"allowSyntheticDefaultImports": false
}, },
"exclude": ["src/deno"] "exclude": ["src/deno"]
} }