Use node apis for test setup, refactor a bunch of runtime stuff
All checks were successful
timw4mail/scroll/pipeline/head This commit looks good
All checks were successful
timw4mail/scroll/pipeline/head This commit looks good
This commit is contained in:
parent
b2169cf54b
commit
2c21bf0c9b
@ -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
|
||||||
|
@ -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;
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
@ -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';
|
||||||
|
@ -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;
|
3
src/common/runtime/node.ts
Normal file
3
src/common/runtime/node.ts
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
import * as process from 'node:process';
|
||||||
|
|
||||||
|
export { process };
|
@ -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;
|
88
src/common/runtime/test_base.ts
Normal file
88
src/common/runtime/test_base.ts
Normal 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;
|
@ -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;
|
|
||||||
}
|
|
||||||
|
@ -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;
|
||||||
|
@ -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 => {
|
||||||
|
@ -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;
|
||||||
|
@ -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"]
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user