summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBartek IwaƄczuk <biwanczuk@gmail.com>2021-11-23 14:57:51 +0100
committerGitHub <noreply@github.com>2021-11-23 14:57:51 +0100
commitd8afd5683857de83f3cc80c33322df3d65377210 (patch)
tree0ffb7f1e94994282aadc6f6c342a1884c19774ae
parentae34f8fa10f4daddde3d32cc63773d288253d4d4 (diff)
feat(test): Add more overloads for "Deno.test" (#12749)
This commit adds 4 more overloads to "Deno.test()" API. ``` // Deno.test(function testName() { }); export function test(fn: (t: TestContext) => void | Promise<void>): void; // Deno.test("test name", { only: true }, function() { }); export function test( name: string, options: Omit<TestDefinition, "name">, fn: (t: TestContext) => void | Promise<void>, ): void; // Deno.test({ name: "test name" }, function() { }); export function test( options: Omit<TestDefinition, "fn">, fn: (t: TestContext) => void | Promise<void>, ): void; // Deno.test({ only: true }, function testName() { }); export function test( options: Omit<TestDefinition, "fn" | "name">, fn: (t: TestContext) => void | Promise<void>, ): void; ```
-rw-r--r--cli/dts/lib.deno.ns.d.ts93
-rw-r--r--cli/tests/integration/mod.rs20
-rw-r--r--cli/tests/integration/test_tests.rs6
-rw-r--r--cli/tests/testdata/test/overloads.out11
-rw-r--r--cli/tests/testdata/test/overloads.ts6
-rw-r--r--cli/tests/unit/testing_test.ts60
-rw-r--r--runtime/js/40_testing.js77
7 files changed, 246 insertions, 27 deletions
diff --git a/cli/dts/lib.deno.ns.d.ts b/cli/dts/lib.deno.ns.d.ts
index 74aa334e2..4bc938ba1 100644
--- a/cli/dts/lib.deno.ns.d.ts
+++ b/cli/dts/lib.deno.ns.d.ts
@@ -315,11 +315,11 @@ declare namespace Deno {
* ```ts
* import {assert, fail, assertEquals} from "https://deno.land/std/testing/asserts.ts";
*
- * Deno.test("My test description", ():void => {
+ * Deno.test("My test description", (): void => {
* assertEquals("hello", "hello");
* });
*
- * Deno.test("My async test description", async ():Promise<void> => {
+ * Deno.test("My async test description", async (): Promise<void> => {
* const decoder = new TextDecoder("utf-8");
* const data = await Deno.readFile("hello_world.txt");
* assertEquals(decoder.decode(data), "Hello world");
@@ -331,6 +331,95 @@ declare namespace Deno {
fn: (t: TestContext) => void | Promise<void>,
): void;
+ /** Register a test which will be run when `deno test` is used on the command
+ * line and the containing module looks like a test module.
+ * `fn` can be async if required. Declared function must have a name.
+ *
+ * ```ts
+ * import {assert, fail, assertEquals} from "https://deno.land/std/testing/asserts.ts";
+ *
+ * Deno.test(function myTestName(): void {
+ * assertEquals("hello", "hello");
+ * });
+ *
+ * Deno.test(async function myOtherTestName(): Promise<void> {
+ * const decoder = new TextDecoder("utf-8");
+ * const data = await Deno.readFile("hello_world.txt");
+ * assertEquals(decoder.decode(data), "Hello world");
+ * });
+ * ```
+ */
+ export function test(fn: (t: TestContext) => void | Promise<void>): void;
+
+ /** Register a test which will be run when `deno test` is used on the command
+ * line and the containing module looks like a test module.
+ * `fn` can be async if required.
+ *
+ * ```ts
+ * import {assert, fail, assertEquals} from "https://deno.land/std/testing/asserts.ts";
+ *
+ * Deno.test("My test description", { permissions: { read: true } }, (): void => {
+ * assertEquals("hello", "hello");
+ * });
+ *
+ * Deno.test("My async test description", { permissions: { read: false } }, async (): Promise<void> => {
+ * const decoder = new TextDecoder("utf-8");
+ * const data = await Deno.readFile("hello_world.txt");
+ * assertEquals(decoder.decode(data), "Hello world");
+ * });
+ * ```
+ */
+ export function test(
+ name: string,
+ options: Omit<TestDefinition, "fn" | "name">,
+ fn: (t: TestContext) => void | Promise<void>,
+ ): void;
+
+ /** Register a test which will be run when `deno test` is used on the command
+ * line and the containing module looks like a test module.
+ * `fn` can be async if required.
+ *
+ * ```ts
+ * import {assert, fail, assertEquals} from "https://deno.land/std/testing/asserts.ts";
+ *
+ * Deno.test({ name: "My test description", permissions: { read: true } }, (): void => {
+ * assertEquals("hello", "hello");
+ * });
+ *
+ * Deno.test({ name: "My async test description", permissions: { read: false } }, async (): Promise<void> => {
+ * const decoder = new TextDecoder("utf-8");
+ * const data = await Deno.readFile("hello_world.txt");
+ * assertEquals(decoder.decode(data), "Hello world");
+ * });
+ * ```
+ */
+ export function test(
+ options: Omit<TestDefinition, "fn">,
+ fn: (t: TestContext) => void | Promise<void>,
+ ): void;
+
+ /** Register a test which will be run when `deno test` is used on the command
+ * line and the containing module looks like a test module.
+ * `fn` can be async if required. Declared function must have a name.
+ *
+ * ```ts
+ * import {assert, fail, assertEquals} from "https://deno.land/std/testing/asserts.ts";
+ *
+ * Deno.test({ permissions: { read: true } }, function myTestName(): void {
+ * assertEquals("hello", "hello");
+ * });
+ *
+ * Deno.test({ permissions: { read: false } }, async function myOtherTestName(): Promise<void> {
+ * const decoder = new TextDecoder("utf-8");
+ * const data = await Deno.readFile("hello_world.txt");
+ * assertEquals(decoder.decode(data), "Hello world");
+ * });
+ * ```
+ */
+ export function test(
+ options: Omit<TestDefinition, "fn" | "name">,
+ fn: (t: TestContext) => void | Promise<void>,
+ ): void;
/** Exit the Deno process with optional exit code. If no exit code is supplied
* then Deno will exit with return code of 0.
*
diff --git a/cli/tests/integration/mod.rs b/cli/tests/integration/mod.rs
index 21ffc5627..cfb950901 100644
--- a/cli/tests/integration/mod.rs
+++ b/cli/tests/integration/mod.rs
@@ -1016,29 +1016,29 @@ async fn test_resolve_dns() {
#[test]
fn typecheck_declarations_ns() {
- let status = util::deno_cmd()
+ let output = util::deno_cmd()
.arg("test")
.arg("--doc")
.arg(util::root_path().join("cli/dts/lib.deno.ns.d.ts"))
- .spawn()
- .unwrap()
- .wait()
+ .output()
.unwrap();
- assert!(status.success());
+ println!("stdout: {}", String::from_utf8(output.stdout).unwrap());
+ println!("stderr: {}", String::from_utf8(output.stderr).unwrap());
+ assert!(output.status.success());
}
#[test]
fn typecheck_declarations_unstable() {
- let status = util::deno_cmd()
+ let output = util::deno_cmd()
.arg("test")
.arg("--doc")
.arg("--unstable")
.arg(util::root_path().join("cli/dts/lib.deno.unstable.d.ts"))
- .spawn()
- .unwrap()
- .wait()
+ .output()
.unwrap();
- assert!(status.success());
+ println!("stdout: {}", String::from_utf8(output.stdout).unwrap());
+ println!("stderr: {}", String::from_utf8(output.stderr).unwrap());
+ assert!(output.status.success());
}
#[test]
diff --git a/cli/tests/integration/test_tests.rs b/cli/tests/integration/test_tests.rs
index e5d3fd358..3f23efca2 100644
--- a/cli/tests/integration/test_tests.rs
+++ b/cli/tests/integration/test_tests.rs
@@ -19,6 +19,12 @@ fn no_color() {
assert!(out.contains("test result: FAILED. 1 passed; 1 failed; 1 ignored; 0 measured; 0 filtered out"));
}
+itest!(overloads {
+ args: "test test/overloads.ts",
+ exit_code: 0,
+ output: "test/overloads.out",
+});
+
itest!(meta {
args: "test test/meta.ts",
exit_code: 0,
diff --git a/cli/tests/testdata/test/overloads.out b/cli/tests/testdata/test/overloads.out
new file mode 100644
index 000000000..13c088f6c
--- /dev/null
+++ b/cli/tests/testdata/test/overloads.out
@@ -0,0 +1,11 @@
+Check [WILDCARD]/test/overloads.ts
+running 6 tests from [WILDCARD]/test/overloads.ts
+test test0 ... ok ([WILDCARD])
+test test1 ... ok ([WILDCARD])
+test test2 ... ok ([WILDCARD])
+test test3 ... ok ([WILDCARD])
+test test4 ... ok ([WILDCARD])
+test test5 ... ignored ([WILDCARD])
+
+test result: ok. 5 passed; 0 failed; 1 ignored; 0 measured; 0 filtered out ([WILDCARD])
+
diff --git a/cli/tests/testdata/test/overloads.ts b/cli/tests/testdata/test/overloads.ts
new file mode 100644
index 000000000..eb7b3dccc
--- /dev/null
+++ b/cli/tests/testdata/test/overloads.ts
@@ -0,0 +1,6 @@
+Deno.test("test0", () => {});
+Deno.test(function test1() {});
+Deno.test({ name: "test2", fn: () => {} });
+Deno.test("test3", { permissions: "none" }, () => {});
+Deno.test({ name: "test4" }, () => {});
+Deno.test({ ignore: true }, function test5() {});
diff --git a/cli/tests/unit/testing_test.ts b/cli/tests/unit/testing_test.ts
index 144246002..d4d25d12f 100644
--- a/cli/tests/unit/testing_test.ts
+++ b/cli/tests/unit/testing_test.ts
@@ -1,9 +1,63 @@
// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
import { assertRejects, assertThrows, unitTest } from "./test_util.ts";
-unitTest(function testFnOverloading() {
- // just verifying that you can use this test definition syntax
- Deno.test("test fn overloading", () => {});
+unitTest(function testWrongOverloads() {
+ assertThrows(
+ () => {
+ // @ts-ignore Testing invalid overloads
+ Deno.test("some name", { fn: () => {} }, () => {});
+ },
+ TypeError,
+ "Unexpected 'fn' field in options, test function is already provided as the third argument.",
+ );
+ assertThrows(
+ () => {
+ // @ts-ignore Testing invalid overloads
+ Deno.test("some name", { name: "some name2" }, () => {});
+ },
+ TypeError,
+ "Unexpected 'name' field in options, test name is already provided as the first argument.",
+ );
+ assertThrows(
+ () => {
+ // @ts-ignore Testing invalid overloads
+ Deno.test(() => {});
+ },
+ TypeError,
+ "The test function must have a name",
+ );
+ assertThrows(
+ () => {
+ // @ts-ignore Testing invalid overloads
+ Deno.test(function foo() {}, {});
+ },
+ TypeError,
+ "Unexpected second argument to Deno.test()",
+ );
+ assertThrows(
+ () => {
+ // @ts-ignore Testing invalid overloads
+ Deno.test({ fn: () => {} }, function foo() {});
+ },
+ TypeError,
+ "Unexpected 'fn' field in options, test function is already provided as the second argument.",
+ );
+ assertThrows(
+ () => {
+ // @ts-ignore Testing invalid overloads
+ Deno.test({});
+ },
+ TypeError,
+ "Expected 'fn' field in the first argument to be a test function.",
+ );
+ assertThrows(
+ () => {
+ // @ts-ignore Testing invalid overloads
+ Deno.test({ fn: "boo!" });
+ },
+ TypeError,
+ "Expected 'fn' field in the first argument to be a test function.",
+ );
});
unitTest(function nameOfTestCaseCantBeEmpty() {
diff --git a/runtime/js/40_testing.js b/runtime/js/40_testing.js
index e07d54a1a..053afc5da 100644
--- a/runtime/js/40_testing.js
+++ b/runtime/js/40_testing.js
@@ -254,8 +254,9 @@ finishing test case.`;
// Main test function provided by Deno.
function test(
- t,
- fn,
+ nameOrFnOrOptions,
+ optionsOrFn,
+ maybeFn,
) {
let testDef;
const defaults = {
@@ -267,22 +268,74 @@ finishing test case.`;
permissions: null,
};
- if (typeof t === "string") {
- if (!fn || typeof fn != "function") {
- throw new TypeError("Missing test function");
- }
- if (!t) {
+ if (typeof nameOrFnOrOptions === "string") {
+ if (!nameOrFnOrOptions) {
throw new TypeError("The test name can't be empty");
}
- testDef = { fn: fn, name: t, ...defaults };
+ if (typeof optionsOrFn === "function") {
+ testDef = { fn: optionsOrFn, name: nameOrFnOrOptions, ...defaults };
+ } else {
+ if (!maybeFn || typeof maybeFn !== "function") {
+ throw new TypeError("Missing test function");
+ }
+ if (optionsOrFn.fn != undefined) {
+ throw new TypeError(
+ "Unexpected 'fn' field in options, test function is already provided as the third argument.",
+ );
+ }
+ if (optionsOrFn.name != undefined) {
+ throw new TypeError(
+ "Unexpected 'name' field in options, test name is already provided as the first argument.",
+ );
+ }
+ testDef = {
+ ...defaults,
+ ...optionsOrFn,
+ fn: maybeFn,
+ name: nameOrFnOrOptions,
+ };
+ }
+ } else if (typeof nameOrFnOrOptions === "function") {
+ if (!nameOrFnOrOptions.name) {
+ throw new TypeError("The test function must have a name");
+ }
+ if (optionsOrFn != undefined) {
+ throw new TypeError("Unexpected second argument to Deno.test()");
+ }
+ if (maybeFn != undefined) {
+ throw new TypeError("Unexpected third argument to Deno.test()");
+ }
+ testDef = {
+ ...defaults,
+ fn: nameOrFnOrOptions,
+ name: nameOrFnOrOptions.name,
+ };
} else {
- if (!t.fn) {
- throw new TypeError("Missing test function");
+ let fn;
+ let name;
+ if (typeof optionsOrFn === "function") {
+ fn = optionsOrFn;
+ if (nameOrFnOrOptions.fn != undefined) {
+ throw new TypeError(
+ "Unexpected 'fn' field in options, test function is already provided as the second argument.",
+ );
+ }
+ name = nameOrFnOrOptions.name ?? fn.name;
+ } else {
+ if (
+ !nameOrFnOrOptions.fn || typeof nameOrFnOrOptions.fn !== "function"
+ ) {
+ throw new TypeError(
+ "Expected 'fn' field in the first argument to be a test function.",
+ );
+ }
+ fn = nameOrFnOrOptions.fn;
+ name = nameOrFnOrOptions.name ?? fn.name;
}
- if (!t.name) {
+ if (!name) {
throw new TypeError("The test name can't be empty");
}
- testDef = { ...defaults, ...t };
+ testDef = { ...defaults, ...nameOrFnOrOptions, fn, name };
}
testDef.fn = wrapTestFnWithSanitizers(testDef.fn, testDef);