Skip to content
Open
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 PORTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ Tests covering the engine-specific part of Node-API, defined in `js_native_api.h
| `test_dataview` | Not ported | Medium |
| `test_date` | Ported ✅ | Easy |
| `test_error` | Ported ✅ | Medium |
| `test_exception` | Not ported | Medium |
| `test_exception` | Ported ✅ | Medium |
| `test_finalizer` | Not ported | Medium |
| `test_function` | Not ported | Medium |
| `test_general` | Not ported | Hard |
Expand Down
38 changes: 23 additions & 15 deletions eslint.config.js
Original file line number Diff line number Diff line change
@@ -1,16 +1,14 @@
import { defineConfig, globalIgnores } from "eslint/config";
import globals from "globals";
import eslint from '@eslint/js';
import tseslint from 'typescript-eslint';
import eslint from "@eslint/js";
import tseslint from "typescript-eslint";

export default defineConfig([
globalIgnores(["**/CMakeFiles/**"]),
eslint.configs.recommended,
tseslint.configs.recommended,
{
files: [
"tests/**/*.js",
],
files: ["tests/**/*.{mjs,js}"],
languageOptions: {
// Only allow ECMAScript built-ins and CTS harness globals.
// This causes no-undef to flag any runtime-specific API (setTimeout, process, Buffer, etc.).
Expand All @@ -22,27 +20,37 @@ export default defineConfig([
mustCall: "readonly",
mustNotCall: "readonly",
gcUntil: "readonly",
spawnTest: "readonly",
experimentalFeatures: "readonly",
napiVersion: "readonly",
skipTest: "readonly",
},
},
rules: {
"no-undef": "error",
"no-restricted-imports": ["error", {
patterns: ["*"],
}],
"no-restricted-syntax": ["error",
{ selector: "MemberExpression[object.name='globalThis']", message: "Avoid globalThis access in test files — use CTS harness globals instead" },
{ selector: "MemberExpression[object.name='global']", message: "Avoid global access in test files — use CTS harness globals instead" }
"no-restricted-imports": [
"error",
{
patterns: ["*"],
},
],
"no-restricted-syntax": [
"error",
{
selector: "MemberExpression[object.name='globalThis']",
message:
"Avoid globalThis access in test files — use CTS harness globals instead",
},
{
selector: "MemberExpression[object.name='global']",
message:
"Avoid global access in test files — use CTS harness globals instead",
},
],
},
},
{
files: [
"implementors/**/*.{js,ts}",
"scripts/**/*.{js,mjs}",
],
files: ["implementors/**/*.{js,ts}", "scripts/**/*.{js,mjs}"],
languageOptions: {
globals: {
...globals.es2025,
Expand Down
26 changes: 12 additions & 14 deletions implementors/node/assert.js
Original file line number Diff line number Diff line change
@@ -1,24 +1,22 @@

import {
ok,
strictEqual,
notStrictEqual,
deepStrictEqual,
throws,
match,
} from "node:assert/strict";

const assert = Object.assign(
(value, message) => ok(value, message),
{
ok: (value, message) => ok(value, message),
strictEqual: (actual, expected, message) =>
strictEqual(actual, expected, message),
notStrictEqual: (actual, expected, message) =>
notStrictEqual(actual, expected, message),
deepStrictEqual: (actual, expected, message) =>
deepStrictEqual(actual, expected, message),
throws: (fn, error, message) => throws(fn, error, message),
},
);
const assert = Object.assign((value, message) => ok(value, message), {
ok: (value, message) => ok(value, message),
strictEqual: (actual, expected, message) =>
strictEqual(actual, expected, message),
notStrictEqual: (actual, expected, message) =>
notStrictEqual(actual, expected, message),
deepStrictEqual: (actual, expected, message) =>
deepStrictEqual(actual, expected, message),
throws: (fn, error, message) => throws(fn, error, message),
match: (string, regex, message) => match(string, regex, message),
});

Object.assign(globalThis, { assert });
66 changes: 66 additions & 0 deletions implementors/node/child_process.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import { spawnSync } from "node:child_process";
import path from "node:path";

const ROOT_PATH = path.resolve(import.meta.dirname, "..", "..");
const FEATURES_MODULE_PATH = path.join(
ROOT_PATH,
"implementors",
"node",
"features.js",
);
const ASSERT_MODULE_PATH = path.join(
ROOT_PATH,
"implementors",
"node",
"assert.js",
);
const LOAD_ADDON_MODULE_PATH = path.join(
ROOT_PATH,
"implementors",
"node",
"load-addon.js",
);
const GC_MODULE_PATH = path.join(ROOT_PATH, "implementors", "node", "gc.js");
const MUST_CALL_MODULE_PATH = path.join(
ROOT_PATH,
"implementors",
"node",
"must-call.js",
);
const CHILD_PROCESS_MODULE_PATH = path.join(
ROOT_PATH,
"implementors",
"node",
"child_process.js",
);

const spawnTest = (filePath, options = {}) => {
const result = spawnSync(
process.execPath,
[
"--expose-gc",
"--import",
"file://" + FEATURES_MODULE_PATH,
"--import",
"file://" + ASSERT_MODULE_PATH,
"--import",
"file://" + LOAD_ADDON_MODULE_PATH,
"--import",
"file://" + GC_MODULE_PATH,
"--import",
"file://" + MUST_CALL_MODULE_PATH,
"--import",
"file://" + CHILD_PROCESS_MODULE_PATH,
filePath,
],
{ cwd: options.cwd || process.cwd() },
);
return {
status: result.status,
signal: result.signal,
stderr: result.stderr?.toString() ?? "",
stdout: result.stdout?.toString() ?? "",
};
};

Object.assign(globalThis, { spawnTest });
29 changes: 16 additions & 13 deletions implementors/node/tests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import path from "node:path";

assert(
typeof import.meta.dirname === "string",
"Expecting a recent Node.js runtime API version"
"Expecting a recent Node.js runtime API version",
);

const ROOT_PATH = path.resolve(import.meta.dirname, "..", "..");
Expand All @@ -14,31 +14,32 @@ const FEATURES_MODULE_PATH = path.join(
ROOT_PATH,
"implementors",
"node",
"features.js"
"features.js",
);
const ASSERT_MODULE_PATH = path.join(
ROOT_PATH,
"implementors",
"node",
"assert.js"
"assert.js",
);
const LOAD_ADDON_MODULE_PATH = path.join(
ROOT_PATH,
"implementors",
"node",
"load-addon.js"
"load-addon.js",
);
const GC_MODULE_PATH = path.join(
const GC_MODULE_PATH = path.join(ROOT_PATH, "implementors", "node", "gc.js");
const MUST_CALL_MODULE_PATH = path.join(
ROOT_PATH,
"implementors",
"node",
"gc.js"
"must-call.js",
);
const MUST_CALL_MODULE_PATH = path.join(
const CHILD_PROCESS_MODULE_PATH = path.join(
ROOT_PATH,
"implementors",
"node",
"must-call.js"
"child_process.js",
);

export function listDirectoryEntries(dir: string) {
Expand All @@ -62,7 +63,7 @@ export function listDirectoryEntries(dir: string) {

export function runFileInSubprocess(
cwd: string,
filePath: string
filePath: string,
): Promise<void> {
return new Promise((resolve, reject) => {
const child = spawn(
Expand All @@ -80,9 +81,11 @@ export function runFileInSubprocess(
"file://" + GC_MODULE_PATH,
"--import",
"file://" + MUST_CALL_MODULE_PATH,
"--import",
"file://" + CHILD_PROCESS_MODULE_PATH,
filePath,
],
{ cwd }
{ cwd },
);

let stderrOutput = "";
Expand Down Expand Up @@ -111,9 +114,9 @@ export function runFileInSubprocess(
new Error(
`Test file ${path.relative(
TESTS_ROOT_PATH,
filePath
)} failed (${reason})${stderrSuffix}`
)
filePath,
)} failed (${reason})${stderrSuffix}`,
),
);
});
});
Expand Down
1 change: 1 addition & 0 deletions tests/js-native-api/test_exception/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
add_node_api_cts_addon(test_exception test_exception.c)
Loading
Loading