Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/node.js.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ jobs:
build:
strategy:
matrix:
node-version: [16, 18, 22]
node-version: ['22', '24']
os: [ubuntu-latest, macos-latest]
# See supported Node.js release schedule at https://nodejs.org/en/about/releases/

Expand Down
2 changes: 1 addition & 1 deletion .nvmrc
Original file line number Diff line number Diff line change
@@ -1 +1 @@
14
20.19
7 changes: 6 additions & 1 deletion core/cli-config/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {getArguments} from './arguments-parser';
import {getFileConfig} from './config-file-reader';
import {defaultConfiguration} from './default-config';
import {mergeConfigs} from './merge-configs';
import {validateRestartWorker} from './validate-restart-worker';

const isDebugging = () => !!inspector.url();

Expand All @@ -26,13 +27,17 @@ async function getConfig(argv: Array<string> = []): Promise<IConfig> {
temporaryConfig,
);

return mergeConfigs(
const config = mergeConfigs(
defaultConfiguration,
envConfig || {},
fileConfig || {},
args || {},
debugProperty,
);

validateRestartWorker(config);

return config;
}

export {defaultConfiguration, getConfig};
25 changes: 25 additions & 0 deletions core/cli-config/src/validate-restart-worker.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import {IConfig} from '@testring/types';

export function validateRestartWorker(config: IConfig): void {
const {restartWorker} = config;

if (
typeof restartWorker === 'boolean' ||
restartWorker === 'always'
) {
return;
}

if (
typeof restartWorker === 'number' &&
Number.isInteger(restartWorker) &&
restartWorker >= 0
) {
return;
}

throw new Error(
`Invalid "restartWorker" config value: ${JSON.stringify(restartWorker)}. ` +
`Expected a boolean, a non-negative integer, or the string "always".`,
);
}
21 changes: 21 additions & 0 deletions core/cli-config/test/get-config.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -134,4 +134,25 @@ describe('Get config', () => {

chai.expect(config).to.have.property('workerLimit', override);
});

it('should accept a numeric --restart-worker argument', async () => {
const config = await getConfig(['--restart-worker=3']);

chai.expect(config).to.have.property('restartWorker', 3);
});

it('should fail fast on an invalid --restart-worker argument', async () => {
let caughtError: Error | null = null;

try {
await getConfig(['--restart-worker=-2']);
} catch (error) {
caughtError = error as Error;
}

chai.expect(caughtError).to.be.instanceOf(Error);
chai.expect((caughtError as Error).message).to.match(
/Invalid "restartWorker"/,
);
});
});
63 changes: 63 additions & 0 deletions core/cli-config/test/validate-restart-worker.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
/// <reference types="mocha" />

import * as chai from 'chai';
import {IConfig} from '@testring/types';
import {validateRestartWorker} from '../src/validate-restart-worker';
import {defaultConfiguration} from '../src/default-config';

const withRestartWorker = (restartWorker: IConfig['restartWorker']): IConfig => ({
...defaultConfiguration,
restartWorker,
});

describe('validateRestartWorker', () => {
it('should accept false (default)', () => {
chai.expect(() =>
validateRestartWorker(withRestartWorker(false)),
).to.not.throw();
});

it('should accept true', () => {
chai.expect(() =>
validateRestartWorker(withRestartWorker(true)),
).to.not.throw();
});

it("should accept 'always'", () => {
chai.expect(() =>
validateRestartWorker(withRestartWorker('always')),
).to.not.throw();
});

it('should accept 0', () => {
chai.expect(() =>
validateRestartWorker(withRestartWorker(0)),
).to.not.throw();
});

it('should accept a positive integer', () => {
chai.expect(() =>
validateRestartWorker(withRestartWorker(5)),
).to.not.throw();
});

it('should reject a negative number', () => {
chai.expect(() =>
validateRestartWorker(withRestartWorker(-1)),
).to.throw(/Invalid "restartWorker"/);
});

it('should reject a non-integer number', () => {
chai.expect(() =>
validateRestartWorker(withRestartWorker(1.5)),
).to.throw(/Invalid "restartWorker"/);
});

it('should reject an arbitrary string', () => {
chai.expect(() =>
validateRestartWorker(
withRestartWorker('sometimes' as unknown as IConfig['restartWorker']),
),
).to.throw(/Invalid "restartWorker"/);
});
});
2 changes: 1 addition & 1 deletion core/fs-reader/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,6 @@
"@testring/pluggable-module": "0.8.11",
"@testring/types": "0.8.11",
"fast-glob": "3.3.2",
"p-limit": "3.1.0"
"p-limit": "7.3.1"
}
}
25 changes: 24 additions & 1 deletion core/fs-reader/src/file-resolver.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,29 @@
import * as fs from 'fs';
import * as path from 'path';
import {IFile} from '@testring/types';
const pLimit = require('p-limit');

// p-limit has been ESM-only since v4; this package's tsconfig targets
// CommonJS output, so a plain `import`/`require()` can't load it. Loading
// it via a string-wrapped dynamic import keeps the real ESM `import()`
// intact at runtime instead of letting tsc downlevel it to `require()` —
// the same technique `@testring/test-worker`'s worker-controller uses to
// load native ESM test files.
const dynamicImport: (specifier: string) => Promise<unknown> = new Function(
'specifier',
'return import(specifier);',
) as (specifier: string) => Promise<unknown>;

let pLimitModulePromise: Promise<typeof import('p-limit')> | null = null;

function importPLimit(): Promise<typeof import('p-limit')> {
if (pLimitModulePromise === null) {
pLimitModulePromise = dynamicImport('p-limit') as Promise<
typeof import('p-limit')
>;
}

return pLimitModulePromise;
}

const ERR_NO_FILES = new Error('No test files found');

Expand Down Expand Up @@ -33,6 +55,7 @@ export async function resolveFiles(files: Array<string>): Promise<IFile[]> {
throw ERR_NO_FILES;
}

const {default: pLimit} = await importPLimit();
const limit = pLimit(10);

// Limit concurrent file reads
Expand Down
7 changes: 0 additions & 7 deletions core/sandbox/.mocharc.json

This file was deleted.

4 changes: 0 additions & 4 deletions core/sandbox/.npmignore

This file was deleted.

2 changes: 0 additions & 2 deletions core/sandbox/.npmrc

This file was deleted.

16 changes: 0 additions & 16 deletions core/sandbox/README.md

This file was deleted.

25 changes: 0 additions & 25 deletions core/sandbox/package.json

This file was deleted.

1 change: 0 additions & 1 deletion core/sandbox/src/index.ts

This file was deleted.

Loading
Loading