feat(tapbundle): Add global postTask (teardown) and suite lifecycle hooks (beforeAll/afterAll) to tapbundle
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
import * as plugins from './tapbundle.plugins.js';
|
||||
|
||||
import { type IPreTaskFunction, PreTask } from './tapbundle.classes.pretask.js';
|
||||
import { type IPostTaskFunction, PostTask } from './tapbundle.classes.posttask.js';
|
||||
import { TapTest, type ITestFunction } from './tapbundle.classes.taptest.js';
|
||||
import { ProtocolEmitter, type ITestEvent } from '../dist_ts_tapbundle_protocol/index.js';
|
||||
import type { ITapSettings } from './tapbundle.interfaces.js';
|
||||
@@ -9,6 +10,8 @@ import { SettingsManager } from './tapbundle.classes.settingsmanager.js';
|
||||
export interface ITestSuite {
|
||||
description: string;
|
||||
tests: TapTest<any>[];
|
||||
beforeAll?: ITestFunction<any>;
|
||||
afterAll?: ITestFunction<any>;
|
||||
beforeEach?: ITestFunction<any>;
|
||||
afterEach?: ITestFunction<any>;
|
||||
parent?: ITestSuite;
|
||||
@@ -21,85 +24,89 @@ class TestBuilder<T> {
|
||||
private _priority: 'high' | 'medium' | 'low' = 'medium';
|
||||
private _retryCount?: number;
|
||||
private _timeoutMs?: number;
|
||||
|
||||
constructor(tap: Tap<T>) {
|
||||
private _parallel: boolean = false;
|
||||
|
||||
constructor(tap: Tap<T>, parallel: boolean = false) {
|
||||
this._tap = tap;
|
||||
this._parallel = parallel;
|
||||
}
|
||||
|
||||
|
||||
tags(...tags: string[]) {
|
||||
this._tags = tags;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
priority(level: 'high' | 'medium' | 'low') {
|
||||
this._priority = level;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
retry(count: number) {
|
||||
this._retryCount = count;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
timeout(ms: number) {
|
||||
this._timeoutMs = ms;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
test(description: string, testFunction: ITestFunction<T>) {
|
||||
const test = this._tap.test(description, testFunction, 'normal');
|
||||
|
||||
const test = this._parallel
|
||||
? this._tap.testParallel(description, testFunction)
|
||||
: this._tap.test(description, testFunction, 'normal');
|
||||
|
||||
// Apply settings to the test
|
||||
if (this._tags.length > 0) {
|
||||
test.tags = this._tags;
|
||||
}
|
||||
test.priority = this._priority;
|
||||
|
||||
|
||||
if (this._retryCount !== undefined) {
|
||||
test.tapTools.retry(this._retryCount);
|
||||
}
|
||||
if (this._timeoutMs !== undefined) {
|
||||
test.timeoutMs = this._timeoutMs;
|
||||
}
|
||||
|
||||
|
||||
return test;
|
||||
}
|
||||
|
||||
|
||||
testOnly(description: string, testFunction: ITestFunction<T>) {
|
||||
const test = this._tap.test(description, testFunction, 'only');
|
||||
|
||||
|
||||
// Apply settings to the test
|
||||
if (this._tags.length > 0) {
|
||||
test.tags = this._tags;
|
||||
}
|
||||
test.priority = this._priority;
|
||||
|
||||
|
||||
if (this._retryCount !== undefined) {
|
||||
test.tapTools.retry(this._retryCount);
|
||||
}
|
||||
if (this._timeoutMs !== undefined) {
|
||||
test.timeoutMs = this._timeoutMs;
|
||||
}
|
||||
|
||||
|
||||
return test;
|
||||
}
|
||||
|
||||
|
||||
testSkip(description: string, testFunction: ITestFunction<T>) {
|
||||
const test = this._tap.test(description, testFunction, 'skip');
|
||||
|
||||
|
||||
// Apply settings to the test
|
||||
if (this._tags.length > 0) {
|
||||
test.tags = this._tags;
|
||||
}
|
||||
test.priority = this._priority;
|
||||
|
||||
|
||||
if (this._retryCount !== undefined) {
|
||||
test.tapTools.retry(this._retryCount);
|
||||
}
|
||||
if (this._timeoutMs !== undefined) {
|
||||
test.timeoutMs = this._timeoutMs;
|
||||
}
|
||||
|
||||
|
||||
return test;
|
||||
}
|
||||
}
|
||||
@@ -122,21 +129,25 @@ export class Tap<T> {
|
||||
const builder = new TestBuilder<T>(this);
|
||||
return builder.tags(...tags);
|
||||
}
|
||||
|
||||
|
||||
public priority(level: 'high' | 'medium' | 'low') {
|
||||
const builder = new TestBuilder<T>(this);
|
||||
return builder.priority(level);
|
||||
}
|
||||
|
||||
|
||||
public retry(count: number) {
|
||||
const builder = new TestBuilder<T>(this);
|
||||
return builder.retry(count);
|
||||
}
|
||||
|
||||
|
||||
public timeout(ms: number) {
|
||||
const builder = new TestBuilder<T>(this);
|
||||
return builder.timeout(ms);
|
||||
}
|
||||
|
||||
public parallel() {
|
||||
return new TestBuilder<T>(this, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* skips a test
|
||||
@@ -236,6 +247,7 @@ export class Tap<T> {
|
||||
};
|
||||
|
||||
private _tapPreTasks: PreTask[] = [];
|
||||
private _tapPostTasks: PostTask[] = [];
|
||||
private _tapTests: TapTest<any>[] = [];
|
||||
private _tapTestsOnly: TapTest<any>[] = [];
|
||||
private _currentSuite: ITestSuite | null = null;
|
||||
@@ -304,18 +316,22 @@ export class Tap<T> {
|
||||
this._tapPreTasks.push(new PreTask(descriptionArg, functionArg));
|
||||
}
|
||||
|
||||
public postTask(descriptionArg: string, functionArg: IPostTaskFunction) {
|
||||
this._tapPostTasks.push(new PostTask(descriptionArg, functionArg));
|
||||
}
|
||||
|
||||
/**
|
||||
* A parallel test that will not be waited for before the next starts.
|
||||
* @param testDescription - A description of what the test does
|
||||
* @param testFunction - A Function that returns a Promise and resolves or rejects
|
||||
*/
|
||||
public testParallel(testDescription: string, testFunction: ITestFunction<T>) {
|
||||
public testParallel(testDescription: string, testFunction: ITestFunction<T>): TapTest<T> {
|
||||
const localTest = new TapTest({
|
||||
description: testDescription,
|
||||
testFunction,
|
||||
parallel: true,
|
||||
});
|
||||
|
||||
|
||||
// Apply default settings from settings manager
|
||||
const settings = this.settingsManager.getSettings();
|
||||
if (settings.timeout !== undefined) {
|
||||
@@ -324,12 +340,14 @@ export class Tap<T> {
|
||||
if (settings.retries !== undefined) {
|
||||
localTest.tapTools.retry(settings.retries);
|
||||
}
|
||||
|
||||
|
||||
if (this._currentSuite) {
|
||||
this._currentSuite.tests.push(localTest);
|
||||
} else {
|
||||
this._tapTests.push(localTest);
|
||||
}
|
||||
|
||||
return localTest;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -360,6 +378,28 @@ export class Tap<T> {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set up a function to run once before all tests in the current suite
|
||||
*/
|
||||
public beforeAll(setupFunction: ITestFunction<any>) {
|
||||
if (this._currentSuite) {
|
||||
this._currentSuite.beforeAll = setupFunction;
|
||||
} else {
|
||||
throw new Error('beforeAll can only be used inside a describe block');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set up a function to run once after all tests in the current suite
|
||||
*/
|
||||
public afterAll(teardownFunction: ITestFunction<any>) {
|
||||
if (this._currentSuite) {
|
||||
this._currentSuite.afterAll = teardownFunction;
|
||||
} else {
|
||||
throw new Error('afterAll can only be used inside a describe block');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set up a function to run before each test in the current suite
|
||||
*/
|
||||
@@ -370,7 +410,7 @@ export class Tap<T> {
|
||||
throw new Error('beforeEach can only be used inside a describe block');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set up a function to run after each test in the current suite
|
||||
*/
|
||||
@@ -554,6 +594,11 @@ export class Tap<T> {
|
||||
console.log(failReason);
|
||||
}
|
||||
|
||||
// Run post tasks
|
||||
for (const postTask of this._tapPostTasks) {
|
||||
await postTask.run();
|
||||
}
|
||||
|
||||
// Run global afterAll hook if configured
|
||||
if (settings.afterAll) {
|
||||
try {
|
||||
@@ -597,6 +642,12 @@ export class Tap<T> {
|
||||
suiteName: suite.description
|
||||
}
|
||||
});
|
||||
|
||||
// Run beforeAll hook for this suite
|
||||
if (suite.beforeAll) {
|
||||
await suite.beforeAll(new plugins.smartpromise.Deferred().promise as any);
|
||||
}
|
||||
|
||||
// Run beforeEach from parent suites
|
||||
const beforeEachFunctions: ITestFunction<any>[] = [];
|
||||
let currentSuite: ITestSuite | null = suite;
|
||||
@@ -666,7 +717,12 @@ export class Tap<T> {
|
||||
|
||||
// Recursively run child suites
|
||||
await this._runSuite(suite, suite.children, promiseArray, context);
|
||||
|
||||
|
||||
// Run afterAll hook for this suite
|
||||
if (suite.afterAll) {
|
||||
await suite.afterAll(new plugins.smartpromise.Deferred().promise as any);
|
||||
}
|
||||
|
||||
// Emit suite:completed event
|
||||
this.emitEvent({
|
||||
eventType: 'suite:completed',
|
||||
|
||||
Reference in New Issue
Block a user