summaryrefslogtreecommitdiff
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
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.
-rw-r--r--cli/tests/integration/node_compat_tests.rs6
-rw-r--r--cli/tests/testdata/node/test.js383
-rw-r--r--cli/tests/testdata/node/test.out176
-rw-r--r--ext/node/lib.rs1
-rw-r--r--ext/node/polyfill.rs1
-rw-r--r--ext/node/polyfills/01_require.js2
-rw-r--r--ext/node/polyfills/testing.ts237
7 files changed, 806 insertions, 0 deletions
diff --git a/cli/tests/integration/node_compat_tests.rs b/cli/tests/integration/node_compat_tests.rs
index b5fd7b1b3..29d70708e 100644
--- a/cli/tests/integration/node_compat_tests.rs
+++ b/cli/tests/integration/node_compat_tests.rs
@@ -17,3 +17,9 @@ fn node_compat_tests() {
assert_eq!(Some(0), status.code());
assert!(status.success());
}
+
+itest!(node_test_module {
+ args: "test node/test.js",
+ output: "node/test.out",
+ exit_code: 1,
+});
diff --git a/cli/tests/testdata/node/test.js b/cli/tests/testdata/node/test.js
new file mode 100644
index 000000000..9bb5aa885
--- /dev/null
+++ b/cli/tests/testdata/node/test.js
@@ -0,0 +1,383 @@
+// Copyright Joyent, Inc. and other Node contributors.
+
+// Ported from https://github.com/nodejs/node/blob/d396a041f71cc055ad60b0abc63ad81c0ee6a574/test/fixtures/test-runner/output/output.js
+
+// deno-lint-ignore-file
+
+import assert from "node:assert";
+import test from "node:test";
+import util from "node:util";
+import { setImmediate } from "node:timers";
+
+test("sync pass todo", (t) => {
+ t.todo();
+});
+
+test("sync pass todo with message", (t) => {
+ t.todo("this is a passing todo");
+});
+
+test("sync fail todo", (t) => {
+ t.todo();
+ throw new Error("thrown from sync fail todo");
+});
+
+test("sync fail todo with message", (t) => {
+ t.todo("this is a failing todo");
+ throw new Error("thrown from sync fail todo with message");
+});
+
+test("sync skip pass", (t) => {
+ t.skip();
+});
+
+test("sync skip pass with message", (t) => {
+ t.skip("this is skipped");
+});
+
+test("sync pass", (t) => {
+ t.diagnostic("this test should pass");
+});
+
+test("sync throw fail", () => {
+ throw new Error("thrown from sync throw fail");
+});
+
+test("async skip pass", async (t) => {
+ t.skip();
+});
+
+test("async pass", async () => {
+});
+
+test("async throw fail", async () => {
+ throw new Error("thrown from async throw fail");
+});
+
+test("async skip fail", async (t) => {
+ t.skip();
+ throw new Error("thrown from async throw fail");
+});
+
+test("async assertion fail", async () => {
+ // Make sure the assert module is handled.
+ assert.strictEqual(true, false);
+});
+
+test("resolve pass", () => {
+ return Promise.resolve();
+});
+
+test("reject fail", () => {
+ return Promise.reject(new Error("rejected from reject fail"));
+});
+
+test("unhandled rejection - passes but warns", () => {
+ Promise.reject(new Error("rejected from unhandled rejection fail"));
+});
+
+test("async unhandled rejection - passes but warns", async () => {
+ Promise.reject(new Error("rejected from async unhandled rejection fail"));
+});
+
+test("immediate throw - passes but warns", () => {
+ setImmediate(() => {
+ throw new Error("thrown from immediate throw fail");
+ });
+});
+
+test("immediate reject - passes but warns", () => {
+ setImmediate(() => {
+ Promise.reject(new Error("rejected from immediate reject fail"));
+ });
+});
+
+test("immediate resolve pass", () => {
+ return new Promise((resolve) => {
+ setImmediate(() => {
+ resolve();
+ });
+ });
+});
+
+test("subtest sync throw fail", async (t) => {
+ await t.test("+sync throw fail", (t) => {
+ t.diagnostic("this subtest should make its parent test fail");
+ throw new Error("thrown from subtest sync throw fail");
+ });
+});
+
+test("sync throw non-error fail", async (t) => {
+ throw Symbol("thrown symbol from sync throw non-error fail");
+});
+
+test("level 0a", { concurrency: 4 }, async (t) => {
+ t.test("level 1a", async (t) => {
+ const p1a = new Promise((resolve) => {
+ setTimeout(() => {
+ resolve();
+ }, 100);
+ });
+
+ return p1a;
+ });
+
+ test("level 1b", async (t) => {
+ const p1b = new Promise((resolve) => {
+ resolve();
+ });
+
+ return p1b;
+ });
+
+ t.test("level 1c", async (t) => {
+ const p1c = new Promise((resolve) => {
+ setTimeout(() => {
+ resolve();
+ }, 200);
+ });
+
+ return p1c;
+ });
+
+ t.test("level 1d", async (t) => {
+ const p1c = new Promise((resolve) => {
+ setTimeout(() => {
+ resolve();
+ }, 150);
+ });
+
+ return p1c;
+ });
+
+ const p0a = new Promise((resolve) => {
+ setTimeout(() => {
+ resolve();
+ }, 300);
+ });
+
+ return p0a;
+});
+
+test("top level", { concurrency: 2 }, async (t) => {
+ t.test("+long running", async (t) => {
+ return new Promise((resolve, reject) => {
+ setTimeout(resolve, 300).unref();
+ });
+ });
+
+ t.test("+short running", async (t) => {
+ t.test("++short running", async (t) => {});
+ });
+});
+
+test("invalid subtest - pass but subtest fails", (t) => {
+ setImmediate(() => {
+ t.test("invalid subtest fail", () => {
+ throw new Error("this should not be thrown");
+ });
+ });
+});
+
+test("sync skip option", { skip: true }, (t) => {
+ throw new Error("this should not be executed");
+});
+
+test("sync skip option with message", { skip: "this is skipped" }, (t) => {
+ throw new Error("this should not be executed");
+});
+
+test("sync skip option is false fail", { skip: false }, (t) => {
+ throw new Error("this should be executed");
+});
+
+// A test with no arguments provided.
+test();
+
+// A test with only a named function provided.
+test(function functionOnly() {});
+
+// A test with only an anonymous function provided.
+test(() => {});
+
+// A test with only a name provided.
+test("test with only a name provided");
+
+// A test with an empty string name.
+test("");
+
+// A test with only options provided.
+test({ skip: true });
+
+// A test with only a name and options provided.
+test("test with a name and options provided", { skip: true });
+
+// A test with only options and a function provided.
+test({ skip: true }, function functionAndOptions() {});
+
+// A test whose description needs to be escaped.
+// test("escaped description \\ # \\#\\ \n \t \f \v \b \r");
+
+// A test whose skip message needs to be escaped.
+test("escaped skip message", { skip: "#skip" });
+
+// A test whose todo message needs to be escaped.
+test("escaped todo message", { todo: "#todo" });
+
+// A test with a diagnostic message that needs to be escaped.
+test("escaped diagnostic", (t) => {
+ t.diagnostic("#diagnostic");
+});
+
+test("callback pass", (t, done) => {
+ setImmediate(done);
+});
+
+test("callback fail", (t, done) => {
+ setImmediate(() => {
+ done(new Error("callback failure"));
+ });
+});
+
+test("sync t is this in test", function (t) {
+ assert.strictEqual(this, t);
+});
+
+test("async t is this in test", async function (t) {
+ assert.strictEqual(this, t);
+});
+
+test("callback t is this in test", function (t, done) {
+ assert.strictEqual(this, t);
+ done();
+});
+
+test("callback also returns a Promise", async (t, done) => {
+ throw new Error("thrown from callback also returns a Promise");
+});
+
+test("callback throw", (t, done) => {
+ throw new Error("thrown from callback throw");
+});
+
+test("callback called twice", (t, done) => {
+ done();
+ done();
+});
+
+test("callback called twice in different ticks", (t, done) => {
+ setImmediate(done);
+ done();
+});
+
+test("callback called twice in future tick", (t, done) => {
+ setImmediate(() => {
+ done();
+ done();
+ });
+});
+
+test("callback async throw", (t, done) => {
+ setImmediate(() => {
+ throw new Error("thrown from callback async throw");
+ });
+});
+
+test("callback async throw after done", (t, done) => {
+ setImmediate(() => {
+ throw new Error("thrown from callback async throw after done");
+ });
+
+ done();
+});
+
+test("custom inspect symbol fail", () => {
+ const obj = {
+ [util.inspect.custom]() {
+ return "customized";
+ },
+ foo: 1,
+ };
+
+ throw obj;
+});
+
+test("custom inspect symbol that throws fail", () => {
+ const obj = {
+ [util.inspect.custom]() {
+ throw new Error("bad-inspect");
+ },
+ foo: 1,
+ };
+
+ throw obj;
+});
+
+test("subtest sync throw fails", async (t) => {
+ await t.test("sync throw fails at first", (t) => {
+ throw new Error("thrown from subtest sync throw fails at first");
+ });
+ await t.test("sync throw fails at second", (t) => {
+ throw new Error("thrown from subtest sync throw fails at second");
+ });
+});
+
+test("timed out async test", { timeout: 5 }, async (t) => {
+ return new Promise((resolve) => {
+ setTimeout(resolve, 100);
+ });
+});
+
+test("timed out callback test", { timeout: 5 }, (t, done) => {
+ setTimeout(done, 100);
+});
+
+test("large timeout async test is ok", { timeout: 30_000_000 }, async (t) => {
+ return new Promise((resolve) => {
+ setTimeout(resolve, 10);
+ });
+});
+
+test(
+ "large timeout callback test is ok",
+ { timeout: 30_000_000 },
+ (t, done) => {
+ setTimeout(done, 10);
+ },
+);
+
+test("successful thenable", () => {
+ let thenCalled = false;
+ return {
+ get then() {
+ if (thenCalled) throw new Error();
+ thenCalled = true;
+ return (successHandler) => successHandler();
+ },
+ };
+});
+
+test("rejected thenable", () => {
+ let thenCalled = false;
+ return {
+ get then() {
+ if (thenCalled) throw new Error();
+ thenCalled = true;
+ return (_, errorHandler) => errorHandler("custom error");
+ },
+ };
+});
+
+test("unfinished test with uncaughtException", async () => {
+ await new Promise(() => {
+ setTimeout(() => {
+ throw new Error("foo");
+ });
+ });
+});
+
+test("unfinished test with unhandledRejection", async () => {
+ await new Promise(() => {
+ setTimeout(() => Promise.reject(new Error("bar")));
+ });
+});
diff --git a/cli/tests/testdata/node/test.out b/cli/tests/testdata/node/test.out
new file mode 100644
index 000000000..afda0eca3
--- /dev/null
+++ b/cli/tests/testdata/node/test.out
@@ -0,0 +1,176 @@
+[WILDCARD]
+Warning: Not implemented: test.options.concurrency
+Warning: Not implemented: test.options.concurrency
+Warning: Not implemented: test.options.timeout
+Warning: Not implemented: test.options.timeout
+Warning: Not implemented: test.options.timeout
+Warning: Not implemented: test.options.timeout
+running 62 tests from ./node/test.js
+sync pass todo ...
+------- output -------
+Warning: Not implemented: test.TestContext.todo
+----- output end -----
+sync pass todo ... ok [WILDCARD]
+sync pass todo with message ...
+------- output -------
+Warning: Not implemented: test.TestContext.todo
+----- output end -----
+sync pass todo with message ... ok [WILDCARD]
+sync fail todo ...
+------- output -------
+Warning: Not implemented: test.TestContext.todo
+----- output end -----
+sync fail todo ... FAILED [WILDCARD]
+sync fail todo with message ...
+------- output -------
+Warning: Not implemented: test.TestContext.todo
+----- output end -----
+sync fail todo with message ... FAILED [WILDCARD]
+sync skip pass ...
+------- output -------
+Warning: Not implemented: test.TestContext.skip
+----- output end -----
+sync skip pass ... ok [WILDCARD]
+sync skip pass with message ...
+------- output -------
+Warning: Not implemented: test.TestContext.skip
+----- output end -----
+sync skip pass with message ... ok [WILDCARD]
+sync pass ...
+------- output -------
+DIAGNOSTIC: this test should pass
+----- output end -----
+sync pass ... ok [WILDCARD]
+sync throw fail ... FAILED [WILDCARD]
+async skip pass ...
+------- output -------
+Warning: Not implemented: test.TestContext.skip
+----- output end -----
+async skip pass ... ok [WILDCARD]
+async pass ... ok [WILDCARD]
+async throw fail ... FAILED [WILDCARD]
+async skip fail ...
+------- output -------
+Warning: Not implemented: test.TestContext.skip
+----- output end -----
+async skip fail ... FAILED [WILDCARD]
+async assertion fail ... FAILED [WILDCARD]
+resolve pass ... ok [WILDCARD]
+reject fail ... FAILED [WILDCARD]
+unhandled rejection - passes but warns ...
+Uncaught error from ./node/test.js FAILED
+unhandled rejection - passes but warns ... cancelled ([WILDCARD])
+async unhandled rejection - passes but warns ... cancelled ([WILDCARD])
+immediate throw - passes but warns ... cancelled ([WILDCARD])
+immediate reject - passes but warns ... cancelled ([WILDCARD])
+immediate resolve pass ... cancelled ([WILDCARD])
+subtest sync throw fail ... cancelled ([WILDCARD])
+sync throw non-error fail ... cancelled ([WILDCARD])
+level 0a ... cancelled ([WILDCARD])
+top level ... cancelled ([WILDCARD])
+invalid subtest - pass but subtest fails ... cancelled ([WILDCARD])
+sync skip option ... ignored ([WILDCARD])
+sync skip option with message ... cancelled ([WILDCARD])
+sync skip option is false fail ... cancelled ([WILDCARD])
+noop ... cancelled ([WILDCARD])
+functionOnly ... cancelled ([WILDCARD])
+<anonymous> ... cancelled ([WILDCARD])
+test with only a name provided ... cancelled ([WILDCARD])
+noop ... cancelled ([WILDCARD])
+noop ... ignored ([WILDCARD])
+test with a name and options provided ... ignored ([WILDCARD])
+functionAndOptions ... ignored ([WILDCARD])
+escaped skip message ... cancelled ([WILDCARD])
+escaped todo message ... cancelled ([WILDCARD])
+escaped diagnostic ... cancelled ([WILDCARD])
+callback pass ... cancelled ([WILDCARD])
+callback fail ... cancelled ([WILDCARD])
+sync t is this in test ... cancelled ([WILDCARD])
+async t is this in test ... cancelled ([WILDCARD])
+callback t is this in test ... cancelled ([WILDCARD])
+callback also returns a Promise ... cancelled ([WILDCARD])
+callback throw ... cancelled ([WILDCARD])
+callback called twice ... cancelled ([WILDCARD])
+callback called twice in different ticks ... cancelled ([WILDCARD])
+callback called twice in future tick ... cancelled ([WILDCARD])
+callback async throw ... cancelled ([WILDCARD])
+callback async throw after done ... cancelled ([WILDCARD])
+custom inspect symbol fail ... cancelled ([WILDCARD])
+custom inspect symbol that throws fail ... cancelled ([WILDCARD])
+subtest sync throw fails ... cancelled ([WILDCARD])
+timed out async test ... cancelled ([WILDCARD])
+timed out callback test ... cancelled ([WILDCARD])
+large timeout async test is ok ... cancelled ([WILDCARD])
+large timeout callback test is ok ... cancelled ([WILDCARD])
+successful thenable ... cancelled ([WILDCARD])
+rejected thenable ... cancelled ([WILDCARD])
+unfinished test with uncaughtException ... cancelled ([WILDCARD])
+unfinished test with unhandledRejection ... cancelled ([WILDCARD])
+
+ ERRORS
+
+sync fail todo => node:test:135:10
+error: Error: thrown from sync fail todo
+ throw new Error("thrown from sync fail todo");
+[WILDCARD]
+
+sync fail todo with message => node:test:135:10
+error: Error: thrown from sync fail todo with message
+ throw new Error("thrown from sync fail todo with message");
+[WILDCARD]
+
+sync throw fail => node:test:135:10
+error: Error: thrown from sync throw fail
+ throw new Error("thrown from sync throw fail");
+[WILDCARD]
+
+async throw fail => node:test:135:10
+error: Error: thrown from async throw fail
+ throw new Error("thrown from async throw fail");
+[WILDCARD]
+
+async skip fail => node:test:135:10
+error: Error: thrown from async throw fail
+ throw new Error("thrown from async throw fail");
+[WILDCARD]
+
+async assertion fail => node:test:135:10
+error: AssertionError: Values are not strictly equal:
+
+
+ [Diff] Actual / Expected
+
+
+- true
++ false
+
+ at [WILDCARD]
+
+reject fail => node:test:135:10
+error: Error: rejected from reject fail
+ return Promise.reject(new Error("rejected from reject fail"));
+ ^
+ at [WILDCARD]
+
+./node/test.js (uncaught error)
+error: Error: rejected from unhandled rejection fail
+ Promise.reject(new Error("rejected from unhandled rejection fail"));
+ ^
+ at [WILDCARD]
+This error was not caught from a test and caused the test runner to fail on the referenced module.
+It most likely originated from a dangling promise, event/timeout handler or top-level code.
+
+ FAILURES
+
+sync fail todo => node:test:135:10
+sync fail todo with message => node:test:135:10
+sync throw fail => node:test:135:10
+async throw fail => node:test:135:10
+async skip fail => node:test:135:10
+async assertion fail => node:test:135:10
+reject fail => node:test:135:10
+./node/test.js (uncaught error)
+
+FAILED | 8 passed | 51 failed | 4 ignored [WILDCARD]
+
+error: Test failed
diff --git a/ext/node/lib.rs b/ext/node/lib.rs
index 318de77e1..ca79c3dce 100644
--- a/ext/node/lib.rs
+++ b/ext/node/lib.rs
@@ -491,6 +491,7 @@ deno_core::extension!(deno_node,
"stream/web.ts" with_specifier "node:stream/web",
"string_decoder.ts" with_specifier "node:string_decoder",
"sys.ts" with_specifier "node:sys",
+ "testing.ts" with_specifier "node:test",
"timers.ts" with_specifier "node:timers",
"timers/promises.ts" with_specifier "node:timers/promises",
"tls.ts" with_specifier "node:tls",
diff --git a/ext/node/polyfill.rs b/ext/node/polyfill.rs
index 16ffe185d..fede915a2 100644
--- a/ext/node/polyfill.rs
+++ b/ext/node/polyfill.rs
@@ -62,6 +62,7 @@ generate_builtin_node_module_lists! {
"stream/web",
"string_decoder",
"sys",
+ "test",
"timers",
"timers/promises",
"tls",
diff --git a/ext/node/polyfills/01_require.js b/ext/node/polyfills/01_require.js
index c58dad9a4..acdf8402f 100644
--- a/ext/node/polyfills/01_require.js
+++ b/ext/node/polyfills/01_require.js
@@ -117,6 +117,7 @@ import streamPromises from "node:stream/promises";
import streamWeb from "node:stream/web";
import stringDecoder from "node:string_decoder";
import sys from "node:sys";
+import test from "node:test";
import timers from "node:timers";
import timersPromises from "node:timers/promises";
import tls from "node:tls";
@@ -219,6 +220,7 @@ function setupBuiltinModules() {
"stream/web": streamWeb,
string_decoder: stringDecoder,
sys,
+ test,
timers,
"timers/promises": timersPromises,
tls,
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;