From c00025f2fd1e6d2d9848f4bb3e056b3fffb51024 Mon Sep 17 00:00:00 2001 From: Erick Sosa Garcia <ericksosagarcias@gmail.com> Date: Mon, 20 Jul 2020 16:14:56 -0400 Subject: [PATCH 1/5] add: more numerical assertion methods added --- README.md | 104 ++++++++++++++++----------- src/merlin.ts | 191 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 253 insertions(+), 42 deletions(-) diff --git a/README.md b/README.md index f7358b4..1560725 100644 --- a/README.md +++ b/README.md @@ -22,39 +22,30 @@ Merlin is a [Jest](https://jestjs.io/en/)-inspired testing framework for deno. ### All Matchers -- `testEqual(label: string, config)`_Compare two values and throws an error if the expect and toBe are not equal._ - -- `testNotEqual(label: string, config)`_Compare two values and throws an error if the expect and notBe are equal._ - -- `evalEquals(testEqual[])` _evaluate multiple equality tests in an array. If the data is not the same it throws an error._ - -- `fetchEqual(label: string, config)` _evaluate if two values are equal. If the request data is not the same as expected, it throws an error._ - -- `arrayContains(label: string, config)`_evaluates that the array contains an especific data. if the array does not contain the data it throws an error._ - -- `stringContains(label: string, config)`_evaluates if a string contains an especific word. if the string does not contain the word it throws an error._ - -- `beNull(label: string, config)` _evaluates if a data is null._ - -- `beFalsy(label: string, config)`_evaluates if a data is a falsy value._ - -- `beTruthy(label: string, config)`_evaluates if a data is a truthy value._ - -- `isBigInt(label: string, config)`_evaluates if a data is a bigInt value type._ - -- `isZero(label: string, config)`_evaluates if a data is a Zero_ - -- `isNaN(label: string, config)`_evaluates if a data is NaN value._ - -- `sameLength(label: string, config)`_evaluates if data has a especific length_ - -- `testRegExp(label: string, config)`_evaluates if a regular expression match_ - -- `isFunction(label: string, config)` _evaluates if a data is a function_ - -- `isSymbol(label: string, config)`_evaluates if a data is a symbol_ - -- `isUndefined(label: string, config)`_evaluates if a data is undefined_ +- `testEqual(label: string, config)` Compare two values and throws an error if the expect and toBe are not equal +- `testNotEqual(label: string, config)` Compare two values and throws an error if the expect and notBe are equal +- `evalEquals(testEqual[])` evaluate multiple equality tests in an array. If the data is not the same it throws an error +- `fetchEqual(label: string, config)` evaluate if two values are equal. If the request data is not the same as expected, it throws an error +- `arrayContains(label: string, config)` evaluates that the array contains an especific data. if the array does not contain the data it throws an error +- `stringContains(label: string, config)` evaluates if a string contains an especific word. if the string does not contain the word it throws an error +- `beNull(label: string, config)` evaluates if a data is null +- `beFalsy(label: string, config)` evaluates if a data is a falsy value +- `beTruthy(label: string, config)` evaluates if a data is a truthy value +- `isBigInt(label: string, config)` evaluates if a data is a bigInt value type +- `isZero(label: string, config)` evaluates if a data is a Zero +- `isNaN(label: string, config)` evaluates if a data is NaN value +- `sameLength(label: string, config)` evaluates if data has a especific length +- `testRegExp(label: string, config)` evaluates if a regular expression match +- `isFunction(label: string, config)` evaluates if a data is a function +- `isSymbol(label: string, config)` evaluates if a data is a symbol +- `isUndefined(label: string, config)` evaluates if a data is undefined +- `testSame(label: string, config)` +- `testGreaterOrEqual(label: string, config)` +- `testGreater(label: string, config)` +- `testLess(label: string, config)` +- `testLessOrEqual(label: string, config)` +- `testInstanceOf(label: string, config)` +- `testFloat(label: string, config)` ### Basic Use @@ -108,7 +99,7 @@ all assertions have parameters that they can receive, these parameters can chang - `Resources (optional)` receives a boolean, terminates all asynchronous processes that interact with the system. by default is `true`. - `only (optional)` receives a boolean, only tests that have `only in true` will be executed, the rest will not run. -## about resources and ops sanitizers +### about resources and ops sanitizers Certain actions in Deno create resources in the resource table . These resources should be closed after you are done using them. @@ -126,13 +117,14 @@ async function writeSomething(): Promise<string> { } test.testEqual("Leak resources test", { - expect:() => "test", - toBe:() =>writeSomething(), + expect: async () => await writeSomething(), + toBe: () => "test", only: true, Ops: false, - Resources: false + Resources: false, }); ``` + ```sh deno test @@ -140,6 +132,7 @@ test Leak resources test ... ok (5ms) test result: ok. 3 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out ``` + ### Multiple tests `example.test.ts` @@ -263,16 +256,43 @@ test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out ``` ### testRegExp + ```typescript -test.testRegExp("regEx match",{ - expect:()=> "https://google.com", - toBe:()=> new RegExp("^https?:\/\/[a-z.]+\.com$"), -}) +test.testRegExp("regEx match", { + expect: () => "https://google.com", + toBe: () => new RegExp("^https?://[a-z.]+.com$"), +}); ``` + ```sh deno test test regEx match ... ok (6ms) test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out (342ms) -``` \ No newline at end of file +``` + +### Usin async code. + +you can use asynchronous code by adding `async` in `expect`, `toBe` and `value` functions. + +example + +```typescript +const test = new Merlin(); + +test.testEqual("get error 404", { + async expect() { + const response = await fetch("https://deno.land/std/example/examples.ts"); + + const data = response.text(); + + return data; + }, + toBe() { + return "404: Not Found"; + }, +}); +``` + +> **Note**: all the methods of the merlin class support asyn function since they have top level await diff --git a/src/merlin.ts b/src/merlin.ts index e2ee9f2..7da8a56 100644 --- a/src/merlin.ts +++ b/src/merlin.ts @@ -455,4 +455,195 @@ export class Merlin { sanitizeResources: Resources, }); } + + /** + * expect the two values to be strictly equal + */ + public testSame( + label: string, + { + expect, + toBe, + Ops = true, + Resources = true, + ignore, + message, + only, + }: testConfig + ) { + this.Test({ + name: label, + fn: async () => { + assertStrictEquals(await expect(), await toBe(), message); + }, + ignore, + only, + sanitizeOps: Ops, + sanitizeResources: Resources, + }); + } + + /** + * expect the expected value to be greater than or equal + */ + public testGreaterOrEqual( + label: string, + { + expect, + toBe, + Ops = true, + Resources = true, + ignore, + message, + only, + }: testConfig + ) { + this.Test({ + name: label, + fn: async () => { + assert((await expect()) >= (await toBe()), message); + }, + ignore, + only, + sanitizeOps: Ops, + sanitizeResources: Resources, + }); + } + + /** + * expect the expected value to be greater + */ + public testGreater( + label: string, + { + expect, + toBe, + Ops = true, + Resources = true, + ignore, + message, + only, + }: testConfig + ) { + this.Test({ + name: label, + fn: async () => { + assert((await expect()) > (await toBe()), message); + }, + ignore, + only, + sanitizeOps: Ops, + sanitizeResources: Resources, + }); + } + + /** + * expect the expected value to be less. + */ + public testLess( + label: string, + { + expect, + toBe, + Ops = true, + Resources = true, + ignore, + message, + only, + }: testConfig + ) { + this.Test({ + name: label, + fn: async () => { + assert((await expect()) < (await toBe()), message); + }, + ignore, + only, + sanitizeOps: Ops, + sanitizeResources: Resources, + }); + } + + /** + * expect the expected value to be less or equal. + */ + public testLessOrEqual( + label: string, + { + expect, + toBe, + Ops = true, + Resources = true, + ignore, + message, + only, + }: testConfig + ) { + this.Test({ + name: label, + fn: async () => { + assert((await expect()) <= (await toBe()), message); + }, + ignore, + only, + sanitizeOps: Ops, + sanitizeResources: Resources, + }); + } + + /** + * expect both values to be instances of the same class + */ + public testInstanceOf( + label: string, + { + expect, + toBe, + Ops = true, + Resources = true, + ignore, + message, + only, + }: testConfig + ) { + this.Test({ + name: label, + fn: async () => { + assert((await expect()) instanceof (await toBe()), message); + }, + ignore, + only, + sanitizeOps: Ops, + sanitizeResources: Resources, + }); + } + + public testFloat( + label: string, + { + expect, + toBe, + Ops = true, + Resources = true, + ignore, + message, + only, + strict, + }: testConfig + ) { + this.Test({ + name: label, + fn: async () => { + if (strict) { + assertStrictEquals(await expect(), await toBe(), message); + } else { + assertEquals(await expect(), await toBe(), message); + } + }, + ignore, + only, + sanitizeOps: Ops, + sanitizeResources: Resources, + }); + } } From 9c03f1689dfdf36ae9db3319f2f350d69c1abad6 Mon Sep 17 00:00:00 2001 From: Erick Sosa Garcia <ericksosagarcias@gmail.com> Date: Tue, 21 Jul 2020 13:13:15 -0400 Subject: [PATCH 2/5] add: pretty logs in benchmarks --- deps.ts | 1 + src/maven.ts | 15 ++++++++++++++- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/deps.ts b/deps.ts index f2daf12..c2f8df8 100644 --- a/deps.ts +++ b/deps.ts @@ -6,5 +6,6 @@ * */ +export * from "https://deno.land/x/pretty_benching@v0.1.2/mod.ts"; export * from "https://deno.land/std/testing/asserts.ts"; export * from "https://deno.land/std/testing/bench.ts"; diff --git a/src/maven.ts b/src/maven.ts index 10dd17e..91358e7 100644 --- a/src/maven.ts +++ b/src/maven.ts @@ -11,6 +11,8 @@ import { runBenchmarks, BenchmarkResult, BenchmarkRunOptions, + prettyBenchmarkProgress, + prettyBenchmarkResult, } from "../deps.ts"; interface Bench { @@ -40,10 +42,21 @@ export class Maven { public async runBench(config?: BenchmarkRunOptions) { this.config = config as BenchmarkRunOptions; - return runBenchmarks(config); + return runBenchmarks(config, prettyBenchmarkProgress()); } public async success() { return await this.runBench(this.config); } + + public Result(graphBars = 5) { + return prettyBenchmarkResult({ + parts: { + extraMetrics: true, + graph: true, + threshold: true, + graphBars, + }, + }); + } } From 997c822be21cb5e5875a2596cb99dea5de2c4b40 Mon Sep 17 00:00:00 2001 From: Erick Sosa Garcia <ericksosagarcias@gmail.com> Date: Tue, 21 Jul 2020 15:50:12 -0400 Subject: [PATCH 3/5] add: cli tool for maven and merlin --- cli.ts | 37 +++++++++++++++++++++++++++++++++++++ deps.ts | 2 +- src/maven.ts | 33 +++++++++++++++++++++++++++++++-- 3 files changed, 69 insertions(+), 3 deletions(-) create mode 100644 cli.ts diff --git a/cli.ts b/cli.ts new file mode 100644 index 0000000..68b4da1 --- /dev/null +++ b/cli.ts @@ -0,0 +1,37 @@ +/** + * Copyright (c) Crew Dev. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + */ + +import { colors } from "./deps.ts"; + +async function main() { + const [command, ...args] = Deno.args; + + if (command === "start") { + const process = Deno.run({ + cmd: ["deno", "test", "--allow-hrtime", ...args], + }); + + if ((await process.status()).success) { + Deno.close(process.rid); + } else { + Deno.close(process.rid); + throw new Error(colors.red("something went wrong trying to run the test")) + .message; + } + } else if (command === "help") { + console.log("help info..."); + } else { + throw new Error( + colors.red("this command was not found, try run: merlin help") + ).message; + } +} + +if (import.meta.main) { + await main(); +} diff --git a/deps.ts b/deps.ts index c2f8df8..298e3da 100644 --- a/deps.ts +++ b/deps.ts @@ -5,7 +5,7 @@ * LICENSE file in the root directory of this source tree. * */ - export * from "https://deno.land/x/pretty_benching@v0.1.2/mod.ts"; +export * as colors from "https://deno.land/std/fmt/colors.ts"; export * from "https://deno.land/std/testing/asserts.ts"; export * from "https://deno.land/std/testing/bench.ts"; diff --git a/src/maven.ts b/src/maven.ts index 91358e7..8eaeec8 100644 --- a/src/maven.ts +++ b/src/maven.ts @@ -13,22 +13,43 @@ import { BenchmarkRunOptions, prettyBenchmarkProgress, prettyBenchmarkResult, + colors, } from "../deps.ts"; - interface Bench { name: string; steps?: number; fn: Function; } +interface Thresholds { + [key: string]: { green: number; yellow: number }; +} + export type BenchResult = BenchmarkResult; export class Maven { private bench = bench; + private thresholds: Thresholds = {}; + private config: BenchmarkRunOptions = {}; + private indicators = [ + { + benches: /./, + tableColor: colors.cyan, + modFn: () => "🧪", + }, + ]; + + private runIndicator = [{ benches: /./, modFn: () => colors.green(" ==> ") }]; + + private addThreasholds(name: string) { + this.thresholds[name] = { green: 70, yellow: 90 }; + } + public Bench({ fn, name, steps = 1 }: Bench) { + this.addThreasholds(name); this.bench({ name, func(bench) { @@ -42,7 +63,13 @@ export class Maven { public async runBench(config?: BenchmarkRunOptions) { this.config = config as BenchmarkRunOptions; - return runBenchmarks(config, prettyBenchmarkProgress()); + return runBenchmarks( + { silent: true, ...config }, + prettyBenchmarkProgress({ + indicators: this.runIndicator, + thresholds: this.thresholds, + }) + ); } public async success() { @@ -51,6 +78,8 @@ export class Maven { public Result(graphBars = 5) { return prettyBenchmarkResult({ + thresholds: this.thresholds, + indicators: this.indicators, parts: { extraMetrics: true, graph: true, From d78db54ea3a736934ca3588f7625bea7bdaba7b9 Mon Sep 17 00:00:00 2001 From: Erick Sosa Garcia <ericksosagarcias@gmail.com> Date: Tue, 21 Jul 2020 19:07:34 -0400 Subject: [PATCH 4/5] update: more description information --- README.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 1560725..72e2beb 100644 --- a/README.md +++ b/README.md @@ -25,7 +25,7 @@ Merlin is a [Jest](https://jestjs.io/en/)-inspired testing framework for deno. - `testEqual(label: string, config)` Compare two values and throws an error if the expect and toBe are not equal - `testNotEqual(label: string, config)` Compare two values and throws an error if the expect and notBe are equal - `evalEquals(testEqual[])` evaluate multiple equality tests in an array. If the data is not the same it throws an error -- `fetchEqual(label: string, config)` evaluate if two values are equal. If the request data is not the same as expected, it throws an error +- `fetchEqual(label: string, config)` evaluate if two values are equal. If the request data is not the same as expected, it throws an error - `arrayContains(label: string, config)` evaluates that the array contains an especific data. if the array does not contain the data it throws an error - `stringContains(label: string, config)` evaluates if a string contains an especific word. if the string does not contain the word it throws an error - `beNull(label: string, config)` evaluates if a data is null @@ -39,13 +39,13 @@ Merlin is a [Jest](https://jestjs.io/en/)-inspired testing framework for deno. - `isFunction(label: string, config)` evaluates if a data is a function - `isSymbol(label: string, config)` evaluates if a data is a symbol - `isUndefined(label: string, config)` evaluates if a data is undefined -- `testSame(label: string, config)` -- `testGreaterOrEqual(label: string, config)` -- `testGreater(label: string, config)` -- `testLess(label: string, config)` -- `testLessOrEqual(label: string, config)` -- `testInstanceOf(label: string, config)` -- `testFloat(label: string, config)` +- `testSame(label: string, config)` evaluates if two values are strictly the same +- `testGreaterOrEqual(label: string, config)` evaluates whether the expected data is greater than or equal to another +- `testGreater(label: string, config)` evaluates whether the expected data is greater than another +- `testLess(label: string, config)` evaluates if the expected data is less than another +- `testLessOrEqual(label: string, config)` evaluates if the expected data is less than or equal to another +- `testInstanceOf(label: string, config)` evaluates that one object is an instance of another +- `testFloat(label: string, config)` evaluates if two decimal numbers are equal ### Basic Use From bc4aed7ce73df6da75f3fc061a9a9ceeef30caa7 Mon Sep 17 00:00:00 2001 From: Erick Sosa Garcia <ericksosagarcias@gmail.com> Date: Tue, 21 Jul 2020 19:51:16 -0400 Subject: [PATCH 5/5] add: Maven use information --- README.md | 77 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 77 insertions(+) diff --git a/README.md b/README.md index 72e2beb..7f97764 100644 --- a/README.md +++ b/README.md @@ -296,3 +296,80 @@ test.testEqual("get error 404", { ``` > **Note**: all the methods of the merlin class support asyn function since they have top level await + +## Create benchmarks using Maven + +Maven is a benchmark tool for deno included in Merlin. + +It's easy to use. `example`: + +```typescript +import { Maven } from "https://deno.land/x/merlin/mod.ts"; + +const benchmark = new Maven(); + +benchmark.Bench({ + name: "Sorting arrays", + fn: () => { + new Array(10000).fill(Math.random()).sort(); + }, + steps: 1000, +}); + +benchmark.runBench(); +``` + +this is the terminal output + +![gif](https://cdn.discordapp.com/attachments/656976424778989602/735278025742221324/ezgif.com-video-to-gif.gif) + +### Parameters + +maven receives the following parameters. + +- `name: string` benchmark name +- `fn(): void` function that contains the code +- `steps: number` number of times to repeat the benchmark + +you can see the details at the end of the benchmark using + +```typescript +import { Maven } from "https://deno.land/x/merlin/mod.ts"; + +const benchmark = new Maven(); + +benchmark.Bench({ + name: "Sorting arrays", + fn: () => { + new Array(10000).fill(Math.random()).sort(); + }, + steps: 1000, +}); + +benchmark.runBench().then(benchmark.Result()); +``` + +It has a table with the detailed values + +```sh +▒▒▒▒▒▒▒▒ Benchmarking finished + +┌───────────────────────────────────────────────────────────────────────────────────────────┐ +│ 🧪 Benchmark name: Sorting arrays │ +├───────────────────────┬──────────────────────────────┬────────────────────────────────────┤ +│ Total runs: 1000 │ Total time: 1099.6591 ms │ Avg time: 1.0997 ms │ +├───────────────────────┼────────────────────┬─────────┴───────────┬────────────────────────┤ +│ min: 0.7768 ms │ max: 9.9867 ms │ mean: 5.3817 ms │ median: 0.8511 ms │ +├───────────────────────┴────────────────────┴─────────────────────┴────────────────────────┤ +│ Thresholds: 0 ========== 70 ========== 90 ========== ∞ │ +├───────────────────────────────┬───────────────────────────────────────────────────────────┤ +│ │ │ +│ 0.7768 ms _[ 965][96.5%] │======================================================== │ +│ 2.6188 ms _[ 33][ 3.3%] │== │ +│ 4.4608 ms _[ 1][ 0.1%] │= │ +│ 6.3027 ms _[ 0][ 0%] │ │ +│ 8.1447 ms _[ 1][ 0.1%] │= │ +│ │ │ +└───────────────────────────────┴───────────────────────────────────────────────────────────┘ + +```