Skip to content

Commit

Permalink
add types
Browse files Browse the repository at this point in the history
  • Loading branch information
ljharb committed Feb 29, 2024
1 parent 91a83b6 commit decd5d2
Show file tree
Hide file tree
Showing 14 changed files with 548 additions and 36 deletions.
1 change: 1 addition & 0 deletions .github/workflows/node-aught.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,4 @@ jobs:
range: '< 10'
type: minors
command: npm run tests-only
skip-ls-check: true
1 change: 1 addition & 0 deletions bin/import-or-require.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ const { extname: extnamePath } = require('path');
const { pathToFileURL } = require('url');
const getPackageType = require('get-package-type');

/** @type {(file: string) => undefined | Promise<unknown>} */
// eslint-disable-next-line consistent-return
module.exports = function importOrRequire(file) {
const ext = extnamePath(file);
Expand Down
2 changes: 2 additions & 0 deletions bin/tape
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ var hasImport = require('has-dynamic-import');

var tape = require('../');

/** @type {(hasSupport: boolean) => Promise<void> | void} */
function importFiles(hasSupport) {
if (!hasSupport) {
return files.forEach(function (x) { require(x); });
Expand All @@ -104,6 +105,7 @@ function importFiles(hasSupport) {

tape.wait();

/** @type {null | undefined | Promise<unknown>} */
var filesPromise = files.reduce(function (promise, file) {
return promise ? promise.then(function () {
return importOrRequire(file);
Expand Down
72 changes: 72 additions & 0 deletions index.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import through = require('@ljharb/through');

import Test = require('./lib/test');

namespace tape {
export type TestOptions = {
objectPrintDepth?: number | undefined;
skip?: boolean | undefined;
timeout?: number | undefined;
todo?: boolean | undefined;
};

export interface AssertOptions {
skip?: boolean | string | undefined;
todo?: boolean | string | undefined;
message?: string | undefined;
actual?: unknown;
expected?: unknown;
exiting?: boolean;
};

export interface TestCase {
(test: Test): void | Promise<void>;
};

export interface StreamOptions {
objectMode?: boolean | undefined;
};

declare function createStream(opts?: StreamOptions): ReturnType<through>;

export type CreateStream = typeof createStream;

export type HarnessEventHandler = (cb: Test.SyncCallback, ...rest: unknown[]) => void;

function only(name: string, cb: tape.TestCase): void;
function only(name: string, opts: tape.TestOptions, cb: tape.TestCase): void;
function only(cb: tape.TestCase): void;
function only(opts: tape.TestOptions, cb: tape.TestCase): void;

export type Harness = {
(this: ThisType<Test>, ...args: Parameters<Test>): Test;
run?: () => void;
only: typeof only;
_exitCode: number;
_results: Results;
_tests: Test[];
close: () => void;
createStream: CreateStream;
onFailure: HarnessEventHandler;
onFinish: HarnessEventHandler;
};

export type HarnessConfig = {
autoclose?: boolean;
noOnly?: boolean;
stream?: NodeJS.WritableStream;
exit?: boolean;
} & StreamOptions;

declare function createHarness(conf_?: HarnessConfig): Harness;
Test = Test;
test = typeof tape;
skip = Test['skip'];
}

declare function tape(this: tape.Harness, name: string, cb: Test.Callback): Test;
declare function tape(this: tape.Harness, name: string, opts: tape.TestOptions, cb: Test.Callback): Test;
declare function tape(this: tape.Harness, opts?: tape.TestOptions): Test;
declare function tape(this: tape.Harness, opts?: tape.TestOptions, cb: Test.Callback): Test;

export = tape;
38 changes: 30 additions & 8 deletions index.js
Original file line number Diff line number Diff line change
@@ -1,20 +1,28 @@
'use strict';

var defined = require('defined');
var through = require('@ljharb/through');

var createDefaultStream = require('./lib/default_stream');
var Test = require('./lib/test');
var Results = require('./lib/results');
var through = require('@ljharb/through');

var canEmitExit = typeof process !== 'undefined' && process
// @ts-expect-error i think old browserify uses `process.browser`
&& typeof process.on === 'function' && process.browser !== true;
var canExit = typeof process !== 'undefined' && process
&& typeof process.exit === 'function';

/** @typedef {import('.')} Tape */
/** @typedef {ThisParameterType<Tape>} Harness */
/** @typedef {import('.').HarnessConfig} HarnessConfig */

module.exports = (function () {
var wait = false;
/** @type {undefined | Harness} */
var harness;

/** @type {(opts?: HarnessConfig) => Harness} */
function getHarness(opts) {
// this override is here since tests fail via nyc if createHarness is moved upwards
if (!harness) {
Expand All @@ -24,6 +32,7 @@ module.exports = (function () {
return harness;
}

/** @type {(this: Harness, ...args: Parameters<Tape>) => ReturnType<Tape>} */
function lazyLoad() {
// eslint-disable-next-line no-invalid-this
return getHarness().apply(this, arguments);
Expand All @@ -43,11 +52,13 @@ module.exports = (function () {
return getHarness().only.apply(this, arguments);
};

/** @type {import('.').CreateStream} */
lazyLoad.createStream = function (opts) {
var options = opts || {};
if (!harness) {
var output = through();
getHarness({ stream: output, objectMode: options.objectMode });
// eslint-disable-next-line no-extra-parens
getHarness({ stream: /** @type {import('stream').Writable} */ (output), objectMode: options.objectMode });
return output;
}
return harness.createStream(options);
Expand All @@ -66,21 +77,23 @@ module.exports = (function () {
return lazyLoad;
}());

/** @type {Tape['createHarness']} */
function createHarness(conf_) {
var results = new Results({ todoIsOK: !!(process.env.TODO_IS_OK === '1') });
if (!conf_ || conf_.autoclose !== false) {
results.once('done', function () { results.close(); });
}

/** @type {(name: string, conf: import('.').TestOptions, cb: Test.Callback) => Test} */
function test(name, conf, cb) {
var t = new Test(name, conf, cb);
test._tests.push(t);

(function inspectCode(st) {
st.on('test', function sub(st_) {
st.on('test', /** @type {(st: Test) => void} */ function sub(st_) {
inspectCode(st_);
});
st.on('result', function (r) {
st.on('result', /** @type {(r: import('./lib/results').Result) => void} */ function (r) {
if (!r.todo && !r.ok && typeof r !== 'string') { test._exitCode = 1; }
});
}(t));
Expand All @@ -90,21 +103,25 @@ function createHarness(conf_) {
}
test._results = results;

test._tests = [];
/** @type {Test[]} */ test._tests = [];

/** @type {import('.').CreateStream} */
test.createStream = function (opts) {
return results.createStream(opts);
};

/** @type {import('.').HarnessEventHandler} */
test.onFinish = function (cb) {
results.on('done', cb);
};

/** @type {import('.').HarnessEventHandler} */
test.onFailure = function (cb) {
results.on('fail', cb);
};

var only = false;
/** @type {() => ReturnType<typeof test>} */
test.only = function () {
if (only) { throw new Error('there can only be one only test'); }
if (conf_ && conf_.noOnly) { throw new Error('`only` tests are prohibited'); }
Expand All @@ -117,9 +134,12 @@ function createHarness(conf_) {

test.close = function () { results.close(); };

test.run = function () {};

return test;
}

/** @type {(conf: Omit<HarnessConfig, 'autoclose'>, wait?: boolean) => Harness} */
function createExitHarness(config, wait) {
var noOnly = config.noOnly;
var objectMode = config.objectMode;
Expand All @@ -140,6 +160,7 @@ function createExitHarness(config, wait) {
var es = stream.pipe(cStream || createDefaultStream());
if (canEmitExit && es) { // in node v0.4, `es` is `undefined`
// TODO: use `err` arg?
// @ts-expect-error
// eslint-disable-next-line no-unused-vars
es.on('error', function (err) { harness._exitCode = 1; });
}
Expand Down Expand Up @@ -180,6 +201,7 @@ function createExitHarness(config, wait) {
}

module.exports.createHarness = createHarness;
module.exports.Test = Test;
module.exports.test = module.exports; // tap compat
module.exports.test.skip = Test.skip;
var moduleExports = module.exports; // this hack is needed because TS has a bug with seemingly circular exports
moduleExports.Test = Test;
moduleExports.test = module.exports; // tap compat
moduleExports.skip = Test.skip;
5 changes: 5 additions & 0 deletions lib/default_stream.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import type { ThroughStream } from "@ljharb/through";

declare function defaultStream(): ThroughStream;

export = defaultStream;
11 changes: 7 additions & 4 deletions lib/default_stream.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,13 @@
var through = require('@ljharb/through');
var fs = require('fs');

/** @type {import('./default_stream')} */
module.exports = function () {
var line = '';
var stream = through(write, flush);
return stream;

/** @type {(buf: unknown) => void} */
function write(buf) {
if (
buf == null // eslint-disable-line eqeqeq
Expand All @@ -16,10 +18,11 @@ module.exports = function () {
flush();
return;
}
for (var i = 0; i < buf.length; i++) {
var c = typeof buf === 'string'
? buf.charAt(i)
: String.fromCharCode(buf[i]);
var b = /** @type {string | ArrayLike<number>} */ (buf); // eslint-disable-line no-extra-parens
for (var i = 0; i < b.length; i++) {
var c = typeof b === 'string'
? b.charAt(i)
: String.fromCharCode(b[i]);
if (c === '\n') {
flush();
} else {
Expand Down
54 changes: 54 additions & 0 deletions lib/results.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import through = require('@ljharb/through');
import type { EventEmitter } from 'events';

import type { StreamOptions } from '../';
import Test = require('./test');

type Stream = ReturnType<through>;

declare class Results extends EventEmitter {
constructor(options?: { todoIsOK?: boolean });

count: number;
fail: number;
pass: number;
tests: Test[];
todo: number;
todoIsOK: boolean;
closed?: boolean;

_isRunning: boolean;
_only: Test | null;
_stream: Stream;

close(this: Results): void;
createStream(this: Results, opts?: StreamOptions): Stream;
only(this: Results, t: Test): void;
push(this: Results, t: Test): void;

_watch(this: Results, t: Test): void;
}

namespace Results {
export type Operator = string;

export type Result = {
id: number;
ok: boolean;
skip: unknown;
todo: unknown;
name?: string;
operator: undefined | Operator;
objectPrintDepth?: number;
actual?: unknown;
expected?: unknown;
error?: unknown;
functionName?: string;
file?: string;
line?: number;
column?: number;
at?: string;
};
}

export = Results;
Loading

0 comments on commit decd5d2

Please sign in to comment.