Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 7e67b64a6e | |||
| 1ce730d4f2 | |||
| 9357d6e7ef | |||
| 973ce771d2 | |||
| 8441881d92 | |||
| 16ca3b6374 |
21
changelog.md
21
changelog.md
@@ -1,5 +1,26 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## 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
13
deno.json
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"experimentalDecorators": true,
|
||||||
|
"useDefineForClassFields": false,
|
||||||
|
"lib": [
|
||||||
|
"ES2022",
|
||||||
|
"DOM"
|
||||||
|
],
|
||||||
|
"target": "ES2022"
|
||||||
|
},
|
||||||
|
"nodeModulesDir": true,
|
||||||
|
"version": "2.8.2"
|
||||||
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@git.zone/tstest",
|
"name": "@git.zone/tstest",
|
||||||
"version": "2.7.0",
|
"version": "2.8.2",
|
||||||
"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": {
|
||||||
|
|||||||
91
test/decorator.all.ts
Normal file
91
test/decorator.all.ts
Normal 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();
|
||||||
@@ -3,6 +3,6 @@
|
|||||||
*/
|
*/
|
||||||
export const commitinfo = {
|
export const commitinfo = {
|
||||||
name: '@git.zone/tstest',
|
name: '@git.zone/tstest',
|
||||||
version: '2.7.0',
|
version: '2.8.2',
|
||||||
description: 'a test utility to run tests that match test/**/*.ts'
|
description: 'a test utility to run tests that match test/**/*.ts'
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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',
|
||||||
|
|||||||
@@ -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, '');
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user