Compare commits

..

10 Commits

21 changed files with 2573 additions and 3360 deletions

View File

@@ -22,5 +22,6 @@
} }
} }
} }
] ],
"deno.enable": false
} }

View File

@@ -1,5 +1,44 @@
# Changelog # Changelog
## 2025-11-19 - 3.0.0 - BREAKING CHANGE(tapbundle_serverside)
Rename Node-specific tapbundle module to tapbundle_serverside and migrate server-side utilities
- Change public export in package.json from ./tapbundle_node to ./tapbundle_serverside — consumers must update imports to @git.zone/tstest/tapbundle_serverside
- Move and re-create Node-only implementation files under ts_tapbundle_serverside (plugins, paths, classes.tapnodetools, classes.testfileprovider, index, tspublish.json) and remove legacy ts_tapbundle_node sources
- Update internal imports and tests to reference the new tapbundle_serverside path (e.g. test/tapbundle/test.node.ts updated)
- Update documentation (readme.md and readme.hints.md) to describe the new tapbundle_serverside export and its server-side utilities
- Ensure build outputs and publish metadata reflect the new module directory (tspublish.json order preserved)
## 2025-11-19 - 2.8.3 - fix(dependencies)
Update dependency versions
- Bump devDependency @git.zone/tsbuild to ^3.1.0
- Upgrade @git.zone/tsrun to ^2.0.0 (major)
- Upgrade @push.rocks/smartenv to ^6.0.0 (major)
- Upgrade @push.rocks/smartrequest to ^5.0.1 (major/feature in dependency)
- Patch updates: @api.global/typedserver → ^3.0.80, @git.zone/tsbundle → ^2.5.2, @push.rocks/smartmongo → ^2.0.14
## 2025-11-17 - 2.8.2 - fix(logging)
Include runtime identifier in per-test logfile name and sanitize runtime string
- Append a sanitized runtime identifier to the per-test log filename (format: <safeFilename>__<safeRuntime>.log) so runs for different runtimes don't clash
- Sanitize runtime names by lowercasing and removing non-alphanumeric characters to produce filesystem-safe filenames
## 2025-11-17 - 2.8.1 - fix(config)
Remove Bun config file and set deno.json useDefineForClassFields to false for compatibility
- Removed bunfig.toml (Bun-specific TypeScript decorator configuration) — stops shipping a project-local Bun transpiler config.
- Updated deno.json: set compilerOptions.useDefineForClassFields = false to keep legacy class field semantics and avoid runtime/emit incompatibilities in Deno.
## 2025-11-17 - 2.8.0 - feat(runtime-adapters)
Enable TypeScript decorator support for Deno and Bun runtimes and add decorator tests
- Add bunfig.toml to enable experimentalDecorators for Bun runtime
- Add deno.json to enable experimentalDecorators and set target/lib for Deno
- Update Bun runtime adapter to note bunfig.toml discovery so Bun runs with decorator support
- Update Deno runtime adapter to auto-detect deno.json / deno.jsonc and pass configPath in default options
- Add integration tests for decorators (test/decorator.all.ts) to verify decorator support across runtimes
## 2025-10-26 - 2.7.0 - feat(tapbundle_protocol) ## 2025-10-26 - 2.7.0 - feat(tapbundle_protocol)
Add package export for tapbundle_protocol to expose protocol utilities Add package export for tapbundle_protocol to expose protocol utilities

13
deno.json Normal file
View File

@@ -0,0 +1,13 @@
{
"compilerOptions": {
"experimentalDecorators": true,
"useDefineForClassFields": false,
"lib": [
"ES2022",
"DOM"
],
"target": "ES2022"
},
"nodeModulesDir": true,
"version": "3.0.0"
}

1371
deno.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,12 +1,12 @@
{ {
"name": "@git.zone/tstest", "name": "@git.zone/tstest",
"version": "2.7.0", "version": "3.0.0",
"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": {
".": "./dist_ts/index.js", ".": "./dist_ts/index.js",
"./tapbundle": "./dist_ts_tapbundle/index.js", "./tapbundle": "./dist_ts_tapbundle/index.js",
"./tapbundle_node": "./dist_ts_tapbundle_node/index.js", "./tapbundle_serverside": "./dist_ts_tapbundle_serverside/index.js",
"./tapbundle_protocol": "./dist_ts_tapbundle_protocol/index.js" "./tapbundle_protocol": "./dist_ts_tapbundle_protocol/index.js"
}, },
"type": "module", "type": "module",
@@ -25,29 +25,29 @@
"buildDocs": "tsdoc" "buildDocs": "tsdoc"
}, },
"devDependencies": { "devDependencies": {
"@git.zone/tsbuild": "^2.6.8", "@git.zone/tsbuild": "^3.1.0",
"@types/node": "^22.15.21" "@types/node": "^22.15.21"
}, },
"dependencies": { "dependencies": {
"@api.global/typedserver": "^3.0.79", "@api.global/typedserver": "^3.0.80",
"@git.zone/tsbundle": "^2.5.1", "@git.zone/tsbundle": "^2.5.2",
"@git.zone/tsrun": "^1.6.2", "@git.zone/tsrun": "^2.0.0",
"@push.rocks/consolecolor": "^2.0.3", "@push.rocks/consolecolor": "^2.0.3",
"@push.rocks/qenv": "^6.1.3", "@push.rocks/qenv": "^6.1.3",
"@push.rocks/smartbrowser": "^2.0.8", "@push.rocks/smartbrowser": "^2.0.8",
"@push.rocks/smartchok": "^1.1.1", "@push.rocks/smartchok": "^1.1.1",
"@push.rocks/smartcrypto": "^2.0.4", "@push.rocks/smartcrypto": "^2.0.4",
"@push.rocks/smartdelay": "^3.0.5", "@push.rocks/smartdelay": "^3.0.5",
"@push.rocks/smartenv": "^5.0.13", "@push.rocks/smartenv": "^6.0.0",
"@push.rocks/smartexpect": "^2.5.0", "@push.rocks/smartexpect": "^2.5.0",
"@push.rocks/smartfile": "^11.2.7", "@push.rocks/smartfile": "^11.2.7",
"@push.rocks/smartjson": "^5.2.0", "@push.rocks/smartjson": "^5.2.0",
"@push.rocks/smartlog": "^3.1.10", "@push.rocks/smartlog": "^3.1.10",
"@push.rocks/smartmongo": "^2.0.12", "@push.rocks/smartmongo": "^2.0.14",
"@push.rocks/smartnetwork": "^4.4.0", "@push.rocks/smartnetwork": "^4.4.0",
"@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": "^4.3.2", "@push.rocks/smartrequest": "^5.0.1",
"@push.rocks/smarts3": "^2.2.6", "@push.rocks/smarts3": "^2.2.6",
"@push.rocks/smartshell": "^3.3.0", "@push.rocks/smartshell": "^3.3.0",
"@push.rocks/smarttime": "^4.1.1", "@push.rocks/smarttime": "^4.1.1",

4301
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@@ -6,7 +6,7 @@ This project integrates tstest with tapbundle through a modular architecture:
1. **tstest** (`/ts/`) - The test runner that discovers and executes test files 1. **tstest** (`/ts/`) - The test runner that discovers and executes test files
2. **tapbundle** (`/ts_tapbundle/`) - The TAP testing framework for writing tests 2. **tapbundle** (`/ts_tapbundle/`) - The TAP testing framework for writing tests
3. **tapbundle_node** (`/ts_tapbundle_node/`) - Node.js-specific testing utilities 3. **tapbundle_serverside** (`/ts_tapbundle_serverside/`) - Server-side testing utilities (runCommand, env vars, HTTPS certs, MongoDB, S3, test assets)
## How Components Work Together ## How Components Work Together
@@ -31,7 +31,7 @@ This project integrates tstest with tapbundle through a modular architecture:
1. **Import Structure** 1. **Import Structure**
- Test files import from local tapbundle: `import { tap, expect } from '../../ts_tapbundle/index.js'` - Test files import from local tapbundle: `import { tap, expect } from '../../ts_tapbundle/index.js'`
- Node-specific tests also import from tapbundle_node: `import { tapNodeTools } from '../../ts_tapbundle_node/index.js'` - Server-side tests also import from tapbundle_serverside for Node.js-only utilities: `import { tapNodeTools } from '../../ts_tapbundle_serverside/index.js'`
2. **WebHelpers** 2. **WebHelpers**
- Browser tests can use webhelpers for DOM manipulation - Browser tests can use webhelpers for DOM manipulation
@@ -41,7 +41,7 @@ This project integrates tstest with tapbundle through a modular architecture:
3. **Build System** 3. **Build System**
- Uses `tsbuild tsfolders` to compile TypeScript (invoked by `pnpm build`) - Uses `tsbuild tsfolders` to compile TypeScript (invoked by `pnpm build`)
- Maintains separate output directories: `/dist_ts/`, `/dist_ts_tapbundle/`, `/dist_ts_tapbundle_node/`, `/dist_ts_tapbundle_protocol/` - Maintains separate output directories: `/dist_ts/`, `/dist_ts_tapbundle/`, `/dist_ts_tapbundle_serverside/`, `/dist_ts_tapbundle_protocol/`
- Compilation order is resolved automatically based on dependencies in tspublish.json files - Compilation order is resolved automatically based on dependencies in tspublish.json files
- Protocol imports use compiled dist directories: - Protocol imports use compiled dist directories:
```typescript ```typescript

View File

@@ -318,9 +318,34 @@ tstest provides multiple exports for different use cases:
- `@git.zone/tstest` - Main CLI and test runner functionality - `@git.zone/tstest` - Main CLI and test runner functionality
- `@git.zone/tstest/tapbundle` - Browser-compatible test framework - `@git.zone/tstest/tapbundle` - Browser-compatible test framework
- `@git.zone/tstest/tapbundle_node` - Node.js-specific test utilities - `@git.zone/tstest/tapbundle_serverside` - Server-side testing utilities for Node.js-only tests (*.node.ts files)
- Execute shell commands during tests
- Manage environment variables on-demand with secure storage
- Generate self-signed HTTPS certificates for testing secure connections
- Create ephemeral MongoDB instances for database testing
- Create local S3-compatible storage for object storage testing
- Download and manage test assets (e.g., Docker images)
- `@git.zone/tstest/tapbundle_protocol` - Protocol V2 emitter and parser for TAP extensions - `@git.zone/tstest/tapbundle_protocol` - Protocol V2 emitter and parser for TAP extensions
### When to Use tapbundle_serverside
Use `@git.zone/tstest/tapbundle_serverside` when your tests:
- Run exclusively on Node.js server-side (*.node.ts test files)
- Need to execute shell commands or interact with the file system
- Require environment variable management with secure on-demand prompts
- Test HTTPS servers and need self-signed certificates
- Interact with databases (MongoDB) and need ephemeral test instances
- Work with object storage (S3-compatible) and need local testing
- Require test assets like Docker images or other downloadable files
**Important:** tapbundle_serverside utilities are NOT available in:
- Browser environments
- Deno runtime
- Bun runtime
For cross-runtime tests, only import tapbundle_serverside in `.node.ts` files where you need server-side specific functionality.
## tapbundle Protocol V2 ## tapbundle Protocol V2
tstest includes an enhanced TAP protocol (Protocol V2) that extends standard TAP 13 with additional metadata while maintaining backwards compatibility. tstest includes an enhanced TAP protocol (Protocol V2) that extends standard TAP 13 with additional metadata while maintaining backwards compatibility.

91
test/decorator.all.ts Normal file
View File

@@ -0,0 +1,91 @@
import { tap, expect } from '../ts_tapbundle/index.js';
/**
* Simple class decorator for testing decorator support across runtimes
*/
function testDecorator(target: any) {
target.decoratorApplied = true;
target.decoratorData = 'Decorator was applied successfully';
return target;
}
/**
* Method decorator for testing
*/
function logMethod(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
const originalMethod = descriptor.value;
descriptor.value = function (...args: any[]) {
const result = originalMethod.apply(this, args);
return `[logged] ${result}`;
};
return descriptor;
}
/**
* Parameter decorator for testing
*/
function validateParam(target: any, propertyKey: string, parameterIndex: number) {
// Mark that parameter validation decorator was applied
if (!target.decoratedParams) {
target.decoratedParams = {};
}
if (!target.decoratedParams[propertyKey]) {
target.decoratedParams[propertyKey] = [];
}
target.decoratedParams[propertyKey].push(parameterIndex);
}
/**
* Test class with decorators
*/
@testDecorator
class TestClass {
public name: string = 'test';
@logMethod
public greet(message: string): string {
return `Hello, ${message}!`;
}
public getValue(): number {
return 42;
}
public testParams(@validateParam value: string): string {
return value;
}
}
// Tests
tap.test('Class decorator should be applied', async () => {
expect((TestClass as any).decoratorApplied).toEqual(true);
expect((TestClass as any).decoratorData).toEqual('Decorator was applied successfully');
});
tap.test('Method decorator should modify method behavior', async () => {
const instance = new TestClass();
const result = instance.greet('World');
expect(result).toEqual('[logged] Hello, World!');
});
tap.test('Regular methods should work normally', async () => {
const instance = new TestClass();
expect(instance.getValue()).toEqual(42);
expect(instance.name).toEqual('test');
});
tap.test('Parameter decorator should be applied', async () => {
const decoratedParams = (TestClass.prototype as any).decoratedParams;
expect(decoratedParams).toBeDefined();
expect(decoratedParams.testParams).toBeDefined();
expect(decoratedParams.testParams).toContain(0);
});
tap.test('Decorator metadata preservation', async () => {
const instance = new TestClass();
expect(instance instanceof TestClass).toEqual(true);
expect(instance.constructor.name).toEqual('TestClass');
expect(instance.testParams('hello')).toEqual('hello');
});
export default tap.start();

View File

@@ -1,6 +1,6 @@
import { tap, expect } from '../../ts_tapbundle/index.js'; import { tap, expect } from '../../ts_tapbundle/index.js';
import { tapNodeTools } from '../../ts_tapbundle_node/index.js'; import { tapNodeTools } from '../../ts_tapbundle_serverside/index.js';
tap.test('should execure a command', async () => { tap.test('should execure a command', async () => {
const result = await tapNodeTools.runCommand('ls -la'); const result = await tapNodeTools.runCommand('ls -la');

View File

@@ -3,6 +3,6 @@
*/ */
export const commitinfo = { export const commitinfo = {
name: '@git.zone/tstest', name: '@git.zone/tstest',
version: '2.7.0', version: '3.0.0',
description: 'a test utility to run tests that match test/**/*.ts' description: 'a test utility to run tests that match test/**/*.ts'
} }

View File

@@ -69,6 +69,9 @@ export class BunRuntimeAdapter extends RuntimeAdapter {
const args: string[] = ['run']; const args: string[] = ['run'];
// Note: Bun automatically discovers bunfig.toml in the current directory
// This ensures TypeScript decorator support is enabled if bunfig.toml is present
// Add extra args // Add extra args
if (mergedOptions.extraArgs && mergedOptions.extraArgs.length > 0) { if (mergedOptions.extraArgs && mergedOptions.extraArgs.length > 0) {
args.push(...mergedOptions.extraArgs); args.push(...mergedOptions.extraArgs);

View File

@@ -31,8 +31,20 @@ export class DenoRuntimeAdapter extends RuntimeAdapter {
* Get default Deno options * Get default Deno options
*/ */
protected getDefaultOptions(): DenoOptions { protected getDefaultOptions(): DenoOptions {
// Auto-detect deno.json or deno.jsonc config file for TypeScript decorator support
let configPath: string | undefined;
const denoJsonPath = plugins.path.join(process.cwd(), 'deno.json');
const denoJsoncPath = plugins.path.join(process.cwd(), 'deno.jsonc');
if (plugins.smartfile.fs.fileExistsSync(denoJsonPath)) {
configPath = denoJsonPath;
} else if (plugins.smartfile.fs.fileExistsSync(denoJsoncPath)) {
configPath = denoJsoncPath;
}
return { return {
...super.getDefaultOptions(), ...super.getDefaultOptions(),
configPath,
permissions: [ permissions: [
'--allow-read', '--allow-read',
'--allow-env', '--allow-env',

View File

@@ -200,15 +200,18 @@ export class TsTestLogger {
.replace(/\//g, '__') // Replace path separators with double underscores .replace(/\//g, '__') // Replace path separators with double underscores
.replace(/\.ts$/, '') // Remove .ts extension .replace(/\.ts$/, '') // Remove .ts extension
.replace(/^\.\.__|^\.__|^__/, ''); // Clean up leading separators from relative paths .replace(/^\.\.__|^\.__|^__/, ''); // Clean up leading separators from relative paths
this.currentTestLogFile = path.join('.nogit', 'testlogs', `${safeFilename}.log`); // Sanitize runtime name for use in filename (lowercase, no spaces/dots/special chars)
const safeRuntime = runtime.toLowerCase().replace(/[^a-z0-9]/g, '');
this.currentTestLogFile = path.join('.nogit', 'testlogs', `${safeFilename}__${safeRuntime}.log`);
// Ensure the directory exists // Ensure the directory exists
const logDir = path.dirname(this.currentTestLogFile); const logDir = path.dirname(this.currentTestLogFile);
if (!fs.existsSync(logDir)) { if (!fs.existsSync(logDir)) {
fs.mkdirSync(logDir, { recursive: true }); fs.mkdirSync(logDir, { recursive: true });
} }
// Clear the log file for this test // Clear the log file for this test
fs.writeFileSync(this.currentTestLogFile, ''); fs.writeFileSync(this.currentTestLogFile, '');
} }

View File

@@ -1,17 +1,17 @@
# @git.zone/tstest/tapbundle_node # @git.zone/tstest/tapbundle_serverside
> 🔧 Node.js-specific testing utilities for enhanced test capabilities > 🔧 Server-side testing utilities for Node.js runtime tests
## Installation ## Installation
```bash ```bash
# tapbundle_node is included as part of @git.zone/tstest # tapbundle_serverside is included as part of @git.zone/tstest
pnpm install --save-dev @git.zone/tstest pnpm install --save-dev @git.zone/tstest
``` ```
## Overview ## Overview
`@git.zone/tstest/tapbundle_node` provides Node.js-specific utilities for testing. These tools are only available when running tests in Node.js runtime and provide functionality for working with environment variables, shell commands, test databases, storage systems, and HTTPS certificates. `@git.zone/tstest/tapbundle_serverside` provides server-side testing utilities exclusively for Node.js runtime. These tools enable shell command execution, environment variable management, HTTPS certificate generation, database testing, object storage testing, and test asset management - all functionality that only makes sense on the server-side.
## Key Features ## Key Features
@@ -25,11 +25,11 @@ pnpm install --save-dev @git.zone/tstest
## Basic Usage ## Basic Usage
```typescript ```typescript
import { tapNodeTools } from '@git.zone/tstest/tapbundle_node'; import { tapNodeTools } from '@git.zone/tstest/tapbundle_serverside';
import { tap } from '@git.zone/tstest/tapbundle'; import { tap } from '@git.zone/tstest/tapbundle';
tap.test('should use node-specific tools', async () => { tap.test('should use server-side tools', async () => {
// Use Node.js-specific utilities // Execute shell commands on the server-side
const result = await tapNodeTools.runCommand('echo "hello"'); const result = await tapNodeTools.runCommand('echo "hello"');
console.log(result); console.log(result);
}); });
@@ -131,7 +131,7 @@ tap.test('should create HTTPS server', async () => {
Create an ephemeral MongoDB instance for testing. Automatically started and ready to use. Create an ephemeral MongoDB instance for testing. Automatically started and ready to use.
```typescript ```typescript
import { tapNodeTools } from '@git.zone/tstest/tapbundle_node'; import { tapNodeTools } from '@git.zone/tstest/tapbundle_serverside';
tap.test('should use MongoDB', async () => { tap.test('should use MongoDB', async () => {
const mongoInstance = await tapNodeTools.createSmartmongo(); const mongoInstance = await tapNodeTools.createSmartmongo();
@@ -170,7 +170,7 @@ export default tap.start();
Create a local S3-compatible storage instance for testing object storage operations. Create a local S3-compatible storage instance for testing object storage operations.
```typescript ```typescript
import { tapNodeTools } from '@git.zone/tstest/tapbundle_node'; import { tapNodeTools } from '@git.zone/tstest/tapbundle_serverside';
tap.test('should use S3 storage', async () => { tap.test('should use S3 storage', async () => {
const s3Instance = await tapNodeTools.createSmarts3(); const s3Instance = await tapNodeTools.createSmarts3();
@@ -209,7 +209,7 @@ Utility for downloading and managing test assets.
Download the Alpine Linux Docker image as a tarball for testing. Download the Alpine Linux Docker image as a tarball for testing.
```typescript ```typescript
import { tapNodeTools } from '@git.zone/tstest/tapbundle_node'; import { tapNodeTools } from '@git.zone/tstest/tapbundle_serverside';
tap.test('should provide docker image', async () => { tap.test('should provide docker image', async () => {
const tarballPath = await tapNodeTools.testFileProvider.getDockerAlpineImageAsLocalTarball(); const tarballPath = await tapNodeTools.testFileProvider.getDockerAlpineImageAsLocalTarball();
@@ -238,7 +238,7 @@ export default tap.start();
The module exports useful path constants: The module exports useful path constants:
```typescript ```typescript
import * as paths from '@git.zone/tstest/tapbundle_node/paths'; import * as paths from '@git.zone/tstest/tapbundle_serverside/paths';
console.log(paths.cwd); // Current working directory console.log(paths.cwd); // Current working directory
console.log(paths.testFilesDir); // ./.nogit/testfiles/ console.log(paths.testFilesDir); // ./.nogit/testfiles/
@@ -249,7 +249,7 @@ console.log(paths.testFilesDir); // ./.nogit/testfiles/
### Testing with External Services ### Testing with External Services
```typescript ```typescript
import { tapNodeTools } from '@git.zone/tstest/tapbundle_node'; import { tapNodeTools } from '@git.zone/tstest/tapbundle_serverside';
import { tap, expect } from '@git.zone/tstest/tapbundle'; import { tap, expect } from '@git.zone/tstest/tapbundle';
tap.describe('User Service Integration', () => { tap.describe('User Service Integration', () => {
@@ -280,7 +280,7 @@ export default tap.start();
### Testing HTTPS Servers ### Testing HTTPS Servers
```typescript ```typescript
import { tapNodeTools } from '@git.zone/tstest/tapbundle_node'; import { tapNodeTools } from '@git.zone/tstest/tapbundle_serverside';
import { tap, expect } from '@git.zone/tstest/tapbundle'; import { tap, expect } from '@git.zone/tstest/tapbundle';
import * as https from 'https'; import * as https from 'https';
@@ -311,7 +311,7 @@ export default tap.start();
### Environment-Dependent Tests ### Environment-Dependent Tests
```typescript ```typescript
import { tapNodeTools } from '@git.zone/tstest/tapbundle_node'; import { tapNodeTools } from '@git.zone/tstest/tapbundle_serverside';
import { tap, expect } from '@git.zone/tstest/tapbundle'; import { tap, expect } from '@git.zone/tstest/tapbundle';
tap.test('should authenticate with GitHub', async () => { tap.test('should authenticate with GitHub', async () => {
@@ -332,12 +332,14 @@ export default tap.start();
## Runtime Requirements ## Runtime Requirements
⚠️ **Node.js Only**: All utilities in this module require Node.js runtime. They will not work in: ⚠️ **Server-Side Only (Node.js)**: All utilities in this module are designed exclusively for server-side testing in Node.js runtime. They provide functionality like shell command execution, file system operations, and process management that only make sense on the server.
**NOT available in:**
- Browser environments - Browser environments
- Deno runtime - Deno runtime
- Bun runtime - Bun runtime
For multi-runtime tests, use these utilities only in `.node.ts` test files. **Important:** Import tapbundle_serverside only in tests that run exclusively on the server-side (`.node.ts` test files). For cross-runtime tests, these utilities will fail in non-Node environments.
## File Naming ## File Naming