diff --git a/cli/js/lib.deno.ns.d.ts b/cli/js/lib.deno.ns.d.ts index daae8b09d7..2c3a9aef4f 100644 --- a/cli/js/lib.deno.ns.d.ts +++ b/cli/js/lib.deno.ns.d.ts @@ -17,6 +17,7 @@ declare namespace Deno { export interface TestDefinition { fn: TestFunction; name: string; + skip?: boolean; } /** 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 */ export function test(name: string, fn: TestFunction): void; + enum TestStatus { + Passed = "passed", + Failed = "failed", + Skipped = "skipped" + } + interface TestResult { - passed: boolean; name: string; - skipped: boolean; - hasRun: boolean; - duration: number; + status: TestStatus; + duration?: number; error?: Error; } diff --git a/cli/js/testing.ts b/cli/js/testing.ts index a2944aff41..a4523d2b72 100644 --- a/cli/js/testing.ts +++ b/cli/js/testing.ts @@ -1,10 +1,11 @@ // 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 { Console } from "./web/console.ts"; const RED_FAILED = red("FAILED"); const GREEN_OK = green("OK"); +const YELLOW_SKIPPED = yellow("SKIPPED"); const RED_BG_FAIL = bgRed(" FAIL "); const disabledConsole = new Console((_x: string, _isErr?: boolean): void => {}); @@ -18,6 +19,7 @@ export type TestFunction = () => void | Promise; export interface TestDefinition { fn: TestFunction; name: string; + skip?: boolean; } const TEST_REGISTRY: TestDefinition[] = []; @@ -31,34 +33,32 @@ export function test( t: string | TestDefinition | TestFunction, fn?: TestFunction ): void { - let name: string; + let testDef: TestDefinition; if (typeof t === "string") { - if (!fn) { - throw new Error("Missing test function"); + if (!fn || typeof fn != "function") { + throw new TypeError("Missing test function"); } - name = t; - if (!name) { - throw new Error("The name of test case can't be empty"); + if (!t) { + throw new TypeError("The test name can't be empty"); } + testDef = { fn: fn as TestFunction, name: t, skip: false }; } else if (typeof t === "function") { - fn = t; - name = t.name; - if (!name) { - throw new Error("Test function can't be anonymous"); + if (!t.name) { + throw new TypeError("The test function can't be anonymous"); } + testDef = { fn: t, name: t.name, skip: false }; } else { - fn = t.fn; - if (!fn) { - throw new Error("Missing test function"); + if (!t.fn) { + throw new TypeError("Missing test function"); } - name = t.name; - if (!name) { - throw new Error("The name of test case can't be empty"); + if (!t.name) { + throw new TypeError("The test name 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 { @@ -78,18 +78,17 @@ export interface RunTestsOptions { reporter?: TestReporter; } -interface TestResult { - passed: boolean; - name: string; - skipped: boolean; - hasRun: boolean; - duration: number; - error?: Error; +enum TestStatus { + Passed = "passed", + Failed = "failed", + Skipped = "skipped" } -interface TestCase { - result: TestResult; - fn: TestFunction; +interface TestResult { + name: string; + status: TestStatus; + duration?: number; + error?: Error; } export enum TestEvent { @@ -115,24 +114,10 @@ interface TestEventEnd { 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, but add as "implements to class" // TODO: implements PromiseLike class TestApi { readonly testsToRun: TestDefinition[]; - readonly testCases: TestCase[]; readonly stats: TestStats = { filtered: 0, ignored: 0, @@ -148,7 +133,6 @@ class TestApi { ) { this.testsToRun = tests.filter(filterFn); this.stats.filtered = tests.length - this.testsToRun.length; - this.testCases = this.testsToRun.map(testDefinitionToTestCase); } async *[Symbol.asyncIterator](): AsyncIterator< @@ -159,32 +143,35 @@ class TestApi { tests: this.testsToRun.length }; + const results: TestResult[] = []; const suiteStart = +new Date(); - for (const testCase of this.testCases) { - const { fn, result } = testCase; - let shouldBreak = false; - try { + for (const { name, fn, skip } of this.testsToRun) { + const result: Partial = { name }; + if (skip) { + result.status = TestStatus.Skipped; + this.stats.ignored++; + } else { const start = +new Date(); - await fn(); - result.duration = +new Date() - start; - result.passed = true; - this.stats.passed++; - } catch (err) { - result.passed = false; - result.error = err; - this.stats.failed++; - shouldBreak = this.failFast; - } finally { - result.hasRun = true; - yield { kind: TestEvent.Result, result }; - if (shouldBreak) { - break; + try { + await fn(); + result.duration = +new Date() - start; + result.status = TestStatus.Passed; + this.stats.passed++; + } catch (err) { + result.duration = +new Date() - start; + result.status = TestStatus.Failed; + result.error = err; + this.stats.failed++; } } + 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 results = this.testCases.map(r => r.result); yield { kind: TestEvent.End, @@ -241,13 +228,21 @@ export class ConsoleTestReporter implements TestReporter { async result(event: TestEventResult): Promise { const { result } = event; - if (result.passed) { - this.console.log( - `${GREEN_OK} ${result.name} ${formatDuration(result.duration)}` - ); - } else { - this.console.log(`${RED_FAILED} ${result.name}`); - this.console.log(result.error!); + switch (result.status) { + case TestStatus.Passed: + this.console.log( + `${GREEN_OK} ${result.name} ${formatDuration(result.duration!)}` + ); + break; + 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; } } diff --git a/cli/js/tests/testing_test.ts b/cli/js/tests/testing_test.ts index b47eb03e27..9ed89f5323 100644 --- a/cli/js/tests/testing_test.ts +++ b/cli/js/tests/testing_test.ts @@ -11,8 +11,8 @@ unitTest(function nameOfTestCaseCantBeEmpty(): void { () => { Deno.test("", () => {}); }, - Error, - "The name of test case can't be empty" + TypeError, + "The test name can't be empty" ); assertThrows( () => { @@ -21,8 +21,8 @@ unitTest(function nameOfTestCaseCantBeEmpty(): void { fn: () => {} }); }, - Error, - "The name of test case can't be empty" + TypeError, + "The test name can't be empty" ); }); @@ -31,7 +31,7 @@ unitTest(function testFnCantBeAnonymous(): void { () => { Deno.test(function() {}); }, - Error, - "Test function can't be anonymous" + TypeError, + "The test function can't be anonymous" ); }); diff --git a/cli/tests/057_revoke_permissions.out b/cli/tests/057_revoke_permissions.out index 1f12d3b93a..5ea2b9ecbd 100644 --- a/cli/tests/057_revoke_permissions.out +++ b/cli/tests/057_revoke_permissions.out @@ -1,10 +1,10 @@ running 7 tests -OK runGranted [WILDCARD] -OK readGranted [WILDCARD] -OK writeGranted [WILDCARD] -OK netGranted [WILDCARD] -OK envGranted [WILDCARD] -OK pluginGranted [WILDCARD] -OK hrtimeGranted [WILDCARD] +OK runGranted [WILDCARD] +OK readGranted [WILDCARD] +OK writeGranted [WILDCARD] +OK netGranted [WILDCARD] +OK envGranted [WILDCARD] +OK pluginGranted [WILDCARD] +OK hrtimeGranted [WILDCARD] -test result: OK 7 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out [WILDCARD] \ No newline at end of file +test result: OK 7 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out [WILDCARD] diff --git a/cli/tests/compiler_api_test.out b/cli/tests/compiler_api_test.out index 23f44b092d..9ab6536a20 100644 --- a/cli/tests/compiler_api_test.out +++ b/cli/tests/compiler_api_test.out @@ -1,16 +1,15 @@ running 12 tests -OK compilerApiCompileSources [WILDCARD] -OK compilerApiCompileNoSources [WILDCARD] -OK compilerApiCompileOptions [WILDCARD] -OK compilerApiCompileLib [WILDCARD] -OK compilerApiCompileTypes [WILDCARD] -OK transpileOnlyApi [WILDCARD] -OK transpileOnlyApiConfig [WILDCARD] -OK bundleApiSources [WILDCARD] -OK bundleApiNoSources [WILDCARD] -OK bundleApiConfig [WILDCARD] -OK bundleApiJsModules [WILDCARD] -OK diagnosticsTest [WILDCARD] +OK compilerApiCompileSources [WILDCARD] +OK compilerApiCompileNoSources [WILDCARD] +OK compilerApiCompileOptions [WILDCARD] +OK compilerApiCompileLib [WILDCARD] +OK compilerApiCompileTypes [WILDCARD] +OK transpileOnlyApi [WILDCARD] +OK transpileOnlyApiConfig [WILDCARD] +OK bundleApiSources [WILDCARD] +OK bundleApiNoSources [WILDCARD] +OK bundleApiConfig [WILDCARD] +OK bundleApiJsModules [WILDCARD] +OK diagnosticsTest [WILDCARD] test result: OK 12 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out [WILDCARD] - diff --git a/cli/tests/workers_test.out b/cli/tests/workers_test.out index 691301f810..1238ca70aa 100644 --- a/cli/tests/workers_test.out +++ b/cli/tests/workers_test.out @@ -1,7 +1,7 @@ running 4 tests -OK workersBasic [WILDCARD] -OK nestedWorker [WILDCARD] -OK workerThrowsWhenExecuting [WILDCARD] -OK workerCanUseFetch [WILDCARD] +OK workersBasic [WILDCARD] +OK nestedWorker [WILDCARD] +OK workerThrowsWhenExecuting [WILDCARD] +OK workerCanUseFetch [WILDCARD] -test result: OK 4 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out [WILDCARD] \ No newline at end of file +test result: OK 4 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out [WILDCARD]