Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| b9016206ce | |||
| 8edbbd4850 | |||
| 97c91fc010 | |||
| ca08bb2e3c | |||
| 8fd114334f | |||
| c630a171b5 |
22
changelog.md
22
changelog.md
@@ -1,5 +1,27 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## 2025-11-21 - 3.1.1 - fix(tapbundle)
|
||||||
|
Pass TapTools to suite lifecycle hooks (beforeAll/afterAll) and update @push.rocks/smarts3 to ^3.0.0
|
||||||
|
|
||||||
|
- Replace usage of a Deferred promise with a TapTools instance when invoking suite.beforeAll and suite.afterAll
|
||||||
|
- Add import for TapTools in ts_tapbundle/tapbundle.classes.tap.ts
|
||||||
|
- Bump dependency @push.rocks/smarts3 from ^2.2.7 to ^3.0.0 in package.json
|
||||||
|
|
||||||
|
## 2025-11-20 - 3.1.0 - feat(tapbundle)
|
||||||
|
Add global postTask (teardown) and suite lifecycle hooks (beforeAll/afterAll) to tapbundle
|
||||||
|
|
||||||
|
- Introduce PostTask class (ts_tapbundle/tapbundle.classes.posttask.ts) and tap.postTask() API for global teardown.
|
||||||
|
- Integrate postTask execution into Tap.start() so postTasks run after all tests and before the global afterAll hook.
|
||||||
|
- Add suite-level beforeAll and afterAll support and ensure afterAll runs after child suites and their tests (changes in ts_tapbundle/tapbundle.classes.tap.ts).
|
||||||
|
- Add lifecycle tests (test/tapbundle/test.new-lifecycle.ts) verifying execution order, including parallel tests.
|
||||||
|
- Update documentation (readme.hints.md) describing Phase 1 API improvements and usage notes.
|
||||||
|
- This is additive and backward-compatible (no breaking changes).
|
||||||
|
|
||||||
|
## 2025-11-20 - 3.0.1 - fix(@push.rocks/smarts3)
|
||||||
|
Bump @push.rocks/smarts3 dependency to ^2.2.7
|
||||||
|
|
||||||
|
- Update package.json: @push.rocks/smarts3 upgraded from ^2.2.6 to ^2.2.7
|
||||||
|
|
||||||
## 2025-11-19 - 3.0.0 - BREAKING CHANGE(tapbundle_serverside)
|
## 2025-11-19 - 3.0.0 - BREAKING CHANGE(tapbundle_serverside)
|
||||||
Rename Node-specific tapbundle module to tapbundle_serverside and migrate server-side utilities
|
Rename Node-specific tapbundle module to tapbundle_serverside and migrate server-side utilities
|
||||||
|
|
||||||
|
|||||||
@@ -9,5 +9,5 @@
|
|||||||
"target": "ES2022"
|
"target": "ES2022"
|
||||||
},
|
},
|
||||||
"nodeModulesDir": true,
|
"nodeModulesDir": true,
|
||||||
"version": "3.0.0"
|
"version": "3.1.1"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@git.zone/tstest",
|
"name": "@git.zone/tstest",
|
||||||
"version": "3.0.0",
|
"version": "3.1.1",
|
||||||
"private": false,
|
"private": false,
|
||||||
"description": "a test utility to run tests that match test/**/*.ts",
|
"description": "a test utility to run tests that match test/**/*.ts",
|
||||||
"exports": {
|
"exports": {
|
||||||
@@ -48,7 +48,7 @@
|
|||||||
"@push.rocks/smartpath": "^6.0.0",
|
"@push.rocks/smartpath": "^6.0.0",
|
||||||
"@push.rocks/smartpromise": "^4.2.3",
|
"@push.rocks/smartpromise": "^4.2.3",
|
||||||
"@push.rocks/smartrequest": "^5.0.1",
|
"@push.rocks/smartrequest": "^5.0.1",
|
||||||
"@push.rocks/smarts3": "^2.2.6",
|
"@push.rocks/smarts3": "^3.0.0",
|
||||||
"@push.rocks/smartshell": "^3.3.0",
|
"@push.rocks/smartshell": "^3.3.0",
|
||||||
"@push.rocks/smarttime": "^4.1.1",
|
"@push.rocks/smarttime": "^4.1.1",
|
||||||
"@types/ws": "^8.18.1",
|
"@types/ws": "^8.18.1",
|
||||||
|
|||||||
1102
pnpm-lock.yaml
generated
1102
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
125
readme.hints.md
125
readme.hints.md
@@ -244,6 +244,131 @@ tstest test/specific.ts -w
|
|||||||
- Ignores changes matching the ignore patterns
|
- Ignores changes matching the ignore patterns
|
||||||
- Shows "Waiting for file changes..." between runs
|
- Shows "Waiting for file changes..." between runs
|
||||||
|
|
||||||
|
## Phase 1 API Improvements (v3.1.0)
|
||||||
|
|
||||||
|
### New Features Implemented
|
||||||
|
|
||||||
|
#### 1. tap.postTask() - Global Teardown (COMPLETED)
|
||||||
|
|
||||||
|
Added symmetric teardown method to complement `tap.preTask()`:
|
||||||
|
|
||||||
|
**Implementation:**
|
||||||
|
- Created `PostTask` class in `ts_tapbundle/tapbundle.classes.posttask.ts`
|
||||||
|
- Mirrors PreTask structure with description and function
|
||||||
|
- Integrated into Tap class execution flow
|
||||||
|
- Runs after all tests complete but before global `afterAll` hook
|
||||||
|
|
||||||
|
**Usage:**
|
||||||
|
```typescript
|
||||||
|
tap.postTask('cleanup database', async () => {
|
||||||
|
await cleanupDatabase();
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
**Execution Order:**
|
||||||
|
1. preTask hooks
|
||||||
|
2. Global beforeAll
|
||||||
|
3. Tests (with suite hooks)
|
||||||
|
4. **postTask hooks** ← NEW
|
||||||
|
5. Global afterAll
|
||||||
|
|
||||||
|
#### 2. Suite-Level beforeAll/afterAll (COMPLETED)
|
||||||
|
|
||||||
|
Added once-per-suite lifecycle hooks:
|
||||||
|
|
||||||
|
**Implementation:**
|
||||||
|
- Extended `ITestSuite` interface with `beforeAll` and `afterAll` properties
|
||||||
|
- Added `tap.beforeAll()` and `tap.afterAll()` methods
|
||||||
|
- Integrated into `_runSuite()` execution flow
|
||||||
|
- Properly handles nested suites
|
||||||
|
|
||||||
|
**Usage:**
|
||||||
|
```typescript
|
||||||
|
tap.describe('Database Tests', () => {
|
||||||
|
tap.beforeAll(async () => {
|
||||||
|
await initializeDatabaseConnection(); // Runs once
|
||||||
|
});
|
||||||
|
|
||||||
|
tap.test('test 1', async () => {});
|
||||||
|
tap.test('test 2', async () => {});
|
||||||
|
|
||||||
|
tap.afterAll(async () => {
|
||||||
|
await closeDatabaseConnection(); // Runs once
|
||||||
|
});
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
**Execution Order per Suite:**
|
||||||
|
1. Suite beforeAll ← NEW
|
||||||
|
2. Suite beforeEach
|
||||||
|
3. Test
|
||||||
|
4. Suite afterEach
|
||||||
|
5. (Repeat 2-4 for each test)
|
||||||
|
6. Child suites (recursive)
|
||||||
|
7. Suite afterAll ← NEW
|
||||||
|
|
||||||
|
#### 3. tap.parallel() Fluent Entry Point (COMPLETED)
|
||||||
|
|
||||||
|
Added fluent API for parallel test creation:
|
||||||
|
|
||||||
|
**Implementation:**
|
||||||
|
- Updated `TestBuilder` class with `_parallel` flag
|
||||||
|
- Builder constructor accepts optional parallel parameter
|
||||||
|
- Added `tap.parallel()` method returning configured builder
|
||||||
|
- Fixed `testParallel()` to return TapTest<T> (was void)
|
||||||
|
|
||||||
|
**Usage:**
|
||||||
|
```typescript
|
||||||
|
// Simple parallel test
|
||||||
|
tap.parallel().test('fetch data', async () => {});
|
||||||
|
|
||||||
|
// With full configuration
|
||||||
|
tap
|
||||||
|
.parallel()
|
||||||
|
.tags('api', 'integration')
|
||||||
|
.retry(2)
|
||||||
|
.timeout(5000)
|
||||||
|
.test('configured parallel test', async () => {});
|
||||||
|
```
|
||||||
|
|
||||||
|
**Benefits:**
|
||||||
|
- Consistent with other fluent builders (tags, priority, etc.)
|
||||||
|
- More discoverable than separate `testParallel()` method
|
||||||
|
- Allows chaining parallel with other configurations
|
||||||
|
- `testParallel()` kept for backward compatibility
|
||||||
|
|
||||||
|
### Documentation Updates
|
||||||
|
|
||||||
|
**tapbundle/readme.md:**
|
||||||
|
- Added suite-level beforeAll/afterAll documentation
|
||||||
|
- Documented postTask with execution order notes
|
||||||
|
- Added parallel() fluent API examples
|
||||||
|
- Expanded TapTools documentation with all methods
|
||||||
|
- Added "Additional Tap Methods" section for fail(), getSettings(), etc.
|
||||||
|
- Documented all previously undocumented methods
|
||||||
|
|
||||||
|
### Tests
|
||||||
|
|
||||||
|
**test/tapbundle/test.new-lifecycle.ts:**
|
||||||
|
- Tests postTask execution order
|
||||||
|
- Verifies suite-level beforeAll/afterAll
|
||||||
|
- Tests nested suite lifecycle
|
||||||
|
- Validates parallel() fluent API
|
||||||
|
- Confirms all execution order requirements
|
||||||
|
|
||||||
|
**Test Results:** All 9 tests passing ✅
|
||||||
|
|
||||||
|
### Breaking Changes
|
||||||
|
|
||||||
|
None - all changes are additive and backward compatible.
|
||||||
|
|
||||||
|
### Migration Guide
|
||||||
|
|
||||||
|
No migration needed. New features are opt-in:
|
||||||
|
- Continue using existing patterns
|
||||||
|
- Adopt new features incrementally
|
||||||
|
- `testParallel()` still works (recommended: switch to `parallel().test()`)
|
||||||
|
|
||||||
## Fixed Issues
|
## Fixed Issues
|
||||||
|
|
||||||
### tap.skip.test(), tap.todo(), and tap.only.test() (Fixed)
|
### tap.skip.test(), tap.todo(), and tap.only.test() (Fixed)
|
||||||
|
|||||||
170
test/tapbundle/test.new-lifecycle.ts
Normal file
170
test/tapbundle/test.new-lifecycle.ts
Normal file
@@ -0,0 +1,170 @@
|
|||||||
|
import { tap, expect } from '../../ts_tapbundle/index.js';
|
||||||
|
|
||||||
|
// Global state for testing new lifecycle features
|
||||||
|
const executionOrder: string[] = [];
|
||||||
|
let postTaskRan = false;
|
||||||
|
|
||||||
|
// Test preTask and postTask
|
||||||
|
tap.preTask('setup environment', async () => {
|
||||||
|
executionOrder.push('preTask');
|
||||||
|
console.log('🔧 PreTask: Setting up environment');
|
||||||
|
});
|
||||||
|
|
||||||
|
tap.postTask('cleanup environment', async () => {
|
||||||
|
postTaskRan = true;
|
||||||
|
executionOrder.push('postTask');
|
||||||
|
console.log('🧹 PostTask: Cleaning up environment');
|
||||||
|
});
|
||||||
|
|
||||||
|
// Test suite-level beforeAll and afterAll
|
||||||
|
tap.describe('Suite with beforeAll/afterAll', () => {
|
||||||
|
tap.beforeAll(async () => {
|
||||||
|
executionOrder.push('suite-beforeAll');
|
||||||
|
console.log('🔰 Suite beforeAll executed');
|
||||||
|
});
|
||||||
|
|
||||||
|
tap.afterAll(async () => {
|
||||||
|
executionOrder.push('suite-afterAll');
|
||||||
|
console.log('🏁 Suite afterAll executed');
|
||||||
|
});
|
||||||
|
|
||||||
|
tap.beforeEach(async () => {
|
||||||
|
executionOrder.push('suite-beforeEach');
|
||||||
|
});
|
||||||
|
|
||||||
|
tap.afterEach(async () => {
|
||||||
|
executionOrder.push('suite-afterEach');
|
||||||
|
});
|
||||||
|
|
||||||
|
tap.test('first test in suite', async () => {
|
||||||
|
executionOrder.push('test-1');
|
||||||
|
expect(executionOrder).toContain('preTask');
|
||||||
|
expect(executionOrder).toContain('suite-beforeAll');
|
||||||
|
console.log('✓ Test 1 executed');
|
||||||
|
});
|
||||||
|
|
||||||
|
tap.test('second test in suite', async () => {
|
||||||
|
executionOrder.push('test-2');
|
||||||
|
expect(executionOrder).toContain('suite-beforeAll');
|
||||||
|
console.log('✓ Test 2 executed');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Test nested suites with beforeAll/afterAll
|
||||||
|
tap.describe('Parent Suite', () => {
|
||||||
|
tap.beforeAll(async () => {
|
||||||
|
executionOrder.push('parent-beforeAll');
|
||||||
|
console.log('🔰 Parent beforeAll executed');
|
||||||
|
});
|
||||||
|
|
||||||
|
tap.afterAll(async () => {
|
||||||
|
executionOrder.push('parent-afterAll');
|
||||||
|
console.log('🏁 Parent afterAll executed');
|
||||||
|
});
|
||||||
|
|
||||||
|
tap.test('test in parent', async () => {
|
||||||
|
executionOrder.push('parent-test');
|
||||||
|
expect(executionOrder).toContain('parent-beforeAll');
|
||||||
|
});
|
||||||
|
|
||||||
|
tap.describe('Child Suite', () => {
|
||||||
|
tap.beforeAll(async () => {
|
||||||
|
executionOrder.push('child-beforeAll');
|
||||||
|
console.log('🔰 Child beforeAll executed');
|
||||||
|
});
|
||||||
|
|
||||||
|
tap.afterAll(async () => {
|
||||||
|
executionOrder.push('child-afterAll');
|
||||||
|
console.log('🏁 Child afterAll executed');
|
||||||
|
});
|
||||||
|
|
||||||
|
tap.test('test in child', async () => {
|
||||||
|
executionOrder.push('child-test');
|
||||||
|
expect(executionOrder).toContain('parent-beforeAll');
|
||||||
|
expect(executionOrder).toContain('child-beforeAll');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Test parallel() fluent API
|
||||||
|
tap.parallel().test('parallel test 1', async () => {
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 10));
|
||||||
|
executionOrder.push('parallel-1');
|
||||||
|
console.log('⚡ Parallel test 1 executed');
|
||||||
|
expect(true).toBeTrue();
|
||||||
|
});
|
||||||
|
|
||||||
|
tap.parallel().test('parallel test 2', async () => {
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 5));
|
||||||
|
executionOrder.push('parallel-2');
|
||||||
|
console.log('⚡ Parallel test 2 executed');
|
||||||
|
expect(true).toBeTrue();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Test parallel() with configuration
|
||||||
|
tap
|
||||||
|
.parallel()
|
||||||
|
.tags('integration', 'parallel')
|
||||||
|
.timeout(1000)
|
||||||
|
.test('configured parallel test', async () => {
|
||||||
|
executionOrder.push('parallel-configured');
|
||||||
|
console.log('⚡ Configured parallel test executed');
|
||||||
|
expect(true).toBeTrue();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Verify execution order
|
||||||
|
tap.test('verify lifecycle execution order', async () => {
|
||||||
|
// Give a moment for any async operations to complete
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 100));
|
||||||
|
|
||||||
|
console.log('📊 Execution order:', executionOrder);
|
||||||
|
|
||||||
|
// Verify preTask ran first
|
||||||
|
expect(executionOrder[0]).toEqual('preTask');
|
||||||
|
|
||||||
|
// Verify suite beforeAll ran before tests
|
||||||
|
const suiteBeforeAllIndex = executionOrder.indexOf('suite-beforeAll');
|
||||||
|
const test1Index = executionOrder.indexOf('test-1');
|
||||||
|
expect(suiteBeforeAllIndex).toBeLessThan(test1Index);
|
||||||
|
|
||||||
|
// Verify beforeEach ran before each test
|
||||||
|
const beforeEachIndices = executionOrder
|
||||||
|
.map((item, index) => item === 'suite-beforeEach' ? index : -1)
|
||||||
|
.filter(index => index !== -1);
|
||||||
|
expect(beforeEachIndices.length).toBeGreaterThanOrEqual(2);
|
||||||
|
|
||||||
|
// Verify afterEach ran after each test
|
||||||
|
const afterEachIndices = executionOrder
|
||||||
|
.map((item, index) => item === 'suite-afterEach' ? index : -1)
|
||||||
|
.filter(index => index !== -1);
|
||||||
|
expect(afterEachIndices.length).toBeGreaterThanOrEqual(2);
|
||||||
|
|
||||||
|
// Verify afterAll ran after all tests
|
||||||
|
const suiteAfterAllIndex = executionOrder.indexOf('suite-afterAll');
|
||||||
|
const test2Index = executionOrder.indexOf('test-2');
|
||||||
|
expect(suiteAfterAllIndex).toBeGreaterThan(test2Index);
|
||||||
|
|
||||||
|
// Verify nested suite lifecycle
|
||||||
|
expect(executionOrder).toContain('parent-beforeAll');
|
||||||
|
expect(executionOrder).toContain('parent-test');
|
||||||
|
expect(executionOrder).toContain('child-beforeAll');
|
||||||
|
expect(executionOrder).toContain('child-test');
|
||||||
|
expect(executionOrder).toContain('child-afterAll');
|
||||||
|
expect(executionOrder).toContain('parent-afterAll');
|
||||||
|
|
||||||
|
// Verify parallel tests ran
|
||||||
|
expect(executionOrder).toContain('parallel-1');
|
||||||
|
expect(executionOrder).toContain('parallel-2');
|
||||||
|
expect(executionOrder).toContain('parallel-configured');
|
||||||
|
|
||||||
|
console.log('✅ All lifecycle hooks executed in correct order');
|
||||||
|
});
|
||||||
|
|
||||||
|
// This test will verify postTask ran (after tap.start() completes)
|
||||||
|
tap.test('verify postTask execution', async () => {
|
||||||
|
// PostTask hasn't run yet because tests are still running
|
||||||
|
expect(postTaskRan).toBeFalse();
|
||||||
|
console.log('✓ Verified postTask will run after all tests');
|
||||||
|
});
|
||||||
|
|
||||||
|
export default tap.start();
|
||||||
@@ -3,6 +3,6 @@
|
|||||||
*/
|
*/
|
||||||
export const commitinfo = {
|
export const commitinfo = {
|
||||||
name: '@git.zone/tstest',
|
name: '@git.zone/tstest',
|
||||||
version: '3.0.0',
|
version: '3.1.1',
|
||||||
description: 'a test utility to run tests that match test/**/*.ts'
|
description: 'a test utility to run tests that match test/**/*.ts'
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -91,6 +91,24 @@ tap.testParallel('should fetch user data', async () => {
|
|||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
|
**Note:** The `tap.parallel().test()` fluent API is now the recommended way to define parallel tests (see Fluent API section below).
|
||||||
|
|
||||||
|
#### `tap.parallel()`
|
||||||
|
|
||||||
|
Returns a fluent test builder configured for parallel execution.
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
tap.parallel().test('should fetch data', async () => {
|
||||||
|
// Parallel test
|
||||||
|
});
|
||||||
|
|
||||||
|
// With full configuration
|
||||||
|
tap.parallel()
|
||||||
|
.tags('api')
|
||||||
|
.retry(2)
|
||||||
|
.test('configured parallel test', async () => {});
|
||||||
|
```
|
||||||
|
|
||||||
#### `tap.describe(description, suiteFunction)`
|
#### `tap.describe(description, suiteFunction)`
|
||||||
|
|
||||||
Create a test suite to group related tests.
|
Create a test suite to group related tests.
|
||||||
@@ -141,22 +159,56 @@ tap
|
|||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
|
#### Parallel Tests with Fluent API
|
||||||
|
|
||||||
|
Use `tap.parallel()` to create parallel tests with fluent configuration:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// Simple parallel test
|
||||||
|
tap.parallel().test('fetches user data', async () => {
|
||||||
|
// Runs in parallel with other parallel tests
|
||||||
|
});
|
||||||
|
|
||||||
|
// Parallel test with full configuration
|
||||||
|
tap
|
||||||
|
.parallel()
|
||||||
|
.tags('api', 'integration')
|
||||||
|
.retry(2)
|
||||||
|
.timeout(5000)
|
||||||
|
.test('should fetch data concurrently', async () => {
|
||||||
|
// Configured parallel test
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
**Note:** `tap.parallel().test()` is the recommended way to define parallel tests. The older `tap.testParallel()` method is still supported for backward compatibility.
|
||||||
|
|
||||||
### Lifecycle Hooks
|
### Lifecycle Hooks
|
||||||
|
|
||||||
#### Suite-Level Hooks
|
#### Suite-Level Hooks
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
tap.describe('Database Tests', () => {
|
tap.describe('Database Tests', () => {
|
||||||
|
tap.beforeAll(async (tapTools) => {
|
||||||
|
// Runs once before all tests in this suite
|
||||||
|
await initializeDatabaseConnection();
|
||||||
|
});
|
||||||
|
|
||||||
tap.beforeEach(async (tapTools) => {
|
tap.beforeEach(async (tapTools) => {
|
||||||
// Runs before each test in this suite
|
// Runs before each test in this suite
|
||||||
|
await clearTestData();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
tap.test('test 1', async () => { });
|
||||||
|
tap.test('test 2', async () => { });
|
||||||
|
|
||||||
tap.afterEach(async (tapTools) => {
|
tap.afterEach(async (tapTools) => {
|
||||||
// Runs after each test in this suite
|
// Runs after each test in this suite
|
||||||
});
|
});
|
||||||
|
|
||||||
tap.test('test 1', async () => { });
|
tap.afterAll(async (tapTools) => {
|
||||||
tap.test('test 2', async () => { });
|
// Runs once after all tests in this suite
|
||||||
|
await closeDatabaseConnection();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -267,38 +319,169 @@ TSTEST_FILTER_TAGS=unit tstest test/mytest.node.ts
|
|||||||
|
|
||||||
Each test receives a `tapTools` instance with utilities:
|
Each test receives a `tapTools` instance with utilities:
|
||||||
|
|
||||||
|
#### Test Control Methods
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
tap.test('should have utilities', async (tapTools) => {
|
tap.test('test control examples', async (tapTools) => {
|
||||||
// Mark test as skipped
|
// Skip this test
|
||||||
|
tapTools.skip('reason');
|
||||||
|
|
||||||
|
// Conditionally skip
|
||||||
|
tapTools.skipIf(condition, 'reason');
|
||||||
|
|
||||||
|
// Mark test as skipped before execution
|
||||||
tapTools.markAsSkipped('reason');
|
tapTools.markAsSkipped('reason');
|
||||||
|
|
||||||
// Mark as todo
|
// Mark as todo
|
||||||
tapTools.todo('not implemented');
|
tapTools.todo('not implemented');
|
||||||
|
|
||||||
|
// Allow test to fail without marking suite as failed
|
||||||
|
tapTools.allowFailure();
|
||||||
|
|
||||||
// Configure retries
|
// Configure retries
|
||||||
tapTools.retry(3);
|
tapTools.retry(3);
|
||||||
|
|
||||||
// Log test output
|
// Set timeout
|
||||||
tapTools.log('debug message');
|
tapTools.timeout(5000);
|
||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
|
#### Utility Methods
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
tap.test('utility examples', async (tapTools) => {
|
||||||
|
// Delay execution
|
||||||
|
await tapTools.delayFor(1000); // Wait 1 second
|
||||||
|
await tapTools.delayForRandom(500, 1500); // Random delay
|
||||||
|
|
||||||
|
// Colored console output
|
||||||
|
tapTools.coloredString('✓ Success', 'green');
|
||||||
|
tapTools.coloredString('✗ Error', 'red');
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Context and Data Sharing
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
tap.test('first test', async (tapTools) => {
|
||||||
|
// Store data in context
|
||||||
|
tapTools.context.set('userId', '12345');
|
||||||
|
|
||||||
|
// Store in testData property
|
||||||
|
tapTools.testData = { username: 'alice' };
|
||||||
|
});
|
||||||
|
|
||||||
|
tap.test('second test', async (tapTools) => {
|
||||||
|
// Retrieve from context
|
||||||
|
const userId = tapTools.context.get('userId');
|
||||||
|
|
||||||
|
// Check existence
|
||||||
|
if (tapTools.context.has('userId')) {
|
||||||
|
// Use data
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear context
|
||||||
|
tapTools.context.clear();
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Fixtures
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// Define a fixture globally (outside tests)
|
||||||
|
import { TapTools } from '@git.zone/tstest/tapbundle';
|
||||||
|
|
||||||
|
TapTools.defineFixture('database', async () => {
|
||||||
|
const db = await createTestDatabase();
|
||||||
|
return {
|
||||||
|
value: db,
|
||||||
|
cleanup: async () => await db.close()
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
// Use fixtures in tests
|
||||||
|
tap.test('database test', async (tapTools) => {
|
||||||
|
const db = await tapTools.fixture('database');
|
||||||
|
// Use db...
|
||||||
|
// Cleanup happens automatically
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Factory Pattern
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// Define a factory
|
||||||
|
TapTools.defineFixture('user', async () => {
|
||||||
|
return {
|
||||||
|
value: null, // Not used for factories
|
||||||
|
factory: async (data) => {
|
||||||
|
return await createUser(data);
|
||||||
|
},
|
||||||
|
cleanup: async (user) => await user.delete()
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
// Use factory in tests
|
||||||
|
tap.test('user test', async (tapTools) => {
|
||||||
|
const user = await tapTools.factory('user').create({ name: 'Alice' });
|
||||||
|
|
||||||
|
// Create multiple
|
||||||
|
const users = await tapTools.factory('user').createMany([
|
||||||
|
{ name: 'Alice' },
|
||||||
|
{ name: 'Bob' }
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Cleanup happens automatically
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Snapshot Testing
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
tap.test('snapshot test', async (tapTools) => {
|
||||||
|
const result = { name: 'Alice', age: 30 };
|
||||||
|
|
||||||
|
// Compare with stored snapshot
|
||||||
|
await tapTools.matchSnapshot(result);
|
||||||
|
|
||||||
|
// Named snapshots
|
||||||
|
await tapTools.matchSnapshot(result, 'user-data');
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
To update snapshots, run with:
|
||||||
|
```bash
|
||||||
|
UPDATE_SNAPSHOTS=true tstest test/mytest.ts
|
||||||
|
```
|
||||||
|
|
||||||
## Advanced Features
|
## Advanced Features
|
||||||
|
|
||||||
### Pre-Tasks
|
### Pre-Tasks and Post-Tasks
|
||||||
|
|
||||||
Run setup tasks before any tests execute:
|
Run setup and teardown tasks before/after all tests:
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
tap.preTask('setup database', async () => {
|
tap.preTask('setup database', async () => {
|
||||||
// Runs before any tests
|
// Runs before any tests
|
||||||
|
await initializeDatabase();
|
||||||
});
|
});
|
||||||
|
|
||||||
tap.test('first test', async () => {
|
tap.test('first test', async () => {
|
||||||
// Database is ready
|
// Database is ready
|
||||||
});
|
});
|
||||||
|
|
||||||
|
tap.test('second test', async () => {
|
||||||
|
// Tests run...
|
||||||
|
});
|
||||||
|
|
||||||
|
tap.postTask('cleanup database', async () => {
|
||||||
|
// Runs after all tests complete
|
||||||
|
await cleanupDatabase();
|
||||||
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
|
**Note:** Post tasks run after all tests but before the global `afterAll` hook.
|
||||||
|
|
||||||
### Test Priority
|
### Test Priority
|
||||||
|
|
||||||
Organize tests by priority level:
|
Organize tests by priority level:
|
||||||
@@ -334,6 +517,50 @@ import { setProtocolEmitter } from '@git.zone/tstest/tapbundle';
|
|||||||
// Events: test:started, test:completed, assertion:failed, suite:started, suite:completed
|
// Events: test:started, test:completed, assertion:failed, suite:started, suite:completed
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Additional Tap Methods
|
||||||
|
|
||||||
|
#### Configuration and Inspection
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// Get current test settings
|
||||||
|
const settings = tap.getSettings();
|
||||||
|
console.log(settings.timeout, settings.retries);
|
||||||
|
|
||||||
|
// Explicitly fail a test
|
||||||
|
tap.test('validation test', async () => {
|
||||||
|
if (invalidCondition) {
|
||||||
|
tap.fail('Custom failure message');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Advanced Control
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// Force stop test execution
|
||||||
|
tap.stopForcefully(exitCode, immediate);
|
||||||
|
|
||||||
|
// Handle thrown errors (internal use)
|
||||||
|
tap.threw(error);
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Parallel Test Variants
|
||||||
|
|
||||||
|
In addition to `tap.parallel().test()`, skip/only/todo modes also support parallel execution:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// Skip parallel test
|
||||||
|
tap.skip.testParallel('not ready', async () => {});
|
||||||
|
|
||||||
|
// Only run this parallel test
|
||||||
|
tap.only.testParallel('focus here', async () => {});
|
||||||
|
|
||||||
|
// Todo parallel test
|
||||||
|
tap.todo.testParallel('implement later');
|
||||||
|
```
|
||||||
|
|
||||||
|
**Note:** Using `tap.parallel()` fluent API is recommended over these direct methods.
|
||||||
|
|
||||||
## Best Practices
|
## Best Practices
|
||||||
|
|
||||||
1. **Always export `tap.start()`** at the end of test files:
|
1. **Always export `tap.start()`** at the end of test files:
|
||||||
|
|||||||
21
ts_tapbundle/tapbundle.classes.posttask.ts
Normal file
21
ts_tapbundle/tapbundle.classes.posttask.ts
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
import * as plugins from './tapbundle.plugins.js';
|
||||||
|
import { TapTools } from './tapbundle.classes.taptools.js';
|
||||||
|
|
||||||
|
export interface IPostTaskFunction {
|
||||||
|
(tapTools?: TapTools): Promise<any>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class PostTask {
|
||||||
|
public description: string;
|
||||||
|
public postTaskFunction: IPostTaskFunction;
|
||||||
|
|
||||||
|
constructor(descriptionArg: string, postTaskFunctionArg: IPostTaskFunction) {
|
||||||
|
this.description = descriptionArg;
|
||||||
|
this.postTaskFunction = postTaskFunctionArg;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async run() {
|
||||||
|
console.log(`::__POSTTASK: ${this.description}`);
|
||||||
|
await this.postTaskFunction(new TapTools(null));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,7 +1,9 @@
|
|||||||
import * as plugins from './tapbundle.plugins.js';
|
import * as plugins from './tapbundle.plugins.js';
|
||||||
|
|
||||||
import { type IPreTaskFunction, PreTask } from './tapbundle.classes.pretask.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 { TapTest, type ITestFunction } from './tapbundle.classes.taptest.js';
|
||||||
|
import { TapTools } from './tapbundle.classes.taptools.js';
|
||||||
import { ProtocolEmitter, type ITestEvent } from '../dist_ts_tapbundle_protocol/index.js';
|
import { ProtocolEmitter, type ITestEvent } from '../dist_ts_tapbundle_protocol/index.js';
|
||||||
import type { ITapSettings } from './tapbundle.interfaces.js';
|
import type { ITapSettings } from './tapbundle.interfaces.js';
|
||||||
import { SettingsManager } from './tapbundle.classes.settingsmanager.js';
|
import { SettingsManager } from './tapbundle.classes.settingsmanager.js';
|
||||||
@@ -9,6 +11,8 @@ import { SettingsManager } from './tapbundle.classes.settingsmanager.js';
|
|||||||
export interface ITestSuite {
|
export interface ITestSuite {
|
||||||
description: string;
|
description: string;
|
||||||
tests: TapTest<any>[];
|
tests: TapTest<any>[];
|
||||||
|
beforeAll?: ITestFunction<any>;
|
||||||
|
afterAll?: ITestFunction<any>;
|
||||||
beforeEach?: ITestFunction<any>;
|
beforeEach?: ITestFunction<any>;
|
||||||
afterEach?: ITestFunction<any>;
|
afterEach?: ITestFunction<any>;
|
||||||
parent?: ITestSuite;
|
parent?: ITestSuite;
|
||||||
@@ -21,85 +25,89 @@ class TestBuilder<T> {
|
|||||||
private _priority: 'high' | 'medium' | 'low' = 'medium';
|
private _priority: 'high' | 'medium' | 'low' = 'medium';
|
||||||
private _retryCount?: number;
|
private _retryCount?: number;
|
||||||
private _timeoutMs?: number;
|
private _timeoutMs?: number;
|
||||||
|
private _parallel: boolean = false;
|
||||||
constructor(tap: Tap<T>) {
|
|
||||||
|
constructor(tap: Tap<T>, parallel: boolean = false) {
|
||||||
this._tap = tap;
|
this._tap = tap;
|
||||||
|
this._parallel = parallel;
|
||||||
}
|
}
|
||||||
|
|
||||||
tags(...tags: string[]) {
|
tags(...tags: string[]) {
|
||||||
this._tags = tags;
|
this._tags = tags;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
priority(level: 'high' | 'medium' | 'low') {
|
priority(level: 'high' | 'medium' | 'low') {
|
||||||
this._priority = level;
|
this._priority = level;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
retry(count: number) {
|
retry(count: number) {
|
||||||
this._retryCount = count;
|
this._retryCount = count;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
timeout(ms: number) {
|
timeout(ms: number) {
|
||||||
this._timeoutMs = ms;
|
this._timeoutMs = ms;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
test(description: string, testFunction: ITestFunction<T>) {
|
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
|
// Apply settings to the test
|
||||||
if (this._tags.length > 0) {
|
if (this._tags.length > 0) {
|
||||||
test.tags = this._tags;
|
test.tags = this._tags;
|
||||||
}
|
}
|
||||||
test.priority = this._priority;
|
test.priority = this._priority;
|
||||||
|
|
||||||
if (this._retryCount !== undefined) {
|
if (this._retryCount !== undefined) {
|
||||||
test.tapTools.retry(this._retryCount);
|
test.tapTools.retry(this._retryCount);
|
||||||
}
|
}
|
||||||
if (this._timeoutMs !== undefined) {
|
if (this._timeoutMs !== undefined) {
|
||||||
test.timeoutMs = this._timeoutMs;
|
test.timeoutMs = this._timeoutMs;
|
||||||
}
|
}
|
||||||
|
|
||||||
return test;
|
return test;
|
||||||
}
|
}
|
||||||
|
|
||||||
testOnly(description: string, testFunction: ITestFunction<T>) {
|
testOnly(description: string, testFunction: ITestFunction<T>) {
|
||||||
const test = this._tap.test(description, testFunction, 'only');
|
const test = this._tap.test(description, testFunction, 'only');
|
||||||
|
|
||||||
// Apply settings to the test
|
// Apply settings to the test
|
||||||
if (this._tags.length > 0) {
|
if (this._tags.length > 0) {
|
||||||
test.tags = this._tags;
|
test.tags = this._tags;
|
||||||
}
|
}
|
||||||
test.priority = this._priority;
|
test.priority = this._priority;
|
||||||
|
|
||||||
if (this._retryCount !== undefined) {
|
if (this._retryCount !== undefined) {
|
||||||
test.tapTools.retry(this._retryCount);
|
test.tapTools.retry(this._retryCount);
|
||||||
}
|
}
|
||||||
if (this._timeoutMs !== undefined) {
|
if (this._timeoutMs !== undefined) {
|
||||||
test.timeoutMs = this._timeoutMs;
|
test.timeoutMs = this._timeoutMs;
|
||||||
}
|
}
|
||||||
|
|
||||||
return test;
|
return test;
|
||||||
}
|
}
|
||||||
|
|
||||||
testSkip(description: string, testFunction: ITestFunction<T>) {
|
testSkip(description: string, testFunction: ITestFunction<T>) {
|
||||||
const test = this._tap.test(description, testFunction, 'skip');
|
const test = this._tap.test(description, testFunction, 'skip');
|
||||||
|
|
||||||
// Apply settings to the test
|
// Apply settings to the test
|
||||||
if (this._tags.length > 0) {
|
if (this._tags.length > 0) {
|
||||||
test.tags = this._tags;
|
test.tags = this._tags;
|
||||||
}
|
}
|
||||||
test.priority = this._priority;
|
test.priority = this._priority;
|
||||||
|
|
||||||
if (this._retryCount !== undefined) {
|
if (this._retryCount !== undefined) {
|
||||||
test.tapTools.retry(this._retryCount);
|
test.tapTools.retry(this._retryCount);
|
||||||
}
|
}
|
||||||
if (this._timeoutMs !== undefined) {
|
if (this._timeoutMs !== undefined) {
|
||||||
test.timeoutMs = this._timeoutMs;
|
test.timeoutMs = this._timeoutMs;
|
||||||
}
|
}
|
||||||
|
|
||||||
return test;
|
return test;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -122,21 +130,25 @@ export class Tap<T> {
|
|||||||
const builder = new TestBuilder<T>(this);
|
const builder = new TestBuilder<T>(this);
|
||||||
return builder.tags(...tags);
|
return builder.tags(...tags);
|
||||||
}
|
}
|
||||||
|
|
||||||
public priority(level: 'high' | 'medium' | 'low') {
|
public priority(level: 'high' | 'medium' | 'low') {
|
||||||
const builder = new TestBuilder<T>(this);
|
const builder = new TestBuilder<T>(this);
|
||||||
return builder.priority(level);
|
return builder.priority(level);
|
||||||
}
|
}
|
||||||
|
|
||||||
public retry(count: number) {
|
public retry(count: number) {
|
||||||
const builder = new TestBuilder<T>(this);
|
const builder = new TestBuilder<T>(this);
|
||||||
return builder.retry(count);
|
return builder.retry(count);
|
||||||
}
|
}
|
||||||
|
|
||||||
public timeout(ms: number) {
|
public timeout(ms: number) {
|
||||||
const builder = new TestBuilder<T>(this);
|
const builder = new TestBuilder<T>(this);
|
||||||
return builder.timeout(ms);
|
return builder.timeout(ms);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public parallel() {
|
||||||
|
return new TestBuilder<T>(this, true);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* skips a test
|
* skips a test
|
||||||
@@ -236,6 +248,7 @@ export class Tap<T> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
private _tapPreTasks: PreTask[] = [];
|
private _tapPreTasks: PreTask[] = [];
|
||||||
|
private _tapPostTasks: PostTask[] = [];
|
||||||
private _tapTests: TapTest<any>[] = [];
|
private _tapTests: TapTest<any>[] = [];
|
||||||
private _tapTestsOnly: TapTest<any>[] = [];
|
private _tapTestsOnly: TapTest<any>[] = [];
|
||||||
private _currentSuite: ITestSuite | null = null;
|
private _currentSuite: ITestSuite | null = null;
|
||||||
@@ -304,18 +317,22 @@ export class Tap<T> {
|
|||||||
this._tapPreTasks.push(new PreTask(descriptionArg, functionArg));
|
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.
|
* A parallel test that will not be waited for before the next starts.
|
||||||
* @param testDescription - A description of what the test does
|
* @param testDescription - A description of what the test does
|
||||||
* @param testFunction - A Function that returns a Promise and resolves or rejects
|
* @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({
|
const localTest = new TapTest({
|
||||||
description: testDescription,
|
description: testDescription,
|
||||||
testFunction,
|
testFunction,
|
||||||
parallel: true,
|
parallel: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Apply default settings from settings manager
|
// Apply default settings from settings manager
|
||||||
const settings = this.settingsManager.getSettings();
|
const settings = this.settingsManager.getSettings();
|
||||||
if (settings.timeout !== undefined) {
|
if (settings.timeout !== undefined) {
|
||||||
@@ -324,12 +341,14 @@ export class Tap<T> {
|
|||||||
if (settings.retries !== undefined) {
|
if (settings.retries !== undefined) {
|
||||||
localTest.tapTools.retry(settings.retries);
|
localTest.tapTools.retry(settings.retries);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this._currentSuite) {
|
if (this._currentSuite) {
|
||||||
this._currentSuite.tests.push(localTest);
|
this._currentSuite.tests.push(localTest);
|
||||||
} else {
|
} else {
|
||||||
this._tapTests.push(localTest);
|
this._tapTests.push(localTest);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return localTest;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -360,6 +379,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
|
* Set up a function to run before each test in the current suite
|
||||||
*/
|
*/
|
||||||
@@ -370,7 +411,7 @@ export class Tap<T> {
|
|||||||
throw new Error('beforeEach can only be used inside a describe block');
|
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
|
* Set up a function to run after each test in the current suite
|
||||||
*/
|
*/
|
||||||
@@ -554,6 +595,11 @@ export class Tap<T> {
|
|||||||
console.log(failReason);
|
console.log(failReason);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Run post tasks
|
||||||
|
for (const postTask of this._tapPostTasks) {
|
||||||
|
await postTask.run();
|
||||||
|
}
|
||||||
|
|
||||||
// Run global afterAll hook if configured
|
// Run global afterAll hook if configured
|
||||||
if (settings.afterAll) {
|
if (settings.afterAll) {
|
||||||
try {
|
try {
|
||||||
@@ -597,6 +643,12 @@ export class Tap<T> {
|
|||||||
suiteName: suite.description
|
suiteName: suite.description
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Run beforeAll hook for this suite
|
||||||
|
if (suite.beforeAll) {
|
||||||
|
await suite.beforeAll(new TapTools(null as any));
|
||||||
|
}
|
||||||
|
|
||||||
// Run beforeEach from parent suites
|
// Run beforeEach from parent suites
|
||||||
const beforeEachFunctions: ITestFunction<any>[] = [];
|
const beforeEachFunctions: ITestFunction<any>[] = [];
|
||||||
let currentSuite: ITestSuite | null = suite;
|
let currentSuite: ITestSuite | null = suite;
|
||||||
@@ -666,7 +718,12 @@ export class Tap<T> {
|
|||||||
|
|
||||||
// Recursively run child suites
|
// Recursively run child suites
|
||||||
await this._runSuite(suite, suite.children, promiseArray, context);
|
await this._runSuite(suite, suite.children, promiseArray, context);
|
||||||
|
|
||||||
|
// Run afterAll hook for this suite
|
||||||
|
if (suite.afterAll) {
|
||||||
|
await suite.afterAll(new TapTools(null as any));
|
||||||
|
}
|
||||||
|
|
||||||
// Emit suite:completed event
|
// Emit suite:completed event
|
||||||
this.emitEvent({
|
this.emitEvent({
|
||||||
eventType: 'suite:completed',
|
eventType: 'suite:completed',
|
||||||
|
|||||||
Reference in New Issue
Block a user