@tapjs/core
Version:
pluggable core of node-tap
417 lines • 12.3 kB
TypeScript
import type { Test } from '@tapjs/test';
import { Minipass } from 'minipass';
import { Readable } from 'node:stream';
import { FinalResults, Result as ParserResult } from 'tap-parser';
import { Base, BaseOpts } from './base.js';
import { Spawn } from './spawn.js';
import { Stdin } from './stdin.js';
import { Result, TestPoint } from './test-point.js';
import { Waiter } from './waiter.js';
import { Worker } from './worker.js';
import { IMPLICIT } from './implicit-end-sigil.js';
import { Extra, MessageExtra, TapBaseEvents, TapFile } from './index.js';
/**
* Options that can be passed to TestBase objects
*/
export interface TestBaseOpts extends BaseOpts {
/**
* The number of jobs to run in parallel. Defaults to 1
*/
jobs?: number;
/**
* Test function called when this Test is executed
* This is usually not set on the extra object, but as an argument to
* the `t.test(..)` method, just defined here so TS doesn't complain
* when we reference it in the various flow control machinery.
*
* @internal
*
* @group Internal Machinery
*/
cb?: (...args: any[]) => any;
}
/**
* Sigil to put in the queue to signal the end of all things
*/
declare const EOF: unique symbol;
export type { EOF };
/**
* Entries in the {@link @tapjs/core!test-base.TestBase#queue} awaiting
* processing
*/
export type QueueEntry = string | TestPoint | Base | typeof EOF | Waiter | [method: string, ...args: any[]] | (() => any);
/**
* the promise returned by t.test(), t.spawn(), etc.
* If a subtest was not created (because of being marked skipped,
* the parent having bailed out, etc.) then the `subtest` field
* will be set to `null`.
*/
export interface PromiseWithSubtest<S extends Base> extends Promise<FinalResults | null> {
subtest: S | null;
}
/**
* Events emitted by TestBase and inherited classes
*/
export interface TestBaseEvents extends TapBaseEvents {
/**
* Emitted when a subtest begins running
*
* @event
*/
subtestStart: [p: Base];
/**
* Emitted when a subtest is completed and no longer active, but may
* not yet have been processed by the parent test.
*
* @event
*/
subtestEnd: [p: Base];
/**
* Emitted when a subtest begins to be processed.
*
* @event
*/
subtestProcess: [p: Base];
/**
* Emitted when a subtest is added to its parent's management
*
* @event
*/
subtestAdd: [p: Base];
/**
* Emitted whenever the test has an assertion result, with the minimal
* `{ ok, message, extra }` result object.
*
* @event
*/
result: [res: Result];
/**
* Emitted when the parser emits a result, with the full parser result
* object
*
* @event
*/
assert: [res: ParserResult];
/**
* Emitted when a child test is initiated that will process stdin
* as a TAP stream
*
* @event
*/
stdin: [s: Stdin];
/**
* Emitted when a child test is initiated that will process a subprocess
* output as a TAP stream
*
* @event
*/
spawn: [s: Spawn];
/**
* Emitted when a child test is initiated that will process a node
* Worker thread's output as a TAP stream
*
* @event
*/
worker: [w: Worker];
/**
* Emitted when a child tests is initiated that replays a .tap file.
*
* @event
*/
tapFile: [tf: TapFile];
/**
* Emitted when the test is in an idle state, not waiting
* for anything, with nothing in its queue. Used by the root
* {@link @tapjs/core!tap.TAP} singleton to know when to automatically
* terminate.
*
* @event
*/
idle: [];
}
/**
* The TestBase class is the parent class of {@link @tapjs/test!index.Test},
* and passed
* to all plugins at instantiation time.
*
* This implements subtest functionality, TAP stream generation,
* lifecycle events, and only the basic pass/fail assertion methods.
*
* All other features are added with plugins.
*/
export declare class TestBase extends Base<TestBaseEvents> {
#private;
parent?: TestBase;
options: TestBaseOpts;
/**
* Attached when the Test class is instantiated from a TestBase,
* as a reference to the final plugged-in Test instance.
* If TestBase is used directly (outside the context of a plugin)
* or during plugin setup time, this will be undefined, so watch out.
*
* @group Test Reflection
*/
t: Test;
/**
* A promise that resolves when the test is done.
*
* @group Internal Machinery
*/
donePromise?: Promise<any> & {
tapAbortPromise?: () => void;
};
/**
* The number of subtests to run in parallel, if allowed
*
* @group Test Lifecycle Management
*/
jobs: number;
/**
* Array of all subtests that have been added/scheduled,
* and have not yet completed.
*
* @group Internal Machinery
*/
subtests: Base[];
/**
* The pool of parallel tests currently in process
*
* @group Internal Machinery
*/
pool: Set<Base>;
/**
* Queue of items awaiting processing. Can be any
* {@link @tapjs/core!test-base.QueueEntry} item.
*
* @group Internal Machinery
*/
queue: QueueEntry[];
/**
* Function that will get this test as an argument when it is processed
*
* @internal
*
* @group Internal Machinery
*/
cb?: (...args: any[]) => any;
/**
* The count of all assertions made directly on this test.
*
* @group Test Reflection
*/
count: number;
/**
* Set true when {@link @tapjs/core!test-base.TestBase#end} is called
*/
ended: boolean;
/**
* Show diagnostics for this test. A value of `null` means that
* diagnostics will be shown only if the test is failing.
*/
diagnostic: null | boolean;
/**
* Subtests that are currently in process.
*
* @group Internal Machinery
*/
activeSubtests: Set<Base>;
/**
* Count of all asserts in this and all child tests,
* excluding child test summary points
*
* @group Test Reflection
*/
get assertTotals(): import("./counts.js").Counts;
/**
* true if the test has printed at least one TestPoint
*
* @group Test Reflection
*/
get printedResult(): boolean;
/**
* true if the test is currently waiting for something to finish
*
* @group Test Reflection
*/
get occupied(): boolean;
constructor(options: TestBaseOpts);
/**
* immediately exit this and all parent tests with a TAP
* Bail out! message.
*
* @group Test Lifecycle Management
*/
bailout(message?: string): void;
/**
* output a TAP comment, formatted like console.log()
*
* If the test is currently awaiting a child test, it will be deferred
* until after the child test completes.
*
* If the test is already completed, the comment will be emitted
* on the parent, or if no parent is available, it will be printed
* to standard output.
*/
comment(...args: any[]): void;
/**
* Called when the test times out.
* Options are passed as diagnostics to the threw() method
*
* @internal
*
* @group Internal Machinery
*/
timeout(options?: Extra & {
expired?: string;
}): void;
/**
* Set TAP pragma configs to affect the behavior of the parser.
* Only `strict` is supported by the parser.
*/
pragma(set: {
[k: string]: boolean;
}): void;
/**
* Specify the number of Test Points expected by this test.
* Outputs a TAP plan line.
*
* @group Test Lifecycle Management
*/
plan(n: number, comment?: string): void;
plan(n: number, comment: string, implicit: typeof IMPLICIT): void;
/**
* A passing (ok) Test Point.
*
* @group Assertion Methods
*/
pass(...[msg, extra]: MessageExtra): boolean;
/**
* A failing (not ok) Test Point
*
* @group Assertion Methods
*/
fail(...[msg, extra]: MessageExtra): boolean;
/**
* The current assertion being processed. Set at the start of all
* assertions, and used for calculating stack traces.
*
* @group Internal Machinery
*/
get currentAssert(): undefined | Function | ((...a: any[]) => any);
set currentAssert(fn: undefined | Function | ((...a: any[]) => any));
/**
* Explicitly mark the test as completed, outputting the TAP plan line if
* needed.
*
* This is not required to be called if the test function returns a promise,
* or if a plan is explicitly declared and eventually fulfilled.
*
* @group Test Lifecycle Management
*/
end(implicit?: typeof IMPLICIT): this;
/**
* Await the end of a Promise before proceeding.
* The supplied callback is called with the Waiter object.
*
* This is internal, used in some plugins when a promise must be awaited
* before proceeding. In normal test usage, it's usually best to simply use
* an async test function and `await` promises as normal.
*
* @internal
*
* @group Internal Machinery
*/
waitOn(promise: Promise<any | void>, cb?: (w: Waiter) => any, expectReject?: boolean): Promise<void>;
/**
* The full name of the test, starting with the main script name,
* and including all parent names.
*/
get fullname(): string;
/**
* True if the test is currently in an idle state
*/
get idle(): boolean;
/**
* The main function that starts a test running. Generally no need
* to call this directly.
*
* @internal
*
* @group Internal Machinery
*/
main(cb: () => void): void;
/**
* Parse stdin as the only tap stream (ie, not as a child test)
* If used, then no other subtests or assertions are allowed.
*
* @group Subtest Methods
*/
stdinOnly<T extends BaseOpts>(extra?: T & {
tapStream?: Readable | Minipass<string | Buffer>;
}): void;
/**
* Mount a subtest, using this Test object as a harness.
* Exposed so that it can be used by some builtin plugins, but perhaps
* the least convenient way imaginable to create subtests. Just use
* `t.test()` to do that, it's much easier.
*
* @group Subtest Methods
*
* @internal
*/
sub<T extends Base, O extends BaseOpts>(Class: {
new (options: O): T;
}, extra?: O | TestBaseOpts, caller?: (...a: any[]) => unknown): PromiseWithSubtest<T>;
/**
* Method called when an unrecoverable error is encountered in a test.
*
* Typically, in tests you would not call this, you'd just actually throw
* an error.
*
* @internal
*
* @group Internal Machinery
*/
threw(er: any, extra?: Extra, proxy?: boolean): Extra | void | undefined;
/**
* Method called when the parser encounters a bail out
*
* To listen to bailout events, listen to the
* {@link @tapjs/core!base.TapBaseEvents#bailout} event:
*
* ```ts
* t.on('bailout', message => {
* // test bailed out!
* })
* ```
*
* @internal
*
* @group Internal Machinery
*/
onbail(message?: string): void;
/**
* Called when a test times out or bails out, or the process ends,
* marking all currently active or queued subtests as incomplete.
*
* No need to ever call this directly, exposed so that it can be extended by
* {@link @tapjs/core!spawn.Spawn} and {@link @tapjs/core!worker.Worker},
* which have special behaviors that are required when a process hangs
* indefinitely.
*
* @internal
*
* @group Internal Machinery
*/
endAll(sub?: boolean): void;
/**
* Return true if the child test represented by the options object
* should be skipped. Extended by the `@tapjs/filter` plugin.
*
* @internal
*
* @group Internal Machinery
*/
shouldSkipChild<O extends BaseOpts>(extra: O | TestBaseOpts | BaseOpts): boolean;
}
//# sourceMappingURL=test-base.d.ts.map