summaryrefslogtreecommitdiff
path: root/ext/node/polyfills/testing.ts
diff options
context:
space:
mode:
authorBartek IwaƄczuk <biwanczuk@gmail.com>2023-08-02 01:17:38 +0200
committerGitHub <noreply@github.com>2023-08-02 01:17:38 +0200
commit21f1b2f62b1fac9089c214d9b862fce2359d30fd (patch)
treec18cc3724c0ed18b5cb62915c0afc1f2e1723cdb /ext/node/polyfills/testing.ts
parent00b5fe8ba31240f6e6abf668ebda8ed0c40fb524 (diff)
feat(node): add polyfill for node:test module (#20002)
This commit provides basic polyfill for "node:test" module. Currently only top-level "test" function is polyfilled, all remaining functions from that module throw not implemented errors.
Diffstat (limited to 'ext/node/polyfills/testing.ts')
-rw-r--r--ext/node/polyfills/testing.ts237
1 files changed, 237 insertions, 0 deletions
diff --git a/ext/node/polyfills/testing.ts b/ext/node/polyfills/testing.ts
new file mode 100644
index 000000000..2c112d9fb
--- /dev/null
+++ b/ext/node/polyfills/testing.ts
@@ -0,0 +1,237 @@
+// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
+
+// TODO(petamoriken): enable prefer-primordials for node polyfills
+// deno-lint-ignore-file prefer-primordials
+
+import { notImplemented, warnNotImplemented } from "ext:deno_node/_utils.ts";
+
+export function deferred() {
+ let methods;
+ const promise = new Promise((resolve, reject) => {
+ methods = {
+ async resolve(value) {
+ await value;
+ resolve(value);
+ },
+ // deno-lint-ignore no-explicit-any
+ reject(reason?: any) {
+ reject(reason);
+ },
+ };
+ });
+ return Object.assign(promise, methods);
+}
+
+export function run() {
+ notImplemented("test.run");
+}
+
+function noop() {}
+
+class NodeTestContext {
+ #denoContext: Deno.TestContext;
+
+ constructor(t: Deno.TestContext) {
+ this.#denoContext = t;
+ }
+
+ get signal() {
+ notImplemented("test.TestContext.signal");
+ return null;
+ }
+
+ get name() {
+ notImplemented("test.TestContext.name");
+ return null;
+ }
+
+ diagnostic(message) {
+ console.log("DIAGNOSTIC:", message);
+ }
+
+ get mock() {
+ notImplemented("test.TestContext.mock");
+ return null;
+ }
+
+ runOnly() {
+ notImplemented("test.TestContext.runOnly");
+ return null;
+ }
+
+ skip() {
+ warnNotImplemented("test.TestContext.skip");
+ return null;
+ }
+
+ todo() {
+ warnNotImplemented("test.TestContext.todo");
+ return null;
+ }
+
+ test(name, options, fn) {
+ const prepared = prepareOptions(name, options, fn, {});
+ return this.#denoContext.step({
+ name: prepared.name,
+ fn: prepared.fn,
+ ignore: prepared.options.todo || prepared.options.skip,
+ }).then(() => undefined);
+ }
+
+ before(_fn, _options) {
+ notImplemented("test.TestContext.before");
+ }
+
+ after(_fn, _options) {
+ notImplemented("test.TestContext.after");
+ }
+
+ beforeEach(_fn, _options) {
+ notImplemented("test.TestContext.beforeEach");
+ }
+
+ afterEach(_fn, _options) {
+ notImplemented("test.TestContext.afterEach");
+ }
+}
+
+function prepareOptions(name, options, fn, overrides) {
+ if (typeof name === "function") {
+ fn = name;
+ } else if (name !== null && typeof name === "object") {
+ fn = options;
+ options = name;
+ } else if (typeof options === "function") {
+ fn = options;
+ }
+
+ if (options === null || typeof options !== "object") {
+ options = {};
+ }
+
+ const finalOptions = { ...options, ...overrides };
+ const { concurrency, timeout, signal } = finalOptions;
+
+ if (typeof concurrency !== "undefined") {
+ warnNotImplemented("test.options.concurrency");
+ }
+ if (typeof timeout !== "undefined") {
+ warnNotImplemented("test.options.timeout");
+ }
+ if (typeof signal !== "undefined") {
+ warnNotImplemented("test.options.signal");
+ }
+
+ if (typeof fn !== "function") {
+ fn = noop;
+ }
+
+ if (typeof name !== "string" || name === "") {
+ name = fn.name || "<anonymous>";
+ }
+
+ return { fn, options: finalOptions, name };
+}
+
+function wrapTestFn(fn, promise) {
+ return async function (t) {
+ const nodeTestContext = new NodeTestContext(t);
+ try {
+ await fn(nodeTestContext);
+ } finally {
+ promise.resolve(undefined);
+ }
+ };
+}
+
+function prepareDenoTest(name, options, fn, overrides) {
+ const prepared = prepareOptions(name, options, fn, overrides);
+
+ const promise = deferred();
+
+ const denoTestOptions = {
+ name: prepared.name,
+ fn: wrapTestFn(prepared.fn, promise),
+ only: prepared.options.only,
+ ignore: prepared.options.todo || prepared.options.skip,
+ };
+ Deno.test(denoTestOptions);
+ return promise;
+}
+
+export function test(name, options, fn) {
+ return prepareDenoTest(name, options, fn, {});
+}
+
+test.skip = function skip(name, options, fn) {
+ return prepareDenoTest(name, options, fn, { skip: true });
+};
+
+test.todo = function todo(name, options, fn) {
+ return prepareDenoTest(name, options, fn, { todo: true });
+};
+
+test.only = function only(name, options, fn) {
+ return prepareDenoTest(name, options, fn, { only: true });
+};
+
+export function describe() {
+ notImplemented("test.describe");
+}
+
+export function it() {
+ notImplemented("test.it");
+}
+
+export function before() {
+ notImplemented("test.before");
+}
+
+export function after() {
+ notImplemented("test.after");
+}
+
+export function beforeEach() {
+ notImplemented("test.beforeEach");
+}
+
+export function afterEach() {
+ notImplemented("test.afterEach");
+}
+
+export const mock = {
+ fn: () => {
+ notImplemented("test.mock.fn");
+ },
+ getter: () => {
+ notImplemented("test.mock.getter");
+ },
+ method: () => {
+ notImplemented("test.mock.method");
+ },
+ reset: () => {
+ notImplemented("test.mock.reset");
+ },
+ restoreAll: () => {
+ notImplemented("test.mock.restoreAll");
+ },
+ setter: () => {
+ notImplemented("test.mock.setter");
+ },
+ timers: {
+ enable: () => {
+ notImplemented("test.mock.timers.enable");
+ },
+ reset: () => {
+ notImplemented("test.mock.timers.reset");
+ },
+ tick: () => {
+ notImplemented("test.mock.timers.tick");
+ },
+ runAll: () => {
+ notImplemented("test.mock.timers.runAll");
+ },
+ },
+};
+
+export default test;