fix(docs): Update documentation: expand README with multi-runtime architecture, add module READMEs, and add local dev settings
This commit is contained in:
318
readme.md
318
readme.md
@@ -1,7 +1,7 @@
|
||||
# @git.zone/tstest
|
||||
🧪 **A powerful, modern test runner for TypeScript** - making your test runs beautiful and informative!
|
||||
🧪 **A powerful, modern test runner for TypeScript** - making your test runs beautiful and informative across multiple runtimes!
|
||||
|
||||
## Availabililty and Links
|
||||
## Availability and Links
|
||||
* [npmjs.org (npm package)](https://www.npmjs.com/package/@git.zone/tstest)
|
||||
* [code.foss.global (source)](https://code.foss.global/git.zone/tstest)
|
||||
|
||||
@@ -12,10 +12,10 @@
|
||||
### ✨ Key Features
|
||||
|
||||
- 🎯 **Smart Test Execution** - Run all tests, single files, or use glob patterns
|
||||
- 🚀 **Multi-Runtime Support** - Run tests in Node.js, Deno, Bun, and Chromium
|
||||
- 🎨 **Beautiful Output** - Color-coded results with emojis and clean formatting
|
||||
- 📊 **Multiple Output Modes** - Choose from normal, quiet, verbose, or JSON output
|
||||
- 🔍 **Automatic Discovery** - Finds all your test files automatically
|
||||
- 🌐 **Cross-Environment** - Supports Node.js and browser testing
|
||||
- 📝 **Detailed Logging** - Optional file logging for debugging
|
||||
- ⚡ **Performance Metrics** - See which tests are slow
|
||||
- 🤖 **CI/CD Ready** - JSON output mode for automation
|
||||
@@ -26,13 +26,10 @@
|
||||
- ⏳ **Timeout Control** - Set custom timeouts for tests
|
||||
- 🔁 **Retry Logic** - Automatically retry failing tests
|
||||
- 🛠️ **Test Fixtures** - Create reusable test data
|
||||
- 📦 **Browser-Compatible** - Full browser support with embedded tapbundle
|
||||
- 👀 **Watch Mode** - Automatically re-run tests on file changes
|
||||
- 📊 **Real-time Progress** - Live test execution progress updates
|
||||
- 🎨 **Visual Diffs** - Beautiful side-by-side diffs for failed assertions
|
||||
- 🔄 **Event-based Reporting** - Real-time test lifecycle events
|
||||
- ⚙️ **Test Configuration** - Flexible test settings with .tstest.json files
|
||||
- 🚀 **Protocol V2** - Enhanced TAP protocol with Unicode delimiters
|
||||
|
||||
## Installation
|
||||
|
||||
@@ -42,6 +39,140 @@ npm install --save-dev @git.zone/tstest
|
||||
pnpm add -D @git.zone/tstest
|
||||
```
|
||||
|
||||
## Multi-Runtime Architecture
|
||||
|
||||
tstest supports running your tests across multiple JavaScript runtimes, allowing you to verify cross-platform compatibility easily.
|
||||
|
||||
### Supported Runtimes
|
||||
|
||||
- **Node.js** - Default runtime, uses tsrun for TypeScript execution
|
||||
- **Chromium** - Browser environment testing with full DOM support
|
||||
- **Deno** - Secure TypeScript/JavaScript runtime with modern features
|
||||
- **Bun** - Ultra-fast all-in-one JavaScript runtime
|
||||
|
||||
### Test File Naming Convention
|
||||
|
||||
Name your test files with runtime specifiers to control where they run:
|
||||
|
||||
| Pattern | Runtimes | Example |
|
||||
|---------|----------|---------|
|
||||
| `*.ts` | Node.js only (default) | `test.api.ts` |
|
||||
| `*.node.ts` | Node.js only | `test.server.node.ts` |
|
||||
| `*.chromium.ts` | Chromium browser | `test.dom.chromium.ts` |
|
||||
| `*.deno.ts` | Deno runtime | `test.http.deno.ts` |
|
||||
| `*.bun.ts` | Bun runtime | `test.fast.bun.ts` |
|
||||
| `*.node+chromium.ts` | Both Node.js and Chromium | `test.isomorphic.node+chromium.ts` |
|
||||
| `*.node+deno.ts` | Both Node.js and Deno | `test.cross.node+deno.ts` |
|
||||
| `*.deno+bun.ts` | Both Deno and Bun | `test.modern.deno+bun.ts` |
|
||||
| `*.chromium.nonci.ts` | Chromium, skip in CI | `test.visual.chromium.nonci.ts` |
|
||||
|
||||
**Multi-Runtime Examples:**
|
||||
|
||||
```typescript
|
||||
// test.api.node+deno+bun.ts - runs in Node.js, Deno, and Bun
|
||||
import { expect, tap } from '@git.zone/tstest/tapbundle';
|
||||
|
||||
tap.test('cross-runtime HTTP test', async () => {
|
||||
const response = await fetch('https://api.example.com/test');
|
||||
expect(response.status).toEqual(200);
|
||||
});
|
||||
|
||||
export default tap.start();
|
||||
```
|
||||
|
||||
### Runtime Execution Order
|
||||
|
||||
When multiple runtimes are specified, tests execute in this order:
|
||||
1. Node.js
|
||||
2. Bun
|
||||
3. Deno
|
||||
4. Chromium
|
||||
|
||||
### Legacy Naming (Deprecated)
|
||||
|
||||
The following patterns are still supported but deprecated. Use the migration tool to update:
|
||||
|
||||
| Legacy Pattern | Modern Equivalent | Migration Command |
|
||||
|----------------|-------------------|-------------------|
|
||||
| `*.browser.ts` | `*.chromium.ts` | `tstest migrate` |
|
||||
| `*.both.ts` | `*.node+chromium.ts` | `tstest migrate` |
|
||||
|
||||
When running legacy files, tstest shows a deprecation warning with the suggested new name.
|
||||
|
||||
### Migration Tool
|
||||
|
||||
Migrate your test files from legacy naming to the new convention:
|
||||
|
||||
```bash
|
||||
# Dry run - see what would change
|
||||
tstest migrate --dry-run
|
||||
|
||||
# Apply migrations (uses git mv to preserve history)
|
||||
tstest migrate --write
|
||||
```
|
||||
|
||||
**Migration Features:**
|
||||
- ✅ Uses `git mv` to preserve file history
|
||||
- ✅ Idempotent - safe to run multiple times
|
||||
- ✅ Dry-run by default for safety
|
||||
- ✅ Colored output showing all changes
|
||||
- ✅ Handles modifiers like `.nonci` correctly
|
||||
|
||||
Example output:
|
||||
```
|
||||
============================================================
|
||||
Test File Migration Tool
|
||||
============================================================
|
||||
|
||||
🔍 DRY RUN MODE - No files will be modified
|
||||
|
||||
Found 3 legacy test file(s)
|
||||
|
||||
Would migrate:
|
||||
test.browser.ts
|
||||
→ test.chromium.ts
|
||||
|
||||
Would migrate:
|
||||
test.both.ts
|
||||
→ test.node+chromium.ts
|
||||
|
||||
Would migrate:
|
||||
test.auth.browser.nonci.ts
|
||||
→ test.auth.chromium.nonci.ts
|
||||
|
||||
============================================================
|
||||
Summary:
|
||||
Total legacy files: 3
|
||||
Successfully migrated: 3
|
||||
Errors: 0
|
||||
============================================================
|
||||
|
||||
To apply these changes, run:
|
||||
tstest migrate --write
|
||||
```
|
||||
|
||||
### Runtime-Specific Permissions
|
||||
|
||||
#### Deno Runtime
|
||||
|
||||
Tests run with these permissions by default:
|
||||
```bash
|
||||
--allow-read
|
||||
--allow-env
|
||||
--allow-net
|
||||
--allow-write
|
||||
--allow-sys
|
||||
--allow-import # Enables npm packages and Node.js built-ins
|
||||
--node-modules-dir # Node.js compatibility mode
|
||||
--sloppy-imports # Allows .js imports to resolve to .ts files
|
||||
```
|
||||
|
||||
Configure custom permissions in your test file or via environment variables.
|
||||
|
||||
#### Bun Runtime
|
||||
|
||||
Bun runs with its native TypeScript support and full access to Node.js APIs.
|
||||
|
||||
## Usage
|
||||
|
||||
### Basic Test Execution
|
||||
@@ -92,18 +223,29 @@ tstest "test/unit/*.ts"
|
||||
Pattern: test
|
||||
Found: 4 test file(s)
|
||||
|
||||
▶️ test/test.ts (1/4)
|
||||
Runtime: node.js
|
||||
✅ prepare test (1ms)
|
||||
Summary: 1/1 PASSED
|
||||
━━━ Part 1: Node.js ━━━
|
||||
|
||||
▶️ test/test.node+deno.ts (1/4)
|
||||
Runtime: Node.js
|
||||
✅ HTTP request works (12ms)
|
||||
✅ JSON parsing works (3ms)
|
||||
Summary: 2/2 PASSED in 1.2s
|
||||
|
||||
━━━ Part 2: Deno ━━━
|
||||
|
||||
▶️ test/test.node+deno.ts (1/4)
|
||||
Runtime: Deno
|
||||
✅ HTTP request works (15ms)
|
||||
✅ JSON parsing works (2ms)
|
||||
Summary: 2/2 PASSED in 1.1s
|
||||
|
||||
📊 Test Summary
|
||||
┌────────────────────────────────┐
|
||||
│ Total Files: 4 │
|
||||
│ Total Tests: 4 │
|
||||
│ Passed: 4 │
|
||||
│ Total Tests: 8 │
|
||||
│ Passed: 8 │
|
||||
│ Failed: 0 │
|
||||
│ Duration: 542ms │
|
||||
│ Duration: 2.4s │
|
||||
└────────────────────────────────┘
|
||||
|
||||
ALL TESTS PASSED! 🎉
|
||||
@@ -141,19 +283,7 @@ Perfect for CI/CD pipelines:
|
||||
{"event":"summary","summary":{"totalFiles":4,"totalTests":4,"totalPassed":4,"totalFailed":0,"totalDuration":542}}
|
||||
```
|
||||
|
||||
## Test File Naming Conventions
|
||||
|
||||
tstest supports different test environments through file naming:
|
||||
|
||||
| Pattern | Environment | Example |
|
||||
|---------|-------------|---------|
|
||||
| `*.ts` | Node.js (default) | `test.basic.ts` |
|
||||
| `*.node.ts` | Node.js only | `test.api.node.ts` |
|
||||
| `*.chrome.ts` | Chrome browser | `test.dom.chrome.ts` |
|
||||
| `*.browser.ts` | Browser environment | `test.ui.browser.ts` |
|
||||
| `*.both.ts` | Both Node.js and browser | `test.isomorphic.both.ts` |
|
||||
|
||||
### Writing Tests with tapbundle
|
||||
## Writing Tests with tapbundle
|
||||
|
||||
tstest includes tapbundle, a powerful TAP-based test framework. Import it from the embedded tapbundle:
|
||||
|
||||
@@ -165,7 +295,7 @@ tap.test('my awesome test', async () => {
|
||||
expect(result).toEqual('expected value');
|
||||
});
|
||||
|
||||
tap.start();
|
||||
export default tap.start();
|
||||
```
|
||||
|
||||
**Module Exports**
|
||||
@@ -196,7 +326,7 @@ tap.test('async operations', async (tools) => {
|
||||
});
|
||||
|
||||
// Start test execution
|
||||
tap.start();
|
||||
export default tap.start();
|
||||
```
|
||||
|
||||
### Test Modifiers and Chaining
|
||||
@@ -231,20 +361,20 @@ tap.timeout(5000)
|
||||
```typescript
|
||||
tap.describe('User Management', () => {
|
||||
let testDatabase;
|
||||
|
||||
|
||||
tap.beforeEach(async () => {
|
||||
testDatabase = await createTestDB();
|
||||
});
|
||||
|
||||
|
||||
tap.afterEach(async () => {
|
||||
await testDatabase.cleanup();
|
||||
});
|
||||
|
||||
|
||||
tap.test('should create user', async () => {
|
||||
const user = await testDatabase.createUser({ name: 'John' });
|
||||
expect(user.id).toBeDefined();
|
||||
});
|
||||
|
||||
|
||||
tap.describe('User Permissions', () => {
|
||||
tap.test('should set admin role', async () => {
|
||||
// Nested describe blocks
|
||||
@@ -262,37 +392,37 @@ tap.test('using test tools', async (tools) => {
|
||||
// Delay utilities
|
||||
await tools.delayFor(1000); // delay for 1000ms
|
||||
await tools.delayForRandom(100, 500); // random delay between 100-500ms
|
||||
|
||||
|
||||
// Skip test conditionally
|
||||
tools.skipIf(process.env.CI === 'true', 'Skipping in CI');
|
||||
|
||||
|
||||
// Skip test unconditionally
|
||||
if (!apiKeyAvailable) {
|
||||
tools.skip('API key not available');
|
||||
}
|
||||
|
||||
|
||||
// Mark as todo
|
||||
tools.todo('Needs implementation');
|
||||
|
||||
|
||||
// Retry configuration
|
||||
tools.retry(3); // Set retry count
|
||||
|
||||
|
||||
// Timeout configuration
|
||||
tools.timeout(10000); // Set timeout to 10s
|
||||
|
||||
|
||||
// Context sharing between tests
|
||||
tools.context.set('userId', 12345);
|
||||
const userId = tools.context.get('userId');
|
||||
|
||||
|
||||
// Deferred promises
|
||||
const deferred = tools.defer();
|
||||
setTimeout(() => deferred.resolve('done'), 100);
|
||||
await deferred.promise;
|
||||
|
||||
|
||||
// Colored console output
|
||||
const coloredString = await tools.coloredString('Success!', 'green');
|
||||
console.log(coloredString);
|
||||
|
||||
|
||||
// Error handling helper
|
||||
const error = await tools.returnError(async () => {
|
||||
throw new Error('Expected error');
|
||||
@@ -306,10 +436,10 @@ tap.test('using test tools', async (tools) => {
|
||||
```typescript
|
||||
tap.test('snapshot test', async (tools) => {
|
||||
const output = generateComplexOutput();
|
||||
|
||||
|
||||
// Compare with saved snapshot
|
||||
await tools.matchSnapshot(output);
|
||||
|
||||
|
||||
// Named snapshots for multiple checks in one test
|
||||
await tools.matchSnapshot(output.header, 'header');
|
||||
await tools.matchSnapshot(output.body, 'body');
|
||||
@@ -339,9 +469,9 @@ tap.defineFixture('testPost', async (data) => ({
|
||||
tap.test('fixture test', async (tools) => {
|
||||
const user = await tools.fixture('testUser', { name: 'John' });
|
||||
const post = await tools.fixture('testPost', { authorId: user.id });
|
||||
|
||||
|
||||
expect(post.authorId).toEqual(user.id);
|
||||
|
||||
|
||||
// Factory pattern for multiple instances
|
||||
const users = await tools.factory('testUser').createMany(5);
|
||||
expect(users).toHaveLength(5);
|
||||
@@ -485,7 +615,7 @@ tap.test('first test', async (tools) => {
|
||||
tap.test('second test', async (tools) => {
|
||||
const sessionId = tools.context.get('sessionId');
|
||||
expect(sessionId).toBeDefined();
|
||||
|
||||
|
||||
// Cleanup
|
||||
tools.context.delete('sessionId');
|
||||
});
|
||||
@@ -506,9 +636,9 @@ tap.test('DOM manipulation', async () => {
|
||||
<button id="test-btn">Click Me</button>
|
||||
</div>
|
||||
`);
|
||||
|
||||
|
||||
expect(element.querySelector('h1').textContent).toEqual('Test Title');
|
||||
|
||||
|
||||
// Simulate interactions
|
||||
const button = element.querySelector('#test-btn');
|
||||
button.click();
|
||||
@@ -521,7 +651,7 @@ tap.test('CSS testing', async () => {
|
||||
font-size: 16px;
|
||||
}
|
||||
`;
|
||||
|
||||
|
||||
// styles is a string that can be injected into the page
|
||||
expect(styles).toInclude('color: red');
|
||||
});
|
||||
@@ -535,7 +665,7 @@ tap.test('error handling', async (tools) => {
|
||||
const error = await tools.returnError(async () => {
|
||||
await functionThatThrows();
|
||||
});
|
||||
|
||||
|
||||
expect(error).toBeInstanceOf(Error);
|
||||
expect(error.message).toEqual('Expected error message');
|
||||
});
|
||||
@@ -612,7 +742,7 @@ When assertions fail, tstest shows beautiful side-by-side diffs:
|
||||
String Diff:
|
||||
- Expected
|
||||
+ Received
|
||||
|
||||
|
||||
- Hello World
|
||||
+ Hello Universe
|
||||
|
||||
@@ -625,73 +755,6 @@ When assertions fail, tstest shows beautiful side-by-side diffs:
|
||||
}
|
||||
```
|
||||
|
||||
### Test Configuration (.tstest.json)
|
||||
|
||||
Configure test behavior with `.tstest.json` files:
|
||||
|
||||
```json
|
||||
{
|
||||
"timeout": 30000,
|
||||
"retries": 2,
|
||||
"bail": false,
|
||||
"parallel": true,
|
||||
"tags": ["unit", "fast"],
|
||||
"env": {
|
||||
"NODE_ENV": "test"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Configuration files are discovered in:
|
||||
1. Test file directory
|
||||
2. Parent directories (up to project root)
|
||||
3. Project root
|
||||
4. Home directory (`~/.tstest.json`)
|
||||
|
||||
Settings cascade and merge, with closer files taking precedence.
|
||||
|
||||
### Event-based Test Reporting
|
||||
|
||||
tstest emits detailed events during test execution for integration with CI/CD tools:
|
||||
|
||||
```json
|
||||
{"event":"suite:started","file":"test/api.test.ts","timestamp":"2025-05-26T10:30:00.000Z"}
|
||||
{"event":"test:started","name":"api endpoint validation","timestamp":"2025-05-26T10:30:00.100Z"}
|
||||
{"event":"test:progress","name":"api endpoint validation","message":"Validating response schema"}
|
||||
{"event":"test:completed","name":"api endpoint validation","passed":true,"duration":145}
|
||||
{"event":"suite:completed","file":"test/api.test.ts","passed":true,"total":2,"failed":0}
|
||||
```
|
||||
|
||||
### Enhanced TAP Protocol (Protocol V2)
|
||||
|
||||
tstest uses an enhanced TAP protocol with Unicode delimiters for better parsing:
|
||||
|
||||
```
|
||||
⟦TSTEST:EVENT:test:started⟧{"name":"my test","timestamp":"2025-05-26T10:30:00.000Z"}
|
||||
ok 1 my test
|
||||
⟦TSTEST:EVENT:test:completed⟧{"name":"my test","passed":true,"duration":145}
|
||||
```
|
||||
|
||||
This prevents conflicts with test output that might contain TAP-like formatting.
|
||||
|
||||
## Advanced Features
|
||||
|
||||
### Glob Pattern Support
|
||||
|
||||
Run specific test patterns:
|
||||
```bash
|
||||
# Run all unit tests
|
||||
tstest "test/unit/**/*.ts"
|
||||
|
||||
# Run all integration tests
|
||||
tstest "test/integration/*.test.ts"
|
||||
|
||||
# Run multiple patterns
|
||||
tstest "test/**/*.spec.ts" "test/**/*.test.ts"
|
||||
```
|
||||
|
||||
**Important**: Always quote glob patterns to prevent shell expansion. Without quotes, the shell will expand the pattern and only pass the first matching file to tstest.
|
||||
|
||||
### Enhanced Test Logging
|
||||
|
||||
The `--logfile` option provides intelligent test logging with automatic organization:
|
||||
@@ -849,6 +912,17 @@ tstest test/api/endpoints.test.ts --verbose --timeout 60
|
||||
|
||||
## Changelog
|
||||
|
||||
### Version 2.4.0
|
||||
- 🚀 **Multi-Runtime Architecture** - Support for Deno, Bun, Node.js, and Chromium
|
||||
- 🔀 **New Naming Convention** - Flexible `.runtime1+runtime2.ts` pattern
|
||||
- 🔄 **Migration Tool** - Easy migration from legacy naming (`.browser.ts`, `.both.ts`)
|
||||
- 🦕 **Deno Support** - Full Deno runtime with Node.js compatibility
|
||||
- 🐰 **Bun Support** - Ultra-fast Bun runtime integration
|
||||
- ⚡ **Dynamic Port Selection** - Random port allocation (30000-40000) prevents conflicts
|
||||
- 🏗️ **Runtime Adapter Pattern** - Extensible architecture for adding new runtimes
|
||||
- 📝 **Deprecation Warnings** - Helpful migration suggestions for legacy naming
|
||||
- ✅ **Comprehensive Tests** - Full test coverage for parser and migration tool
|
||||
|
||||
### Version 1.11.0
|
||||
- 👀 Added Watch Mode with `--watch`/`-w` flag for automatic test re-runs
|
||||
- 📊 Implemented real-time test progress updates with event streaming
|
||||
@@ -878,7 +952,7 @@ tstest test/api/endpoints.test.ts --verbose --timeout 60
|
||||
- 📝 Improved internal protocol design documentation
|
||||
- 🔧 Added protocol v2 utilities for future improvements
|
||||
|
||||
### Version 1.9.1
|
||||
### Version 1.9.1
|
||||
- 🐛 Fixed log file naming to preserve directory structure
|
||||
- 📁 Log files now prevent collisions: `test__dir__file.log`
|
||||
|
||||
@@ -901,7 +975,7 @@ tstest test/api/endpoints.test.ts --verbose --timeout 60
|
||||
|
||||
## License and Legal Information
|
||||
|
||||
This repository contains open-source code that is licensed under the MIT License. A copy of the MIT License can be found in the [license](license.md) file within this repository.
|
||||
This repository contains open-source code that is licensed under the MIT License. A copy of the MIT License can be found in the [license](license) file within this repository.
|
||||
|
||||
**Please note:** The MIT License does not grant permission to use the trade names, trademarks, service marks, or product names of the project, except as required for reasonable and customary use in describing the origin of the work and reproducing the content of the NOTICE file.
|
||||
|
||||
@@ -911,9 +985,9 @@ This project is owned and maintained by Task Venture Capital GmbH. The names and
|
||||
|
||||
### Company Information
|
||||
|
||||
Task Venture Capital GmbH
|
||||
Task Venture Capital GmbH
|
||||
Registered at District court Bremen HRB 35230 HB, Germany
|
||||
|
||||
For any legal inquiries or if you require further information, please contact us via email at hello@task.vc.
|
||||
|
||||
By using this repository, you acknowledge that you have read this section, agree to comply with its terms, and understand that the licensing of the code does not imply endorsement by Task Venture Capital GmbH of any derivative works.
|
||||
By using this repository, you acknowledge that you have read this section, agree to comply with its terms, and understand that the licensing of the code does not imply endorsement by Task Venture Capital GmbH of any derivative works.
|
||||
|
Reference in New Issue
Block a user