1
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2024-12-25 16:49:18 -05:00

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;
```
This commit is contained in:
Bartek Iwańczuk 2021-11-23 14:57:51 +01:00 committed by GitHub
parent ae34f8fa10
commit d8afd56838
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 246 additions and 27 deletions

View file

@ -315,11 +315,11 @@ declare namespace Deno {
* ```ts * ```ts
* import {assert, fail, assertEquals} from "https://deno.land/std/testing/asserts.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"); * 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 decoder = new TextDecoder("utf-8");
* const data = await Deno.readFile("hello_world.txt"); * const data = await Deno.readFile("hello_world.txt");
* assertEquals(decoder.decode(data), "Hello world"); * assertEquals(decoder.decode(data), "Hello world");
@ -331,6 +331,95 @@ declare namespace Deno {
fn: (t: TestContext) => void | Promise<void>, fn: (t: TestContext) => void | Promise<void>,
): 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 /** Exit the Deno process with optional exit code. If no exit code is supplied
* then Deno will exit with return code of 0. * then Deno will exit with return code of 0.
* *

View file

@ -1016,29 +1016,29 @@ async fn test_resolve_dns() {
#[test] #[test]
fn typecheck_declarations_ns() { fn typecheck_declarations_ns() {
let status = util::deno_cmd() let output = util::deno_cmd()
.arg("test") .arg("test")
.arg("--doc") .arg("--doc")
.arg(util::root_path().join("cli/dts/lib.deno.ns.d.ts")) .arg(util::root_path().join("cli/dts/lib.deno.ns.d.ts"))
.spawn() .output()
.unwrap()
.wait()
.unwrap(); .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] #[test]
fn typecheck_declarations_unstable() { fn typecheck_declarations_unstable() {
let status = util::deno_cmd() let output = util::deno_cmd()
.arg("test") .arg("test")
.arg("--doc") .arg("--doc")
.arg("--unstable") .arg("--unstable")
.arg(util::root_path().join("cli/dts/lib.deno.unstable.d.ts")) .arg(util::root_path().join("cli/dts/lib.deno.unstable.d.ts"))
.spawn() .output()
.unwrap()
.wait()
.unwrap(); .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] #[test]

View file

@ -19,6 +19,12 @@ fn no_color() {
assert!(out.contains("test result: FAILED. 1 passed; 1 failed; 1 ignored; 0 measured; 0 filtered out")); 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 { itest!(meta {
args: "test test/meta.ts", args: "test test/meta.ts",
exit_code: 0, exit_code: 0,

11
cli/tests/testdata/test/overloads.out vendored Normal file
View file

@ -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])

6
cli/tests/testdata/test/overloads.ts vendored Normal file
View file

@ -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() {});

View file

@ -1,9 +1,63 @@
// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. // Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
import { assertRejects, assertThrows, unitTest } from "./test_util.ts"; import { assertRejects, assertThrows, unitTest } from "./test_util.ts";
unitTest(function testFnOverloading() { unitTest(function testWrongOverloads() {
// just verifying that you can use this test definition syntax assertThrows(
Deno.test("test fn overloading", () => {}); () => {
// @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() { unitTest(function nameOfTestCaseCantBeEmpty() {

View file

@ -254,8 +254,9 @@ finishing test case.`;
// Main test function provided by Deno. // Main test function provided by Deno.
function test( function test(
t, nameOrFnOrOptions,
fn, optionsOrFn,
maybeFn,
) { ) {
let testDef; let testDef;
const defaults = { const defaults = {
@ -267,22 +268,74 @@ finishing test case.`;
permissions: null, permissions: null,
}; };
if (typeof t === "string") { if (typeof nameOrFnOrOptions === "string") {
if (!fn || typeof fn != "function") { if (!nameOrFnOrOptions) {
throw new TypeError("Missing test function");
}
if (!t) {
throw new TypeError("The test name can't be empty"); 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 { } else {
if (!t.fn) { if (!maybeFn || typeof maybeFn !== "function") {
throw new TypeError("Missing test function"); throw new TypeError("Missing test function");
} }
if (!t.name) { 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 {
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 (!name) {
throw new TypeError("The test name can't be empty"); throw new TypeError("The test name can't be empty");
} }
testDef = { ...defaults, ...t }; testDef = { ...defaults, ...nameOrFnOrOptions, fn, name };
} }
testDef.fn = wrapTestFnWithSanitizers(testDef.fn, testDef); testDef.fn = wrapTestFnWithSanitizers(testDef.fn, testDef);