mirror of
https://github.com/denoland/deno.git
synced 2025-02-01 12:16:11 -05:00
test: add node compat tests (#17805)
This commit is contained in:
parent
1a7666a6ca
commit
a01af067d7
16 changed files with 896 additions and 4 deletions
|
@ -70,6 +70,8 @@ mod js_unit_tests;
|
|||
mod lint;
|
||||
#[path = "lsp_tests.rs"]
|
||||
mod lsp;
|
||||
#[path = "node_compat_tests.rs"]
|
||||
mod node_compat_tests;
|
||||
#[path = "node_unit_tests.rs"]
|
||||
mod node_unit_tests;
|
||||
#[path = "npm_tests.rs"]
|
||||
|
|
25
cli/tests/integration/node_compat_tests.rs
Normal file
25
cli/tests/integration/node_compat_tests.rs
Normal file
|
@ -0,0 +1,25 @@
|
|||
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
use test_util as util;
|
||||
|
||||
#[test]
|
||||
fn node_compat_tests() {
|
||||
let mut deno = util::deno_cmd()
|
||||
.current_dir(util::root_path())
|
||||
.arg("test")
|
||||
.arg("--unstable")
|
||||
.arg("--import-map")
|
||||
.arg(
|
||||
util::tests_path()
|
||||
.join("node_compat")
|
||||
.join("import_map.json"),
|
||||
)
|
||||
.arg("-A")
|
||||
.arg(util::tests_path().join("node_compat"))
|
||||
.spawn()
|
||||
.expect("failed to spawn script");
|
||||
|
||||
let status = deno.wait().expect("failed to wait for the child process");
|
||||
assert_eq!(Some(0), status.code());
|
||||
assert!(status.success());
|
||||
}
|
54
cli/tests/node_compat/common.ts
Normal file
54
cli/tests/node_compat/common.ts
Normal file
|
@ -0,0 +1,54 @@
|
|||
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
|
||||
import { join } from "std/path/mod.ts";
|
||||
|
||||
/**
|
||||
* The test suite matches the folders inside the `test` folder inside the
|
||||
* node repo
|
||||
*
|
||||
* Each test suite contains a list of files (which can be paths
|
||||
* or a regex to match) that will be pulled from the node repo
|
||||
*/
|
||||
type TestSuites = Record<string, string[]>;
|
||||
|
||||
interface Config {
|
||||
nodeVersion: string;
|
||||
/** Ignored files won't regenerated by the update script */
|
||||
ignore: TestSuites;
|
||||
/**
|
||||
* The files that will be run by the test suite
|
||||
*
|
||||
* The files to be generated with the update script must be listed here as well,
|
||||
* but they won't be regenerated if they are listed in the `ignore` configuration
|
||||
*/
|
||||
tests: TestSuites;
|
||||
windowsIgnore: TestSuites;
|
||||
darwinIgnore: TestSuites;
|
||||
}
|
||||
|
||||
export const config: Config = JSON.parse(
|
||||
await Deno.readTextFile(new URL("./config.json", import.meta.url)),
|
||||
);
|
||||
|
||||
export const ignoreList = Object.entries(config.ignore).reduce(
|
||||
(total: RegExp[], [suite, paths]) => {
|
||||
paths.forEach((path) => total.push(new RegExp(join(suite, path))));
|
||||
return total;
|
||||
},
|
||||
[],
|
||||
);
|
||||
|
||||
export function getPathsFromTestSuites(suites: TestSuites): string[] {
|
||||
const testPaths: string[] = [];
|
||||
for (const [dir, paths] of Object.entries(suites)) {
|
||||
if (
|
||||
["parallel", "internet", "pummel", "sequential", "pseudo-tty"].includes(
|
||||
dir,
|
||||
)
|
||||
) {
|
||||
for (const path of paths) {
|
||||
testPaths.push(join(dir, path));
|
||||
}
|
||||
}
|
||||
}
|
||||
return testPaths;
|
||||
}
|
13
cli/tests/node_compat/config.json
Normal file
13
cli/tests/node_compat/config.json
Normal file
|
@ -0,0 +1,13 @@
|
|||
{
|
||||
"nodeVersion": "18.12.1",
|
||||
"ignore": {},
|
||||
"tests": {
|
||||
"parallel": [
|
||||
"test-assert-fail.js",
|
||||
"test-assert-strict-exists.js",
|
||||
"test-btoa-atob.js"
|
||||
]
|
||||
},
|
||||
"windowsIgnore": {},
|
||||
"darwinIgnore": {}
|
||||
}
|
5
cli/tests/node_compat/import_map.json
Normal file
5
cli/tests/node_compat/import_map.json
Normal file
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"imports": {
|
||||
"std/": "../../../test_util/std/"
|
||||
}
|
||||
}
|
7
cli/tests/node_compat/runner.ts
Normal file
7
cli/tests/node_compat/runner.ts
Normal file
|
@ -0,0 +1,7 @@
|
|||
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
|
||||
import { createRequire } from "node:module";
|
||||
const file = Deno.args[0];
|
||||
if (!file) {
|
||||
throw new Error("No file provided");
|
||||
}
|
||||
createRequire(import.meta.url)(file);
|
115
cli/tests/node_compat/test.ts
Normal file
115
cli/tests/node_compat/test.ts
Normal file
|
@ -0,0 +1,115 @@
|
|||
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
|
||||
import { magenta } from "std/fmt/colors.ts";
|
||||
import { dirname, fromFileUrl, join } from "std/path/mod.ts";
|
||||
import { fail } from "std/testing/asserts.ts";
|
||||
import { config, getPathsFromTestSuites } from "./common.ts";
|
||||
|
||||
// If the test case is invoked like
|
||||
// deno test -A cli/tests/node_compat/test.ts -- <test-names>
|
||||
// Use the test-names as filters
|
||||
const filters = Deno.args;
|
||||
|
||||
/**
|
||||
* This script will run the test files specified in the configuration file
|
||||
*
|
||||
* Each test file will be run independently and wait until completion, if an abnormal
|
||||
* code for the test is reported, the test suite will fail immediately
|
||||
*/
|
||||
|
||||
const toolsPath = dirname(fromFileUrl(import.meta.url));
|
||||
const stdRootUrl = new URL("../../", import.meta.url).href;
|
||||
const testPaths = getPathsFromTestSuites(config.tests);
|
||||
const cwd = new URL(".", import.meta.url);
|
||||
const importMap = "import_map.json";
|
||||
const windowsIgnorePaths = new Set(
|
||||
getPathsFromTestSuites(config.windowsIgnore),
|
||||
);
|
||||
const darwinIgnorePaths = new Set(
|
||||
getPathsFromTestSuites(config.darwinIgnore),
|
||||
);
|
||||
|
||||
const decoder = new TextDecoder();
|
||||
|
||||
for await (const path of testPaths) {
|
||||
// If filter patterns are given and any pattern doesn't match
|
||||
// to the file path, then skip the case
|
||||
if (
|
||||
filters.length > 0 &&
|
||||
filters.every((pattern) => !path.includes(pattern))
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
const ignore =
|
||||
(Deno.build.os === "windows" && windowsIgnorePaths.has(path)) ||
|
||||
(Deno.build.os === "darwin" && darwinIgnorePaths.has(path));
|
||||
Deno.test({
|
||||
name: `Node.js compatibility "${path}"`,
|
||||
ignore,
|
||||
fn: async () => {
|
||||
const testCase = join(toolsPath, "test", path);
|
||||
|
||||
const v8Flags = ["--stack-size=4000"];
|
||||
const testSource = await Deno.readTextFile(testCase);
|
||||
// TODO(kt3k): Parse `Flags` directive correctly
|
||||
if (testSource.includes("Flags: --expose_externalize_string")) {
|
||||
v8Flags.push("--expose-externalize-string");
|
||||
}
|
||||
|
||||
const args = [
|
||||
"run",
|
||||
"-A",
|
||||
"--quiet",
|
||||
"--unstable",
|
||||
"--unsafely-ignore-certificate-errors",
|
||||
"--v8-flags=" + v8Flags.join(),
|
||||
testCase.endsWith(".mjs") ? "--import-map=" + importMap : "runner.ts",
|
||||
testCase,
|
||||
];
|
||||
|
||||
// Pipe stdout in order to output each test result as Deno.test output
|
||||
// That way the tests will respect the `--quiet` option when provided
|
||||
const command = new Deno.Command(Deno.execPath(), {
|
||||
args,
|
||||
env: {
|
||||
DENO_NODE_COMPAT_URL: stdRootUrl,
|
||||
},
|
||||
cwd,
|
||||
});
|
||||
const { code, stdout, stderr } = await command.output();
|
||||
|
||||
if (stdout.length) console.log(decoder.decode(stdout));
|
||||
|
||||
if (code !== 0) {
|
||||
console.log(`Error: "${path}" failed`);
|
||||
console.log(
|
||||
"You can repeat only this test with the command:",
|
||||
magenta(
|
||||
`./target/debug/deno test -A --import-map cli/tests/node_compat/import_map.json cli/tests/node_compat/test.ts -- ${path}`,
|
||||
),
|
||||
);
|
||||
fail(decoder.decode(stderr));
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
function checkConfigTestFilesOrder(testFileLists: Array<string[]>) {
|
||||
for (const testFileList of testFileLists) {
|
||||
const sortedTestList = JSON.parse(JSON.stringify(testFileList));
|
||||
sortedTestList.sort();
|
||||
if (JSON.stringify(testFileList) !== JSON.stringify(sortedTestList)) {
|
||||
throw new Error(
|
||||
`File names in \`config.json\` are not correct order.`,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (filters.length === 0) {
|
||||
Deno.test("checkConfigTestFilesOrder", function () {
|
||||
checkConfigTestFilesOrder([
|
||||
...Object.keys(config.ignore).map((suite) => config.ignore[suite]),
|
||||
...Object.keys(config.tests).map((suite) => config.tests[suite]),
|
||||
]);
|
||||
});
|
||||
}
|
489
cli/tests/node_compat/test/common/index.js
Normal file
489
cli/tests/node_compat/test/common/index.js
Normal file
|
@ -0,0 +1,489 @@
|
|||
// deno-fmt-ignore-file
|
||||
// deno-lint-ignore-file
|
||||
|
||||
// Copyright Joyent and Node contributors. All rights reserved. MIT license.
|
||||
|
||||
/**
|
||||
* This file is meant as a replacement for the original common/index.js
|
||||
*
|
||||
* That file has a lot of node functionality not currently supported, so this is a lite
|
||||
* version of that file, which most tests should be able to use
|
||||
*/
|
||||
'use strict';
|
||||
const assert = require("assert");
|
||||
const path = require("path");
|
||||
const util = require("util");
|
||||
const tmpdir = require("./tmpdir");
|
||||
|
||||
function platformTimeout(ms) {
|
||||
return ms;
|
||||
}
|
||||
|
||||
let localhostIPv4 = null;
|
||||
|
||||
let knownGlobals = [
|
||||
AbortSignal,
|
||||
addEventListener,
|
||||
alert,
|
||||
atob,
|
||||
btoa,
|
||||
Buffer,
|
||||
caches,
|
||||
clearImmediate,
|
||||
close,
|
||||
closed,
|
||||
confirm,
|
||||
console,
|
||||
crypto,
|
||||
Deno,
|
||||
dispatchEvent,
|
||||
fetch,
|
||||
getParent,
|
||||
global,
|
||||
global.clearInterval,
|
||||
global.clearTimeout,
|
||||
global.setInterval,
|
||||
global.setTimeout,
|
||||
localStorage,
|
||||
location,
|
||||
navigator,
|
||||
onload,
|
||||
onunload,
|
||||
process,
|
||||
prompt,
|
||||
queueMicrotask,
|
||||
removeEventListener,
|
||||
reportError,
|
||||
self,
|
||||
sessionStorage,
|
||||
setImmediate,
|
||||
window,
|
||||
];
|
||||
|
||||
if (global.AbortController)
|
||||
knownGlobals.push(global.AbortController);
|
||||
|
||||
if (global.gc) {
|
||||
knownGlobals.push(global.gc);
|
||||
}
|
||||
|
||||
if (global.performance) {
|
||||
knownGlobals.push(global.performance);
|
||||
}
|
||||
if (global.PerformanceMark) {
|
||||
knownGlobals.push(global.PerformanceMark);
|
||||
}
|
||||
if (global.PerformanceMeasure) {
|
||||
knownGlobals.push(global.PerformanceMeasure);
|
||||
}
|
||||
|
||||
if (global.structuredClone) {
|
||||
knownGlobals.push(global.structuredClone);
|
||||
}
|
||||
|
||||
function allowGlobals(...allowlist) {
|
||||
knownGlobals = knownGlobals.concat(allowlist);
|
||||
}
|
||||
|
||||
if (process.env.NODE_TEST_KNOWN_GLOBALS !== '0') {
|
||||
if (process.env.NODE_TEST_KNOWN_GLOBALS) {
|
||||
const knownFromEnv = process.env.NODE_TEST_KNOWN_GLOBALS.split(',');
|
||||
allowGlobals(...knownFromEnv);
|
||||
}
|
||||
|
||||
function leakedGlobals() {
|
||||
const leaked = [];
|
||||
|
||||
for (const val in global) {
|
||||
if (!knownGlobals.includes(global[val])) {
|
||||
leaked.push(val);
|
||||
}
|
||||
}
|
||||
|
||||
return leaked;
|
||||
}
|
||||
|
||||
process.on('exit', function() {
|
||||
const leaked = leakedGlobals();
|
||||
if (leaked.length > 0) {
|
||||
assert.fail(`Unexpected global(s) found: ${leaked.join(', ')}`);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function _expectWarning(name, expected, code) {
|
||||
if (typeof expected === 'string') {
|
||||
expected = [[expected, code]];
|
||||
} else if (!Array.isArray(expected)) {
|
||||
expected = Object.entries(expected).map(([a, b]) => [b, a]);
|
||||
} else if (!(Array.isArray(expected[0]))) {
|
||||
expected = [[expected[0], expected[1]]];
|
||||
}
|
||||
// Deprecation codes are mandatory, everything else is not.
|
||||
if (name === 'DeprecationWarning') {
|
||||
expected.forEach(([_, code]) => assert(code, expected));
|
||||
}
|
||||
return mustCall((warning) => {
|
||||
const [ message, code ] = expected.shift();
|
||||
assert.strictEqual(warning.name, name);
|
||||
if (typeof message === 'string') {
|
||||
assert.strictEqual(warning.message, message);
|
||||
} else {
|
||||
assert.match(warning.message, message);
|
||||
}
|
||||
assert.strictEqual(warning.code, code);
|
||||
}, expected.length);
|
||||
}
|
||||
|
||||
let catchWarning;
|
||||
|
||||
// Accepts a warning name and description or array of descriptions or a map of
|
||||
// warning names to description(s) ensures a warning is generated for each
|
||||
// name/description pair.
|
||||
// The expected messages have to be unique per `expectWarning()` call.
|
||||
function expectWarning(nameOrMap, expected, code) {
|
||||
if (catchWarning === undefined) {
|
||||
catchWarning = {};
|
||||
process.on('warning', (warning) => {
|
||||
if (!catchWarning[warning.name]) {
|
||||
throw new TypeError(
|
||||
`"${warning.name}" was triggered without being expected.\n` +
|
||||
util.inspect(warning)
|
||||
);
|
||||
}
|
||||
catchWarning[warning.name](warning);
|
||||
});
|
||||
}
|
||||
if (typeof nameOrMap === 'string') {
|
||||
catchWarning[nameOrMap] = _expectWarning(nameOrMap, expected, code);
|
||||
} else {
|
||||
Object.keys(nameOrMap).forEach((name) => {
|
||||
catchWarning[name] = _expectWarning(name, nameOrMap[name]);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Useful for testing expected internal/error objects
|
||||
*
|
||||
* @param {Error} error
|
||||
*/
|
||||
function expectsError(validator, exact) {
|
||||
/**
|
||||
* @param {Error} error
|
||||
*/
|
||||
return mustCall((...args) => {
|
||||
if (args.length !== 1) {
|
||||
// Do not use `assert.strictEqual()` to prevent `inspect` from
|
||||
// always being called.
|
||||
assert.fail(`Expected one argument, got ${util.inspect(args)}`);
|
||||
}
|
||||
const error = args.pop();
|
||||
const descriptor = Object.getOwnPropertyDescriptor(error, 'message');
|
||||
// The error message should be non-enumerable
|
||||
assert.strictEqual(descriptor.enumerable, false);
|
||||
|
||||
assert.throws(() => { throw error; }, validator);
|
||||
return true;
|
||||
}, exact);
|
||||
}
|
||||
|
||||
const noop = () => {};
|
||||
|
||||
/**
|
||||
* @param {Function} fn
|
||||
* @param {number} exact
|
||||
*/
|
||||
function mustCall(fn, exact) {
|
||||
return _mustCallInner(fn, exact, "exact");
|
||||
}
|
||||
|
||||
function mustCallAtLeast(fn, minimum) {
|
||||
return _mustCallInner(fn, minimum, 'minimum');
|
||||
}
|
||||
|
||||
function mustSucceed(fn, exact) {
|
||||
return mustCall(function(err, ...args) {
|
||||
assert.ifError(err);
|
||||
if (typeof fn === 'function')
|
||||
return fn.apply(this, args);
|
||||
}, exact);
|
||||
}
|
||||
|
||||
const mustCallChecks = [];
|
||||
/**
|
||||
* @param {number} exitCode
|
||||
*/
|
||||
function runCallChecks(exitCode) {
|
||||
if (exitCode !== 0) return;
|
||||
|
||||
const failed = mustCallChecks.filter(function (context) {
|
||||
if ("minimum" in context) {
|
||||
context.messageSegment = `at least ${context.minimum}`;
|
||||
return context.actual < context.minimum;
|
||||
}
|
||||
context.messageSegment = `exactly ${context.exact}`;
|
||||
return context.actual !== context.exact;
|
||||
});
|
||||
|
||||
failed.forEach(function (context) {
|
||||
console.log(
|
||||
"Mismatched %s function calls. Expected %s, actual %d.",
|
||||
context.name,
|
||||
context.messageSegment,
|
||||
context.actual,
|
||||
);
|
||||
console.log(context.stack.split("\n").slice(2).join("\n"));
|
||||
});
|
||||
|
||||
if (failed.length) process.exit(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Function} fn
|
||||
* @param {"exact" | "minimum"} field
|
||||
*/
|
||||
function _mustCallInner(fn, criteria = 1, field) {
|
||||
// @ts-ignore
|
||||
if (process._exiting) {
|
||||
throw new Error("Cannot use common.mustCall*() in process exit handler");
|
||||
}
|
||||
if (typeof fn === "number") {
|
||||
criteria = fn;
|
||||
fn = noop;
|
||||
} else if (fn === undefined) {
|
||||
fn = noop;
|
||||
}
|
||||
|
||||
if (typeof criteria !== "number") {
|
||||
throw new TypeError(`Invalid ${field} value: ${criteria}`);
|
||||
}
|
||||
|
||||
let context;
|
||||
if (field === "exact") {
|
||||
context = {
|
||||
exact: criteria,
|
||||
actual: 0,
|
||||
stack: util.inspect(new Error()),
|
||||
name: fn.name || "<anonymous>",
|
||||
};
|
||||
} else {
|
||||
context = {
|
||||
minimum: criteria,
|
||||
actual: 0,
|
||||
stack: util.inspect(new Error()),
|
||||
name: fn.name || "<anonymous>",
|
||||
};
|
||||
}
|
||||
|
||||
// Add the exit listener only once to avoid listener leak warnings
|
||||
if (mustCallChecks.length === 0) process.on("exit", runCallChecks);
|
||||
|
||||
mustCallChecks.push(context);
|
||||
|
||||
return function () {
|
||||
context.actual++;
|
||||
return fn.apply(this, arguments);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string=} msg
|
||||
*/
|
||||
function mustNotCall(msg) {
|
||||
/**
|
||||
* @param {any[]} args
|
||||
*/
|
||||
return function mustNotCall(...args) {
|
||||
const argsInfo = args.length > 0
|
||||
? `\ncalled with arguments: ${args.map(util.inspect).join(", ")}`
|
||||
: "";
|
||||
assert.fail(
|
||||
`${msg || "function should not have been called"} at unknown` +
|
||||
argsInfo,
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
const _mustNotMutateObjectDeepProxies = new WeakMap();
|
||||
|
||||
function mustNotMutateObjectDeep(original) {
|
||||
// Return primitives and functions directly. Primitives are immutable, and
|
||||
// proxied functions are impossible to compare against originals, e.g. with
|
||||
// `assert.deepEqual()`.
|
||||
if (original === null || typeof original !== 'object') {
|
||||
return original;
|
||||
}
|
||||
|
||||
const cachedProxy = _mustNotMutateObjectDeepProxies.get(original);
|
||||
if (cachedProxy) {
|
||||
return cachedProxy;
|
||||
}
|
||||
|
||||
const _mustNotMutateObjectDeepHandler = {
|
||||
__proto__: null,
|
||||
defineProperty(target, property, descriptor) {
|
||||
assert.fail(`Expected no side effects, got ${inspect(property)} ` +
|
||||
'defined');
|
||||
},
|
||||
deleteProperty(target, property) {
|
||||
assert.fail(`Expected no side effects, got ${inspect(property)} ` +
|
||||
'deleted');
|
||||
},
|
||||
get(target, prop, receiver) {
|
||||
return mustNotMutateObjectDeep(Reflect.get(target, prop, receiver));
|
||||
},
|
||||
preventExtensions(target) {
|
||||
assert.fail('Expected no side effects, got extensions prevented on ' +
|
||||
inspect(target));
|
||||
},
|
||||
set(target, property, value, receiver) {
|
||||
assert.fail(`Expected no side effects, got ${inspect(value)} ` +
|
||||
`assigned to ${inspect(property)}`);
|
||||
},
|
||||
setPrototypeOf(target, prototype) {
|
||||
assert.fail(`Expected no side effects, got set prototype to ${prototype}`);
|
||||
}
|
||||
};
|
||||
|
||||
const proxy = new Proxy(original, _mustNotMutateObjectDeepHandler);
|
||||
_mustNotMutateObjectDeepProxies.set(original, proxy);
|
||||
return proxy;
|
||||
}
|
||||
|
||||
// A helper function to simplify checking for ERR_INVALID_ARG_TYPE output.
|
||||
function invalidArgTypeHelper(input) {
|
||||
if (input == null) {
|
||||
return ` Received ${input}`;
|
||||
}
|
||||
if (typeof input === "function" && input.name) {
|
||||
return ` Received function ${input.name}`;
|
||||
}
|
||||
if (typeof input === "object") {
|
||||
if (input.constructor && input.constructor.name) {
|
||||
return ` Received an instance of ${input.constructor.name}`;
|
||||
}
|
||||
return ` Received ${util.inspect(input, { depth: -1 })}`;
|
||||
}
|
||||
let inspected = util.inspect(input, { colors: false });
|
||||
if (inspected.length > 25) {
|
||||
inspected = `${inspected.slice(0, 25)}...`;
|
||||
}
|
||||
return ` Received type ${typeof input} (${inspected})`;
|
||||
}
|
||||
|
||||
const isWindows = process.platform === 'win32';
|
||||
const isAIX = process.platform === 'aix';
|
||||
const isSunOS = process.platform === 'sunos';
|
||||
const isFreeBSD = process.platform === 'freebsd';
|
||||
const isOpenBSD = process.platform === 'openbsd';
|
||||
const isLinux = process.platform === 'linux';
|
||||
const isOSX = process.platform === 'darwin';
|
||||
|
||||
const isDumbTerminal = process.env.TERM === 'dumb';
|
||||
|
||||
function skipIfDumbTerminal() {
|
||||
if (isDumbTerminal) {
|
||||
skip('skipping - dumb terminal');
|
||||
}
|
||||
}
|
||||
|
||||
function printSkipMessage(msg) {
|
||||
console.log(`1..0 # Skipped: ${msg}`);
|
||||
}
|
||||
|
||||
function skip(msg) {
|
||||
printSkipMessage(msg);
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
const PIPE = (() => {
|
||||
const localRelative = path.relative(process.cwd(), `${tmpdir.path}/`);
|
||||
const pipePrefix = isWindows ? "\\\\.\\pipe\\" : localRelative;
|
||||
const pipeName = `node-test.${process.pid}.sock`;
|
||||
return path.join(pipePrefix, pipeName);
|
||||
})();
|
||||
|
||||
function getArrayBufferViews(buf) {
|
||||
const { buffer, byteOffset, byteLength } = buf;
|
||||
|
||||
const out = [];
|
||||
|
||||
const arrayBufferViews = [
|
||||
Int8Array,
|
||||
Uint8Array,
|
||||
Uint8ClampedArray,
|
||||
Int16Array,
|
||||
Uint16Array,
|
||||
Int32Array,
|
||||
Uint32Array,
|
||||
Float32Array,
|
||||
Float64Array,
|
||||
DataView,
|
||||
];
|
||||
|
||||
for (const type of arrayBufferViews) {
|
||||
const { BYTES_PER_ELEMENT = 1 } = type;
|
||||
if (byteLength % BYTES_PER_ELEMENT === 0) {
|
||||
out.push(new type(buffer, byteOffset, byteLength / BYTES_PER_ELEMENT));
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
function getBufferSources(buf) {
|
||||
return [...getArrayBufferViews(buf), new Uint8Array(buf).buffer];
|
||||
}
|
||||
|
||||
const pwdCommand = isWindows ?
|
||||
['cmd.exe', ['/d', '/c', 'cd']] :
|
||||
['pwd', []];
|
||||
|
||||
module.exports = {
|
||||
allowGlobals,
|
||||
expectsError,
|
||||
expectWarning,
|
||||
getArrayBufferViews,
|
||||
getBufferSources,
|
||||
hasCrypto: true,
|
||||
invalidArgTypeHelper,
|
||||
mustCall,
|
||||
mustCallAtLeast,
|
||||
mustNotCall,
|
||||
mustNotMutateObjectDeep,
|
||||
mustSucceed,
|
||||
PIPE,
|
||||
platformTimeout,
|
||||
printSkipMessage,
|
||||
pwdCommand,
|
||||
skipIfDumbTerminal,
|
||||
isDumbTerminal,
|
||||
isWindows,
|
||||
isAIX,
|
||||
isSunOS,
|
||||
isFreeBSD,
|
||||
isOpenBSD,
|
||||
isLinux,
|
||||
isOSX,
|
||||
isMainThread: true, // TODO(f3n67u): replace with `worker_thread.isMainThread` when `worker_thread` implemented
|
||||
skip,
|
||||
get hasIPv6() {
|
||||
const iFaces = require('os').networkInterfaces();
|
||||
const re = isWindows ? /Loopback Pseudo-Interface/ : /lo/;
|
||||
return Object.keys(iFaces).some((name) => {
|
||||
return re.test(name) &&
|
||||
iFaces[name].some(({ family }) => family === 'IPv6');
|
||||
});
|
||||
},
|
||||
|
||||
get localhostIPv4() {
|
||||
if (localhostIPv4 !== null) return localhostIPv4;
|
||||
if (localhostIPv4 === null) localhostIPv4 = '127.0.0.1';
|
||||
|
||||
return localhostIPv4;
|
||||
},
|
||||
|
||||
get PORT() {
|
||||
return 12346;
|
||||
},
|
||||
};
|
1
cli/tests/node_compat/test/common/package.json
Normal file
1
cli/tests/node_compat/test/common/package.json
Normal file
|
@ -0,0 +1 @@
|
|||
{}
|
69
cli/tests/node_compat/test/common/tmpdir.js
Normal file
69
cli/tests/node_compat/test/common/tmpdir.js
Normal file
|
@ -0,0 +1,69 @@
|
|||
// deno-fmt-ignore-file
|
||||
// deno-lint-ignore-file
|
||||
|
||||
// Copyright Joyent and Node contributors. All rights reserved. MIT license.
|
||||
// Taken from Node 16.13.0
|
||||
// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually
|
||||
|
||||
'use strict';
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
// const { isMainThread } = require('worker_threads');
|
||||
|
||||
function rmSync(pathname) {
|
||||
fs.rmSync(pathname, { maxRetries: 3, recursive: true, force: true });
|
||||
}
|
||||
|
||||
const testRoot = process.env.NODE_TEST_DIR ?
|
||||
fs.realpathSync(process.env.NODE_TEST_DIR) : path.resolve(__dirname, '..');
|
||||
|
||||
// Using a `.` prefixed name, which is the convention for "hidden" on POSIX,
|
||||
// gets tools to ignore it by default or by simple rules, especially eslint.
|
||||
const tmpdirName = '.tmp.' +
|
||||
(process.env.TEST_SERIAL_ID || process.env.TEST_THREAD_ID || '0');
|
||||
const tmpPath = path.join(testRoot, tmpdirName);
|
||||
|
||||
let firstRefresh = true;
|
||||
function refresh() {
|
||||
rmSync(this.path);
|
||||
fs.mkdirSync(this.path);
|
||||
|
||||
if (firstRefresh) {
|
||||
firstRefresh = false;
|
||||
// Clean only when a test uses refresh. This allows for child processes to
|
||||
// use the tmpdir and only the parent will clean on exit.
|
||||
process.on('exit', onexit);
|
||||
}
|
||||
}
|
||||
|
||||
function onexit() {
|
||||
// Change directory to avoid possible EBUSY
|
||||
// TODO(f3n67u): uncomment when `worker_thread.isMainThread` implemented
|
||||
// if (isMainThread)
|
||||
// process.chdir(testRoot);
|
||||
|
||||
try {
|
||||
rmSync(tmpPath);
|
||||
} catch (e) {
|
||||
console.error('Can\'t clean tmpdir:', tmpPath);
|
||||
|
||||
const files = fs.readdirSync(tmpPath);
|
||||
console.error('Files blocking:', files);
|
||||
|
||||
if (files.some((f) => f.startsWith('.nfs'))) {
|
||||
// Warn about NFS "silly rename"
|
||||
console.error('Note: ".nfs*" might be files that were open and ' +
|
||||
'unlinked but not closed.');
|
||||
console.error('See http://nfs.sourceforge.net/#faq_d2 for details.');
|
||||
}
|
||||
|
||||
console.error();
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
path: tmpPath,
|
||||
refresh
|
||||
};
|
1
cli/tests/node_compat/test/parallel/package.json
Normal file
1
cli/tests/node_compat/test/parallel/package.json
Normal file
|
@ -0,0 +1 @@
|
|||
{}
|
51
cli/tests/node_compat/test/parallel/test-assert-fail.js
Normal file
51
cli/tests/node_compat/test/parallel/test-assert-fail.js
Normal file
|
@ -0,0 +1,51 @@
|
|||
// deno-fmt-ignore-file
|
||||
// deno-lint-ignore-file
|
||||
|
||||
// Copyright Joyent and Node contributors. All rights reserved. MIT license.
|
||||
// Taken from Node 18.12.1
|
||||
// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually
|
||||
|
||||
'use strict';
|
||||
|
||||
const common = require('../common');
|
||||
const assert = require('assert');
|
||||
|
||||
// No args
|
||||
assert.throws(
|
||||
() => { assert.fail(); },
|
||||
{
|
||||
code: 'ERR_ASSERTION',
|
||||
name: 'AssertionError',
|
||||
message: 'Failed',
|
||||
operator: 'fail',
|
||||
actual: undefined,
|
||||
expected: undefined,
|
||||
generatedMessage: true,
|
||||
stack: /Failed/
|
||||
}
|
||||
);
|
||||
|
||||
// One arg = message
|
||||
assert.throws(() => {
|
||||
assert.fail('custom message');
|
||||
}, {
|
||||
code: 'ERR_ASSERTION',
|
||||
name: 'AssertionError',
|
||||
message: 'custom message',
|
||||
operator: 'fail',
|
||||
actual: undefined,
|
||||
expected: undefined,
|
||||
generatedMessage: false
|
||||
});
|
||||
|
||||
// One arg = Error
|
||||
assert.throws(() => {
|
||||
assert.fail(new TypeError('custom message'));
|
||||
}, {
|
||||
name: 'TypeError',
|
||||
message: 'custom message'
|
||||
});
|
||||
|
||||
Object.prototype.get = common.mustNotCall();
|
||||
assert.throws(() => assert.fail(''), { code: 'ERR_ASSERTION' });
|
||||
delete Object.prototype.get;
|
|
@ -0,0 +1,13 @@
|
|||
// deno-fmt-ignore-file
|
||||
// deno-lint-ignore-file
|
||||
|
||||
// Copyright Joyent and Node contributors. All rights reserved. MIT license.
|
||||
// Taken from Node 18.12.1
|
||||
// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually
|
||||
|
||||
'use strict';
|
||||
|
||||
require('../common');
|
||||
const assert = require('assert');
|
||||
|
||||
assert.strictEqual(require('assert/strict'), assert.strict);
|
46
cli/tests/node_compat/test/parallel/test-btoa-atob.js
Normal file
46
cli/tests/node_compat/test/parallel/test-btoa-atob.js
Normal file
|
@ -0,0 +1,46 @@
|
|||
// deno-fmt-ignore-file
|
||||
// deno-lint-ignore-file
|
||||
|
||||
// Copyright Joyent and Node contributors. All rights reserved. MIT license.
|
||||
// Taken from Node 18.12.1
|
||||
// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually
|
||||
|
||||
'use strict';
|
||||
|
||||
require('../common');
|
||||
|
||||
const { strictEqual, throws } = require('assert');
|
||||
const buffer = require('buffer');
|
||||
|
||||
// Exported on the global object
|
||||
strictEqual(globalThis.atob, buffer.atob);
|
||||
strictEqual(globalThis.btoa, buffer.btoa);
|
||||
|
||||
// Throws type error on no argument passed
|
||||
throws(() => buffer.atob(), /TypeError/);
|
||||
throws(() => buffer.btoa(), /TypeError/);
|
||||
|
||||
strictEqual(atob(' '), '');
|
||||
strictEqual(atob(' Y\fW\tJ\njZ A=\r= '), 'abcd');
|
||||
|
||||
strictEqual(atob(null), '\x9Eée');
|
||||
strictEqual(atob(NaN), '5£');
|
||||
strictEqual(atob(Infinity), '"wâ\x9E+r');
|
||||
strictEqual(atob(true), '¶»\x9E');
|
||||
strictEqual(atob(1234), '×mø');
|
||||
strictEqual(atob([]), '');
|
||||
strictEqual(atob({ toString: () => '' }), '');
|
||||
strictEqual(atob({ [Symbol.toPrimitive]: () => '' }), '');
|
||||
|
||||
throws(() => atob(Symbol()), /TypeError/);
|
||||
[
|
||||
undefined, false, () => {}, {}, [1],
|
||||
0, 1, 0n, 1n, -Infinity,
|
||||
'a', 'a\n\n\n', '\ra\r\r', ' a ', '\t\t\ta', 'a\f\f\f', '\ta\r \n\f',
|
||||
].forEach((value) =>
|
||||
// See #2 - https://html.spec.whatwg.org/multipage/webappapis.html#dom-atob
|
||||
throws(() => atob(value), {
|
||||
constructor: DOMException,
|
||||
name: 'InvalidCharacterError',
|
||||
code: 5,
|
||||
}));
|
|
@ -20,6 +20,10 @@ import { normalizeEncoding } from "internal:deno_node/polyfills/internal/util.mj
|
|||
import { validateBuffer } from "internal:deno_node/polyfills/internal/validators.mjs";
|
||||
import { isUint8Array } from "internal:deno_node/polyfills/internal/util/types.ts";
|
||||
import { forgivingBase64Encode, forgivingBase64UrlEncode } from "internal:deno_web/00_infra.js";
|
||||
import { atob, btoa } from "internal:deno_web/05_base64.js";
|
||||
import { Blob } from "internal:deno_web/09_file.js";
|
||||
|
||||
export { atob, btoa, Blob };
|
||||
|
||||
const utf8Encoder = new TextEncoder();
|
||||
|
||||
|
@ -1802,10 +1806,6 @@ function BufferBigIntNotDefined() {
|
|||
throw new Error("BigInt not supported");
|
||||
}
|
||||
|
||||
export const atob = globalThis.atob;
|
||||
export const Blob = globalThis.Blob;
|
||||
export const btoa = globalThis.btoa;
|
||||
|
||||
export function readUInt48LE(buf, offset = 0) {
|
||||
validateNumber(offset, "offset");
|
||||
const first = buf[offset];
|
||||
|
|
|
@ -30,6 +30,7 @@ export async function checkCopyright() {
|
|||
":!:test_util/wpt/**",
|
||||
":!:cli/tools/init/templates/**",
|
||||
":!:cli/tests/unit_node/testdata/**",
|
||||
":!:cli/tests/node_compat/test/**",
|
||||
|
||||
// rust
|
||||
"*.rs",
|
||||
|
|
Loading…
Add table
Reference in a new issue