mirror of
https://github.com/denoland/deno.git
synced 2025-08-03 02:22:40 +00:00
feat: Add TestDefinition::only (#5793)
This commit is contained in:
parent
3eee961473
commit
e613bfe47a
7 changed files with 75 additions and 15 deletions
3
cli/js/lib.deno.ns.d.ts
vendored
3
cli/js/lib.deno.ns.d.ts
vendored
|
@ -38,6 +38,9 @@ declare namespace Deno {
|
||||||
fn: () => void | Promise<void>;
|
fn: () => void | Promise<void>;
|
||||||
name: string;
|
name: string;
|
||||||
ignore?: boolean;
|
ignore?: boolean;
|
||||||
|
/** If at lease one test has `only` set to true, only run tests that have
|
||||||
|
* `only` set to true and fail the test suite. */
|
||||||
|
only?: boolean;
|
||||||
/** Check that the number of async completed ops after the test is the same
|
/** Check that the number of async completed ops after the test is the same
|
||||||
* as number of dispatched ops. Defaults to true.*/
|
* as number of dispatched ops. Defaults to true.*/
|
||||||
sanitizeOps?: boolean;
|
sanitizeOps?: boolean;
|
||||||
|
|
|
@ -52,8 +52,8 @@ Before:
|
||||||
After:
|
After:
|
||||||
- dispatched: ${post.opsDispatchedAsync}
|
- dispatched: ${post.opsDispatchedAsync}
|
||||||
- completed: ${post.opsCompletedAsync}
|
- completed: ${post.opsCompletedAsync}
|
||||||
|
|
||||||
Make sure to await all promises returned from Deno APIs before
|
Make sure to await all promises returned from Deno APIs before
|
||||||
finishing test case.`
|
finishing test case.`
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -76,7 +76,7 @@ function assertResources(
|
||||||
Before: ${preStr}
|
Before: ${preStr}
|
||||||
After: ${postStr}
|
After: ${postStr}
|
||||||
|
|
||||||
Make sure to close all open resource handles returned from Deno APIs before
|
Make sure to close all open resource handles returned from Deno APIs before
|
||||||
finishing test case.`;
|
finishing test case.`;
|
||||||
assert(preStr === postStr, msg);
|
assert(preStr === postStr, msg);
|
||||||
};
|
};
|
||||||
|
@ -86,6 +86,7 @@ export interface TestDefinition {
|
||||||
fn: () => void | Promise<void>;
|
fn: () => void | Promise<void>;
|
||||||
name: string;
|
name: string;
|
||||||
ignore?: boolean;
|
ignore?: boolean;
|
||||||
|
only?: boolean;
|
||||||
sanitizeOps?: boolean;
|
sanitizeOps?: boolean;
|
||||||
sanitizeResources?: boolean;
|
sanitizeResources?: boolean;
|
||||||
}
|
}
|
||||||
|
@ -103,6 +104,7 @@ export function test(
|
||||||
let testDef: TestDefinition;
|
let testDef: TestDefinition;
|
||||||
const defaults = {
|
const defaults = {
|
||||||
ignore: false,
|
ignore: false,
|
||||||
|
only: false,
|
||||||
sanitizeOps: true,
|
sanitizeOps: true,
|
||||||
sanitizeResources: true,
|
sanitizeResources: true,
|
||||||
};
|
};
|
||||||
|
@ -156,6 +158,7 @@ interface TestMessage {
|
||||||
measured: number;
|
measured: number;
|
||||||
passed: number;
|
passed: number;
|
||||||
failed: number;
|
failed: number;
|
||||||
|
usedOnly: boolean;
|
||||||
duration: number;
|
duration: number;
|
||||||
results: Array<TestMessage["testEnd"] & {}>;
|
results: Array<TestMessage["testEnd"] & {}>;
|
||||||
};
|
};
|
||||||
|
@ -218,6 +221,10 @@ function reportToConsole(message: TestMessage): void {
|
||||||
`${message.end.filtered} filtered out ` +
|
`${message.end.filtered} filtered out ` +
|
||||||
`${formatDuration(message.end.duration)}\n`
|
`${formatDuration(message.end.duration)}\n`
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (message.end.usedOnly && message.end.failed == 0) {
|
||||||
|
log(`${RED_FAILED} because the "only" option was used\n`);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -225,7 +232,7 @@ exposeForTest("reportToConsole", reportToConsole);
|
||||||
|
|
||||||
// TODO: already implements AsyncGenerator<RunTestsMessage>, but add as "implements to class"
|
// TODO: already implements AsyncGenerator<RunTestsMessage>, but add as "implements to class"
|
||||||
// TODO: implements PromiseLike<RunTestsEndResult>
|
// TODO: implements PromiseLike<RunTestsEndResult>
|
||||||
class TestApi {
|
class TestRunner {
|
||||||
readonly testsToRun: TestDefinition[];
|
readonly testsToRun: TestDefinition[];
|
||||||
readonly stats = {
|
readonly stats = {
|
||||||
filtered: 0,
|
filtered: 0,
|
||||||
|
@ -234,14 +241,18 @@ class TestApi {
|
||||||
passed: 0,
|
passed: 0,
|
||||||
failed: 0,
|
failed: 0,
|
||||||
};
|
};
|
||||||
|
private usedOnly: boolean;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
public tests: TestDefinition[],
|
tests: TestDefinition[],
|
||||||
public filterFn: (def: TestDefinition) => boolean,
|
public filterFn: (def: TestDefinition) => boolean,
|
||||||
public failFast: boolean
|
public failFast: boolean
|
||||||
) {
|
) {
|
||||||
this.testsToRun = tests.filter(filterFn);
|
const onlyTests = tests.filter(({ only }) => only);
|
||||||
this.stats.filtered = tests.length - this.testsToRun.length;
|
this.usedOnly = onlyTests.length > 0;
|
||||||
|
const unfilteredTests = this.usedOnly ? onlyTests : tests;
|
||||||
|
this.testsToRun = unfilteredTests.filter(filterFn);
|
||||||
|
this.stats.filtered = unfilteredTests.length - this.testsToRun.length;
|
||||||
}
|
}
|
||||||
|
|
||||||
async *[Symbol.asyncIterator](): AsyncIterator<TestMessage> {
|
async *[Symbol.asyncIterator](): AsyncIterator<TestMessage> {
|
||||||
|
@ -280,7 +291,9 @@ class TestApi {
|
||||||
|
|
||||||
const duration = +new Date() - suiteStart;
|
const duration = +new Date() - suiteStart;
|
||||||
|
|
||||||
yield { end: { ...this.stats, duration, results } };
|
yield {
|
||||||
|
end: { ...this.stats, usedOnly: this.usedOnly, duration, results },
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -331,7 +344,7 @@ async function runTests({
|
||||||
onMessage = undefined,
|
onMessage = undefined,
|
||||||
}: RunTestsOptions = {}): Promise<TestMessage["end"] & {}> {
|
}: RunTestsOptions = {}): Promise<TestMessage["end"] & {}> {
|
||||||
const filterFn = createFilterFn(filter, skip);
|
const filterFn = createFilterFn(filter, skip);
|
||||||
const testApi = new TestApi(TEST_REGISTRY, filterFn, failFast);
|
const testRunner = new TestRunner(TEST_REGISTRY, filterFn, failFast);
|
||||||
|
|
||||||
const originalConsole = globalThis.console;
|
const originalConsole = globalThis.console;
|
||||||
|
|
||||||
|
@ -342,7 +355,7 @@ async function runTests({
|
||||||
|
|
||||||
let endMsg: TestMessage["end"];
|
let endMsg: TestMessage["end"];
|
||||||
|
|
||||||
for await (const message of testApi) {
|
for await (const message of testRunner) {
|
||||||
if (onMessage != null) {
|
if (onMessage != null) {
|
||||||
await onMessage(message);
|
await onMessage(message);
|
||||||
}
|
}
|
||||||
|
@ -358,7 +371,7 @@ async function runTests({
|
||||||
globalThis.console = originalConsole;
|
globalThis.console = originalConsole;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (endMsg!.failed > 0 && exitOnFail) {
|
if ((endMsg!.failed > 0 || endMsg?.usedOnly) && exitOnFail) {
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
15
cli/tests/deno_test_only.ts
Normal file
15
cli/tests/deno_test_only.ts
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
Deno.test({
|
||||||
|
name: "abc",
|
||||||
|
fn() {},
|
||||||
|
});
|
||||||
|
|
||||||
|
Deno.test({
|
||||||
|
only: true,
|
||||||
|
name: "def",
|
||||||
|
fn() {},
|
||||||
|
});
|
||||||
|
|
||||||
|
Deno.test({
|
||||||
|
name: "ghi",
|
||||||
|
fn() {},
|
||||||
|
});
|
7
cli/tests/deno_test_only.ts.out
Normal file
7
cli/tests/deno_test_only.ts.out
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
[WILDCARD]running 1 tests
|
||||||
|
test def ... ok ([WILDCARD])
|
||||||
|
|
||||||
|
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out ([WILDCARD])
|
||||||
|
|
||||||
|
FAILED because the "only" option was used
|
||||||
|
|
|
@ -1272,16 +1272,22 @@ itest!(_026_redirect_javascript {
|
||||||
http_server: true,
|
http_server: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
itest!(deno_test {
|
||||||
|
args: "test test_runner_test.ts",
|
||||||
|
exit_code: 1,
|
||||||
|
output: "deno_test.out",
|
||||||
|
});
|
||||||
|
|
||||||
itest!(deno_test_fail_fast {
|
itest!(deno_test_fail_fast {
|
||||||
args: "test --failfast test_runner_test.ts",
|
args: "test --failfast test_runner_test.ts",
|
||||||
exit_code: 1,
|
exit_code: 1,
|
||||||
output: "deno_test_fail_fast.out",
|
output: "deno_test_fail_fast.out",
|
||||||
});
|
});
|
||||||
|
|
||||||
itest!(deno_test {
|
itest!(deno_test_only {
|
||||||
args: "test test_runner_test.ts",
|
args: "test deno_test_only.ts",
|
||||||
exit_code: 1,
|
exit_code: 1,
|
||||||
output: "deno_test.out",
|
output: "deno_test_only.ts.out",
|
||||||
});
|
});
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
||||||
|
|
||||||
import { assert, assertEquals } from "../../../std/testing/asserts.ts";
|
import { assert, assertEquals } from "../../../std/testing/asserts.ts";
|
||||||
|
import * as colors from "../../../std/fmt/colors.ts";
|
||||||
|
export { colors };
|
||||||
import { resolve } from "../../../std/path/mod.ts";
|
import { resolve } from "../../../std/path/mod.ts";
|
||||||
export {
|
export {
|
||||||
assert,
|
assert,
|
||||||
|
@ -93,7 +95,9 @@ function registerPermCombination(perms: Permissions): void {
|
||||||
export async function registerUnitTests(): Promise<void> {
|
export async function registerUnitTests(): Promise<void> {
|
||||||
const processPerms = await getProcessPermissions();
|
const processPerms = await getProcessPermissions();
|
||||||
|
|
||||||
for (const unitTestDefinition of REGISTERED_UNIT_TESTS) {
|
const onlyTests = REGISTERED_UNIT_TESTS.filter(({ only }) => only);
|
||||||
|
const unitTests = onlyTests.length > 0 ? onlyTests : REGISTERED_UNIT_TESTS;
|
||||||
|
for (const unitTestDefinition of unitTests) {
|
||||||
if (!permissionsMatch(processPerms, unitTestDefinition.perms)) {
|
if (!permissionsMatch(processPerms, unitTestDefinition.perms)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -126,11 +130,13 @@ interface UnitTestPermissions {
|
||||||
|
|
||||||
interface UnitTestOptions {
|
interface UnitTestOptions {
|
||||||
ignore?: boolean;
|
ignore?: boolean;
|
||||||
|
only?: boolean;
|
||||||
perms?: UnitTestPermissions;
|
perms?: UnitTestPermissions;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface UnitTestDefinition extends Deno.TestDefinition {
|
interface UnitTestDefinition extends Deno.TestDefinition {
|
||||||
ignore: boolean;
|
ignore: boolean;
|
||||||
|
only: boolean;
|
||||||
perms: Permissions;
|
perms: Permissions;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -174,6 +180,7 @@ export function unitTest(
|
||||||
name,
|
name,
|
||||||
fn,
|
fn,
|
||||||
ignore: !!options.ignore,
|
ignore: !!options.ignore,
|
||||||
|
only: !!options.only,
|
||||||
perms: normalizedPerms,
|
perms: normalizedPerms,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,8 @@
|
||||||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
||||||
import "./unit_tests.ts";
|
import "./unit_tests.ts";
|
||||||
import {
|
import {
|
||||||
|
REGISTERED_UNIT_TESTS,
|
||||||
|
colors,
|
||||||
readLines,
|
readLines,
|
||||||
permissionCombinations,
|
permissionCombinations,
|
||||||
Permissions,
|
Permissions,
|
||||||
|
@ -225,6 +227,13 @@ async function masterRunnerMain(
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log("Unit tests passed");
|
console.log("Unit tests passed");
|
||||||
|
|
||||||
|
if (REGISTERED_UNIT_TESTS.find(({ only }) => only)) {
|
||||||
|
console.error(
|
||||||
|
`\n${colors.red("FAILED")} because the "only" option was used`
|
||||||
|
);
|
||||||
|
Deno.exit(1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const HELP = `Unit test runner
|
const HELP = `Unit test runner
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue