Skip to content
Merged
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
8 changes: 8 additions & 0 deletions doc/api/errors.md
Original file line number Diff line number Diff line change
Expand Up @@ -3350,6 +3350,14 @@ The WASI instance has already started.

The WASI instance has not been started.

<a id="ERR_WEBASSEMBLY_NOT_SUPPORTED"></a>

### `ERR_WEBASSEMBLY_NOT_SUPPORTED`

A feature requiring WebAssembly was used, but WebAssembly is not supported or
has been disabled in the current environment (for example, when running with
`--jitless`).

<a id="ERR_WEBASSEMBLY_RESPONSE"></a>

### `ERR_WEBASSEMBLY_RESPONSE`
Expand Down
3 changes: 3 additions & 0 deletions lib/internal/errors.js
Original file line number Diff line number Diff line change
Expand Up @@ -1905,6 +1905,9 @@ E('ERR_VM_MODULE_NOT_MODULE',
'Provided module is not an instance of Module', Error);
E('ERR_VM_MODULE_STATUS', 'Module status %s', Error);
E('ERR_WASI_ALREADY_STARTED', 'WASI instance has already started', Error);
E('ERR_WEBASSEMBLY_NOT_SUPPORTED',
'WebAssembly is not supported in this environment, but is required for %s',
Error);
E('ERR_WEBASSEMBLY_RESPONSE', 'WebAssembly response %s', TypeError);
E('ERR_WORKER_INIT_FAILED', 'Worker initialization failure: %s', Error);
E('ERR_WORKER_INVALID_EXEC_ARGV', (errors, msg = 'invalid execArgv flags') =>
Expand Down
11 changes: 10 additions & 1 deletion lib/internal/fs/cp/cp.js
Original file line number Diff line number Diff line change
Expand Up @@ -300,8 +300,17 @@ async function setDestTimestamps(src, dest) {
return utimes(dest, updatedSrcStat.atime, updatedSrcStat.mtime);
}

function onDir(srcStat, destStat, src, dest, opts) {
async function onDir(srcStat, destStat, src, dest, opts) {
if (!destStat) return mkDirAndCopy(srcStat.mode, src, dest, opts);
if (opts.errorOnExist && !opts.force) {
throw new ERR_FS_CP_EEXIST({
message: `${dest} already exists`,
path: dest,
syscall: 'cp',
errno: EEXIST,
code: 'EEXIST',
});
}
return copyDir(src, dest, opts);
}

Expand Down
5 changes: 5 additions & 0 deletions lib/internal/test_runner/reporter/spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ const {
ArrayPrototypePush,
ArrayPrototypeShift,
ArrayPrototypeUnshift,
Date,
DatePrototypeToLocaleString,
} = primordials;
const assert = require('assert');
const Transform = require('internal/streams/transform');
Expand Down Expand Up @@ -101,6 +103,9 @@ class SpecReporter extends Transform {
if (data.file === undefined) {
return this.#formatFailedTestResults();
}
break;
case 'test:watch:restarted':
return `\nRestarted at ${DatePrototypeToLocaleString(new Date())}\n`;
}
}
_transform({ type, data }, encoding, callback) {
Expand Down
25 changes: 19 additions & 6 deletions lib/internal/test_runner/runner.js
Original file line number Diff line number Diff line change
Expand Up @@ -460,6 +460,9 @@ function runTestFile(path, filesWatcher, opts) {
finished(child.stdout, { __proto__: null, signal: t.signal }),
]);

// Close readline interface to prevent memory leak
rl.close();

if (watchMode) {
filesWatcher.runningProcesses.delete(path);
filesWatcher.runningSubtests.delete(path);
Expand Down Expand Up @@ -522,7 +525,7 @@ function watchFiles(testFiles, opts) {
}

// Watch for changes in current filtered files
watcher.on('changed', ({ owners, eventType }) => {
const onChanged = ({ owners, eventType }) => {
if (!opts.hasFiles && (eventType === 'rename' || eventType === 'change')) {
const updatedTestFiles = createTestFileList(opts.globPatterns, opts.cwd);
const newFileName = ArrayPrototypeFind(updatedTestFiles, (x) => !ArrayPrototypeIncludes(testFiles, x));
Expand Down Expand Up @@ -563,19 +566,29 @@ function watchFiles(testFiles, opts) {
triggerUncaughtException(error, true /* fromPromise */);
}));
}
});
};

watcher.on('changed', onChanged);

// Cleanup function to remove event listener and prevent memory leak
const cleanup = () => {
watcher.removeListener('changed', onChanged);
opts.root.harness.watching = false;
opts.root.postRun();
};

if (opts.signal) {
kResistStopPropagation ??= require('internal/event_target').kResistStopPropagation;
opts.signal.addEventListener(
'abort',
() => {
opts.root.harness.watching = false;
opts.root.postRun();
},
cleanup,
{ __proto__: null, once: true, [kResistStopPropagation]: true },
);
}

// Expose cleanup method for proper resource management
filesWatcher.cleanup = cleanup;

return filesWatcher;
}

Expand Down
2 changes: 1 addition & 1 deletion lib/internal/test_runner/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -551,7 +551,7 @@ function getCoverageReport(pad, summary, symbol, color, table) {

function printCoverageBodyTree(tree, depth = 0) {
for (const key in tree) {
if (tree[key].file) {
if (tree[key].file?.path) {
const file = tree[key].file;
const fileName = ArrayPrototypePop(StringPrototypeSplit(file.path, sep));

Expand Down
4 changes: 4 additions & 0 deletions lib/internal/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,13 +46,15 @@ const {
SymbolPrototypeGetDescription,
SymbolReplace,
SymbolSplit,
globalThis,
} = primordials;

const {
codes: {
ERR_NO_CRYPTO,
ERR_NO_TYPESCRIPT,
ERR_UNKNOWN_SIGNAL,
ERR_WEBASSEMBLY_NOT_SUPPORTED,
},
isErrorStackTraceLimitWritable,
overrideStackTrace,
Expand Down Expand Up @@ -244,6 +246,8 @@ function assertCrypto() {
function assertTypeScript() {
if (noTypeScript)
throw new ERR_NO_TYPESCRIPT();
if (globalThis.WebAssembly === undefined)
throw new ERR_WEBASSEMBLY_NOT_SUPPORTED('TypeScript');
}

/**
Expand Down
11 changes: 11 additions & 0 deletions test/es-module/test-typescript.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -342,3 +342,14 @@ test('check transform types warning', async () => {
assert.match(result.stdout, /Hello, TypeScript!/);
assert.strictEqual(result.code, 0);
});

test('expect error when executing a TypeScript file with --jitless', async () => {
const result = await spawnPromisified(process.execPath, [
'--jitless',
fixtures.path('typescript/ts/test-typescript.ts'),
]);

assert.match(result.stderr, /ERR_WEBASSEMBLY_NOT_SUPPORTED/);
assert.match(result.stderr, /WebAssembly is not supported in this environment, but is required for TypeScript/);
assert.strictEqual(result.code, 1);
});
4 changes: 4 additions & 0 deletions test/fixtures/test-runner/coverage-file-name/file/file
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
'use strict';
module.exports.fn = function() {
return 1;
};
7 changes: 7 additions & 0 deletions test/fixtures/test-runner/coverage-file-name/test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
'use strict';
const { fn } = require('./file/file');
const test = require('node:test');

test('coverage with file/file directory structure', () => {
fn();
});
30 changes: 30 additions & 0 deletions test/parallel/test-fs-cp-async-dir-exists-error-on-exist.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// This tests that cp() returns error if errorOnExist is true, force is false,
// and the destination directory already exists (even if contents don't conflict).

import { mustCall } from '../common/index.mjs';
import { nextdir } from '../common/fs.js';
import assert from 'node:assert';
import { cp, mkdirSync, writeFileSync } from 'node:fs';
import tmpdir from '../common/tmpdir.js';

tmpdir.refresh();

const src = nextdir();
const dest = nextdir();

// Create source directory with a file
mkdirSync(src);
writeFileSync(`${src}/file.txt`, 'test');

// Create destination directory with different file
mkdirSync(dest);
writeFileSync(`${dest}/other.txt`, 'existing');

// Should fail because dest directory already exists
cp(src, dest, {
recursive: true,
errorOnExist: true,
force: false,
}, mustCall((err) => {
assert.strictEqual(err.code, 'ERR_FS_CP_EEXIST');
}));
16 changes: 16 additions & 0 deletions test/parallel/test-runner-coverage.js
Original file line number Diff line number Diff line change
Expand Up @@ -550,3 +550,19 @@ test('correctly prints the coverage report of files contained in parent director
assert(result.stdout.toString().includes(report));
assert.strictEqual(result.status, 0);
});

// Regression test for https://github.com/nodejs/node/issues/61080
test('coverage with directory and file named "file"', skipIfNoInspector, () => {
const fixture = fixtures.path('test-runner', 'coverage-file-name', 'test.js');
const args = [
'--experimental-test-coverage',
'--test-reporter',
'tap',
fixture,
];
const result = spawnSync(process.execPath, args);

assert.strictEqual(result.stderr.toString(), '');
assert.strictEqual(result.status, 0);
assert(result.stdout.toString().includes('start of coverage report'));
});
18 changes: 18 additions & 0 deletions test/test-runner/test-output-spec-reporter-watch-restart.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import '../common/index.mjs';
import { spec as SpecReporter } from 'node:test/reporters';
import assert from 'node:assert';

const reporter = new SpecReporter();
let output = '';

reporter.on('data', (chunk) => {
output += chunk;
});

reporter.write({
type: 'test:watch:restarted',
});

reporter.end();

assert.match(output, /Restarted at .+/);
Loading