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

feat(cli/js/testing): Add TestDefinition::skip (#4351)

This commit is contained in:
Nayeem Rahman 2020-03-15 09:34:24 +00:00 committed by GitHub
parent a159165fe5
commit 64a35acd64
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 104 additions and 105 deletions

View file

@ -17,6 +17,7 @@ declare namespace Deno {
export interface TestDefinition { export interface TestDefinition {
fn: TestFunction; fn: TestFunction;
name: string; name: string;
skip?: boolean;
} }
/** Register a test which will be run when `deno test` is used on the command /** Register a test which will be run when `deno test` is used on the command
@ -32,12 +33,16 @@ declare namespace Deno {
* when `Deno.runTests` is used */ * when `Deno.runTests` is used */
export function test(name: string, fn: TestFunction): void; export function test(name: string, fn: TestFunction): void;
enum TestStatus {
Passed = "passed",
Failed = "failed",
Skipped = "skipped"
}
interface TestResult { interface TestResult {
passed: boolean;
name: string; name: string;
skipped: boolean; status: TestStatus;
hasRun: boolean; duration?: number;
duration: number;
error?: Error; error?: Error;
} }

View file

@ -1,10 +1,11 @@
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. // Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
import { red, green, bgRed, gray, italic } from "./colors.ts"; import { bgRed, gray, green, italic, red, yellow } from "./colors.ts";
import { exit } from "./ops/os.ts"; import { exit } from "./ops/os.ts";
import { Console } from "./web/console.ts"; import { Console } from "./web/console.ts";
const RED_FAILED = red("FAILED"); const RED_FAILED = red("FAILED");
const GREEN_OK = green("OK"); const GREEN_OK = green("OK");
const YELLOW_SKIPPED = yellow("SKIPPED");
const RED_BG_FAIL = bgRed(" FAIL "); const RED_BG_FAIL = bgRed(" FAIL ");
const disabledConsole = new Console((_x: string, _isErr?: boolean): void => {}); const disabledConsole = new Console((_x: string, _isErr?: boolean): void => {});
@ -18,6 +19,7 @@ export type TestFunction = () => void | Promise<void>;
export interface TestDefinition { export interface TestDefinition {
fn: TestFunction; fn: TestFunction;
name: string; name: string;
skip?: boolean;
} }
const TEST_REGISTRY: TestDefinition[] = []; const TEST_REGISTRY: TestDefinition[] = [];
@ -31,34 +33,32 @@ export function test(
t: string | TestDefinition | TestFunction, t: string | TestDefinition | TestFunction,
fn?: TestFunction fn?: TestFunction
): void { ): void {
let name: string; let testDef: TestDefinition;
if (typeof t === "string") { if (typeof t === "string") {
if (!fn) { if (!fn || typeof fn != "function") {
throw new Error("Missing test function"); throw new TypeError("Missing test function");
} }
name = t; if (!t) {
if (!name) { throw new TypeError("The test name can't be empty");
throw new Error("The name of test case can't be empty");
} }
testDef = { fn: fn as TestFunction, name: t, skip: false };
} else if (typeof t === "function") { } else if (typeof t === "function") {
fn = t; if (!t.name) {
name = t.name; throw new TypeError("The test function can't be anonymous");
if (!name) {
throw new Error("Test function can't be anonymous");
} }
testDef = { fn: t, name: t.name, skip: false };
} else { } else {
fn = t.fn; if (!t.fn) {
if (!fn) { throw new TypeError("Missing test function");
throw new Error("Missing test function");
} }
name = t.name; if (!t.name) {
if (!name) { throw new TypeError("The test name can't be empty");
throw new Error("The name of test case can't be empty");
} }
testDef = { fn: t.fn, name: t.name, skip: Boolean(t.skip) };
} }
TEST_REGISTRY.push({ fn, name }); TEST_REGISTRY.push(testDef);
} }
interface TestStats { interface TestStats {
@ -78,18 +78,17 @@ export interface RunTestsOptions {
reporter?: TestReporter; reporter?: TestReporter;
} }
interface TestResult { enum TestStatus {
passed: boolean; Passed = "passed",
name: string; Failed = "failed",
skipped: boolean; Skipped = "skipped"
hasRun: boolean;
duration: number;
error?: Error;
} }
interface TestCase { interface TestResult {
result: TestResult; name: string;
fn: TestFunction; status: TestStatus;
duration?: number;
error?: Error;
} }
export enum TestEvent { export enum TestEvent {
@ -115,24 +114,10 @@ interface TestEventEnd {
results: TestResult[]; results: TestResult[];
} }
function testDefinitionToTestCase(def: TestDefinition): TestCase {
return {
fn: def.fn,
result: {
name: def.name,
passed: false,
skipped: false,
hasRun: false,
duration: 0
}
};
}
// 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<TestsResult> // TODO: implements PromiseLike<TestsResult>
class TestApi { class TestApi {
readonly testsToRun: TestDefinition[]; readonly testsToRun: TestDefinition[];
readonly testCases: TestCase[];
readonly stats: TestStats = { readonly stats: TestStats = {
filtered: 0, filtered: 0,
ignored: 0, ignored: 0,
@ -148,7 +133,6 @@ class TestApi {
) { ) {
this.testsToRun = tests.filter(filterFn); this.testsToRun = tests.filter(filterFn);
this.stats.filtered = tests.length - this.testsToRun.length; this.stats.filtered = tests.length - this.testsToRun.length;
this.testCases = this.testsToRun.map(testDefinitionToTestCase);
} }
async *[Symbol.asyncIterator](): AsyncIterator< async *[Symbol.asyncIterator](): AsyncIterator<
@ -159,32 +143,35 @@ class TestApi {
tests: this.testsToRun.length tests: this.testsToRun.length
}; };
const results: TestResult[] = [];
const suiteStart = +new Date(); const suiteStart = +new Date();
for (const testCase of this.testCases) { for (const { name, fn, skip } of this.testsToRun) {
const { fn, result } = testCase; const result: Partial<TestResult> = { name };
let shouldBreak = false; if (skip) {
try { result.status = TestStatus.Skipped;
this.stats.ignored++;
} else {
const start = +new Date(); const start = +new Date();
await fn(); try {
result.duration = +new Date() - start; await fn();
result.passed = true; result.duration = +new Date() - start;
this.stats.passed++; result.status = TestStatus.Passed;
} catch (err) { this.stats.passed++;
result.passed = false; } catch (err) {
result.error = err; result.duration = +new Date() - start;
this.stats.failed++; result.status = TestStatus.Failed;
shouldBreak = this.failFast; result.error = err;
} finally { this.stats.failed++;
result.hasRun = true;
yield { kind: TestEvent.Result, result };
if (shouldBreak) {
break;
} }
} }
yield { kind: TestEvent.Result, result: result as TestResult };
results.push(result as TestResult);
if (this.failFast && result.error != null) {
break;
}
} }
const duration = +new Date() - suiteStart; const duration = +new Date() - suiteStart;
const results = this.testCases.map(r => r.result);
yield { yield {
kind: TestEvent.End, kind: TestEvent.End,
@ -241,13 +228,21 @@ export class ConsoleTestReporter implements TestReporter {
async result(event: TestEventResult): Promise<void> { async result(event: TestEventResult): Promise<void> {
const { result } = event; const { result } = event;
if (result.passed) { switch (result.status) {
this.console.log( case TestStatus.Passed:
`${GREEN_OK} ${result.name} ${formatDuration(result.duration)}` this.console.log(
); `${GREEN_OK} ${result.name} ${formatDuration(result.duration!)}`
} else { );
this.console.log(`${RED_FAILED} ${result.name}`); break;
this.console.log(result.error!); case TestStatus.Failed:
this.console.log(
`${RED_FAILED} ${result.name} ${formatDuration(result.duration!)}`
);
this.console.log(result.error!);
break;
case TestStatus.Skipped:
this.console.log(`${YELLOW_SKIPPED} ${result.name}`);
break;
} }
} }

View file

@ -11,8 +11,8 @@ unitTest(function nameOfTestCaseCantBeEmpty(): void {
() => { () => {
Deno.test("", () => {}); Deno.test("", () => {});
}, },
Error, TypeError,
"The name of test case can't be empty" "The test name can't be empty"
); );
assertThrows( assertThrows(
() => { () => {
@ -21,8 +21,8 @@ unitTest(function nameOfTestCaseCantBeEmpty(): void {
fn: () => {} fn: () => {}
}); });
}, },
Error, TypeError,
"The name of test case can't be empty" "The test name can't be empty"
); );
}); });
@ -31,7 +31,7 @@ unitTest(function testFnCantBeAnonymous(): void {
() => { () => {
Deno.test(function() {}); Deno.test(function() {});
}, },
Error, TypeError,
"Test function can't be anonymous" "The test function can't be anonymous"
); );
}); });

View file

@ -1,10 +1,10 @@
running 7 tests running 7 tests
OK runGranted [WILDCARD] OK runGranted [WILDCARD]
OK readGranted [WILDCARD] OK readGranted [WILDCARD]
OK writeGranted [WILDCARD] OK writeGranted [WILDCARD]
OK netGranted [WILDCARD] OK netGranted [WILDCARD]
OK envGranted [WILDCARD] OK envGranted [WILDCARD]
OK pluginGranted [WILDCARD] OK pluginGranted [WILDCARD]
OK hrtimeGranted [WILDCARD] OK hrtimeGranted [WILDCARD]
test result: OK 7 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out [WILDCARD] test result: OK 7 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out [WILDCARD]

View file

@ -1,16 +1,15 @@
running 12 tests running 12 tests
OK compilerApiCompileSources [WILDCARD] OK compilerApiCompileSources [WILDCARD]
OK compilerApiCompileNoSources [WILDCARD] OK compilerApiCompileNoSources [WILDCARD]
OK compilerApiCompileOptions [WILDCARD] OK compilerApiCompileOptions [WILDCARD]
OK compilerApiCompileLib [WILDCARD] OK compilerApiCompileLib [WILDCARD]
OK compilerApiCompileTypes [WILDCARD] OK compilerApiCompileTypes [WILDCARD]
OK transpileOnlyApi [WILDCARD] OK transpileOnlyApi [WILDCARD]
OK transpileOnlyApiConfig [WILDCARD] OK transpileOnlyApiConfig [WILDCARD]
OK bundleApiSources [WILDCARD] OK bundleApiSources [WILDCARD]
OK bundleApiNoSources [WILDCARD] OK bundleApiNoSources [WILDCARD]
OK bundleApiConfig [WILDCARD] OK bundleApiConfig [WILDCARD]
OK bundleApiJsModules [WILDCARD] OK bundleApiJsModules [WILDCARD]
OK diagnosticsTest [WILDCARD] OK diagnosticsTest [WILDCARD]
test result: OK 12 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out [WILDCARD] test result: OK 12 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out [WILDCARD]

View file

@ -1,7 +1,7 @@
running 4 tests running 4 tests
OK workersBasic [WILDCARD] OK workersBasic [WILDCARD]
OK nestedWorker [WILDCARD] OK nestedWorker [WILDCARD]
OK workerThrowsWhenExecuting [WILDCARD] OK workerThrowsWhenExecuting [WILDCARD]
OK workerCanUseFetch [WILDCARD] OK workerCanUseFetch [WILDCARD]
test result: OK 4 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out [WILDCARD] test result: OK 4 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out [WILDCARD]