From d7284078cd4dd2b3a03d9e52c72cce9efc5f2be1 Mon Sep 17 00:00:00 2001 From: Eric Crooks Date: Thu, 20 Oct 2022 19:16:08 -0400 Subject: [PATCH 01/22] fix(deno): type-checked tests --- mod.ts | 2 +- package.json | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/mod.ts b/mod.ts index 006924d2..a5514f08 100644 --- a/mod.ts +++ b/mod.ts @@ -228,7 +228,7 @@ export function Stub( // the value of `returnValue`. if (typeof obj === "object" && dataMember !== undefined) { // If we are stubbing a method, then make sure the method is still callable - if (typeof obj[dataMember] === "function") { + if (typeof obj![dataMember] === "function") { Object.defineProperty(obj, dataMember, { value: () => returnValue !== undefined ? returnValue : "stubbed", writable: true, diff --git a/package.json b/package.json index 2b6cfeae..981773ae 100644 --- a/package.json +++ b/package.json @@ -15,7 +15,8 @@ "build:windows": "bash console/build_esm_lib && yarn build:cjs && yarn build:esm", "check": "rm -rf node_modules/ && rm yarn.lock && yarn install && yarn build && yarn test", "release": "yarn publish --access public", - "test": "jest" + "test:deno": "deno test --check -A tests/deno", + "test:node": "jest" }, "devDependencies": { "@babel/preset-env": "7.x", From 612499a120d2aaca5062cd6fda80ca88a07fa815 Mon Sep 17 00:00:00 2001 From: Eric Crooks Date: Fri, 21 Oct 2022 01:38:08 -0400 Subject: [PATCH 02/22] feat(fake/mock): allow pre-programming methods with args --- package.json | 3 +- src/fake/fake_builder.ts | 9 +- src/fake/fake_mixin.ts | 26 ++++- src/interfaces.ts | 33 ++++-- src/mock/mock_builder.ts | 9 +- src/mock/mock_mixin.ts | 26 ++++- src/pre_programmed_method.ts | 174 ++++++++++++++++++++++++----- tests/deno/unit/mod/fake_test.ts | 186 +++++++++++++++++++++++++++---- tests/deno/unit/mod/mock_test.ts | 139 +++++++++++++++++++++++ 9 files changed, 531 insertions(+), 74 deletions(-) diff --git a/package.json b/package.json index 981773ae..2b6cfeae 100644 --- a/package.json +++ b/package.json @@ -15,8 +15,7 @@ "build:windows": "bash console/build_esm_lib && yarn build:cjs && yarn build:esm", "check": "rm -rf node_modules/ && rm yarn.lock && yarn install && yarn build && yarn test", "release": "yarn publish --access public", - "test:deno": "deno test --check -A tests/deno", - "test:node": "jest" + "test": "jest" }, "devDependencies": { "@babel/preset-env": "7.x", diff --git a/src/fake/fake_builder.ts b/src/fake/fake_builder.ts index 2e234aa0..b15e712f 100644 --- a/src/fake/fake_builder.ts +++ b/src/fake/fake_builder.ts @@ -104,10 +104,13 @@ export class FakeBuilder extends TestDoubleBuilder { // something. If it was, then we make sure that this method we are // currently defining returns that pre-programmed value. if (methodToCall instanceof PreProgrammedMethod) { - if (methodToCall.will_throw) { - throw methodToCall.error; + const methodSetup = methodToCall.findSetupByArgs(args); + + if (methodSetup?.will_throw) { + methodSetup.throw(); } - return methodToCall.return; + + return methodSetup?.return(); } // When method calls its original self, let the `this` context of the diff --git a/src/fake/fake_mixin.ts b/src/fake/fake_mixin.ts index d722be4e..bdc9552e 100644 --- a/src/fake/fake_mixin.ts +++ b/src/fake/fake_mixin.ts @@ -1,4 +1,4 @@ -import type { IFake } from "../interfaces.ts"; +import type { IFake, IMethodChanger } from "../interfaces.ts"; import type { Constructor, MethodOf } from "../types.ts"; import { FakeError } from "../errors.ts"; @@ -30,6 +30,14 @@ export function createFake( */ #original!: OriginalObject; + /** + * Map of pre-programmed methods defined by the user. + */ + #pre_programmed_methods: Map< + MethodOf, + IMethodChanger + > = new Map(); + //////////////////////////////////////////////////////////////////////////// // FILE MARKER - CONSTRUCTOR /////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////// @@ -57,7 +65,13 @@ export function createFake( */ public method( methodName: MethodOf, - ): PreProgrammedMethod { + ): IMethodChanger { + // If the method was already pre-programmed previously, then return it so + // the user can add more method setups to it + if (this.#pre_programmed_methods.has(methodName)) { + return this.#pre_programmed_methods.get(methodName)!; + } + const methodConfiguration = new PreProgrammedMethod< OriginalObject, ReturnValueType @@ -78,7 +92,13 @@ export function createFake( writable: true, }); - return methodConfiguration; + const methodChanger = methodConfiguration as unknown as IMethodChanger< + ReturnValueType + >; + + this.#pre_programmed_methods.set(methodName, methodChanger); + + return methodChanger; } }(); } diff --git a/src/interfaces.ts b/src/interfaces.ts index 8c400583..832ad1e8 100644 --- a/src/interfaces.ts +++ b/src/interfaces.ts @@ -36,9 +36,9 @@ export interface IFake { * * @param method - The name of the method to shorten. */ - method( + method( method: MethodOf, - ): IMethodChanger; + ): IMethodChanger; } //////////////////////////////////////////////////////////////////////////////// @@ -99,13 +99,13 @@ export interface IMethodExpectation { // FILE MARKER - IMETHODCHANGER //////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// -export interface IMethodChanger { +export interface IMethodChanger { /** * Make the given method return the given `returnValue`. * * @param returnValue - The value to make the method return. */ - willReturn(returnValue: T): void; + willReturn(returnValue: T): IMethodSetup; /** * Make the given method throw the given error. @@ -113,7 +113,26 @@ export interface IMethodChanger { * @param error - An error which extends the `Error` class or has the same * interface as the `Error` class. */ - willThrow(error: IError & T): void; + willThrow(error: IError & T): IMethodSetup; + + withArgs(...args: unknown[]): IMethodSetup; +} + +export interface IMethodSetup { + /** + * Make the given method return the given `returnValue`. + * + * @param returnValue - The value to make the method return. + */ + willReturn(returnValue: ReturnValue): IMethodSetup; + + /** + * Make the given method throw the given error. + * + * @param error - An error which extends the `Error` class or has the same + * interface as the `Error` class. + */ + willThrow(error: IError): IMethodSetup; } //////////////////////////////////////////////////////////////////////////////// @@ -167,9 +186,9 @@ export interface IMock { * * @param method - The name of the method to pre-program. */ - method( + method( method: MethodOf, - ): IMethodChanger; + ): IMethodChanger; /** * Verify that all expectations from the `.expects()` calls. diff --git a/src/mock/mock_builder.ts b/src/mock/mock_builder.ts index 832a6d96..6a184fa7 100644 --- a/src/mock/mock_builder.ts +++ b/src/mock/mock_builder.ts @@ -112,10 +112,13 @@ export class MockBuilder extends TestDoubleBuilder { // something. If it was, then we make sure that this method we are // currently defining returns that pre-programmed value. if (methodToCall instanceof PreProgrammedMethod) { - if (methodToCall.will_throw) { - throw methodToCall.error; + const methodSetup = methodToCall.findSetupByArgs(args); + + if (methodSetup?.will_throw) { + throw methodSetup.throw(); } - return methodToCall.return; + + return methodSetup?.return(); } // When method calls its original self, let the `this` context of the diff --git a/src/mock/mock_mixin.ts b/src/mock/mock_mixin.ts index e0c0fed5..fb84cc75 100644 --- a/src/mock/mock_mixin.ts +++ b/src/mock/mock_mixin.ts @@ -1,4 +1,4 @@ -import type { IMock } from "../interfaces.ts"; +import type { IMethodChanger, IMock } from "../interfaces.ts"; import type { Constructor, MethodOf, MockMethodCalls } from "../types.ts"; import { MethodVerifier } from "../verifiers/method_verifier.ts"; @@ -129,6 +129,14 @@ export function createMock( */ #original!: OriginalObject; + /** + * Map of pre-programmed methods defined by the user. + */ + #pre_programmed_methods: Map< + MethodOf, + IMethodChanger + > = new Map(); + //////////////////////////////////////////////////////////////////////////// // FILE MARKER - CONSTRUCTOR /////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////// @@ -174,7 +182,13 @@ export function createMock( */ public method( methodName: MethodOf, - ): PreProgrammedMethod { + ): IMethodChanger { + // If the method was already pre-programmed previously, then return it so + // the user can add more method setups to it + if (this.#pre_programmed_methods.has(methodName)) { + return this.#pre_programmed_methods.get(methodName)!; + } + const methodConfiguration = new PreProgrammedMethod< OriginalObject, ReturnValueType @@ -194,7 +208,13 @@ export function createMock( writable: true, }); - return methodConfiguration; + const methodChanger = methodConfiguration as unknown as IMethodChanger< + ReturnValueType + >; + + this.#pre_programmed_methods.set(methodName, methodChanger); + + return methodChanger; } /** diff --git a/src/pre_programmed_method.ts b/src/pre_programmed_method.ts index 86fd81bd..dfe3c82c 100644 --- a/src/pre_programmed_method.ts +++ b/src/pre_programmed_method.ts @@ -10,11 +10,20 @@ class PreProgrammedMethodError extends Error {} * pre-programmed methods. */ export class PreProgrammedMethod { + /** + * Pre-programmed methods can have multiple setups. For example, a method can + * be pre-programmed to return or throw when it receives a specific set of + * arguments. Each setup is stored here. Setups are either created or updated. + * For example, if a setup for `someMethod("hello")` was not created yet, then + * it will be created; and if a setup for `someMethod("hello")` was already + * created and being set up again, then it will be updated. + */ + #setups: Map> = new Map(); + + /** + * The original name of the method being pre-programmed. + */ #method_name: MethodOf; - #will_throw = false; - #will_return = false; - #return?: ReturnValue; - #error?: IError; ////////////////////////////////////////////////////////////////////////////// // FILE MARKER - CONSTRUCTOR ///////////////////////////////////////////////// @@ -26,30 +35,132 @@ export class PreProgrammedMethod { */ constructor(methodName: MethodOf) { this.#method_name = methodName; + + // Add a method setup that takes no args for this pre-programmed method. + // Users are required to finish setting up this pre-programmed method. If + // the method is not finished being pre-programmed, then the + // `PreProgrammedMethodError` will be thrown. + this.#setups.set("[]", new MethodSetup(this.#method_name)); } ////////////////////////////////////////////////////////////////////////////// - // FILE MARKER - GETTERS / SETTERS ////////////////////////////////////////// + // FILE MARKER - METHODS - PUBLIC /////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// - get error(): void { - const typeSafeMethodName = String(this.#method_name); + /** + * Find the given method setup with the given arguments. + * @param args The arguments to use to query the method setup map. + * @returns A `MethodSetup` instance that holds the pre-programmed return + * value or error to throw. + */ + public findSetupByArgs( + args: unknown, + ): MethodSetup | undefined { + return this.#setups.get(JSON.stringify(args)); + } - if (!this.#will_throw) { - throw new PreProgrammedMethodError( - `Pre-programmed method "${typeSafeMethodName}" is not set up to throw an error.`, - ); - } - if (this.#error === undefined) { - throw new PreProgrammedMethodError( - `Pre-programmed method "${typeSafeMethodName}" is set up to throw an error, but no error was provided.`, - ); - } + /** + * Create a method setup with a specific set of arguments. + * @param args The arguments to use for this setup. When this pre-programmed + * method is called with these arguments, it will return or throw the + * defined return value or error associated with these arguments. + * @returns A `MethodSetup` instance that can further configure this + * pre-programmed method to return a value or throw an error. + */ + public withArgs( + ...args: unknown[] + ): MethodSetup { + const methodSetup = new MethodSetup( + this.#method_name, + ); + methodSetup.setArgs(args); - throw this.#error; + this.#setups.set(methodSetup.args_as_string, methodSetup); + + return methodSetup; + } + + /** + * Pre-program this method to return the given value. + * + * @param returnValue The value that should be returned when this object is + * being used in place of an original method. + */ + public willReturn( + returnValue: ReturnValue, + ): MethodSetup { + const methodSetup = new MethodSetup( + this.#method_name, + ); + methodSetup.setReturnValue(returnValue); + + this.#setups.set(methodSetup.args_as_string, methodSetup); + + return methodSetup; } - get return(): ReturnValue { + /** + * Pre-program this method to throw the given error. + * + * @param error - The error to throw. + */ + public willThrow(error: IError): MethodSetup { + const methodSetup = new MethodSetup( + this.#method_name, + ); + methodSetup.setErrorToThrow(error); + + this.#setups.set(methodSetup.args_as_string, methodSetup); + + return methodSetup; + } +} + +/** + * Class to hold information about a specific pre-programmed method setup. + */ +class MethodSetup { + #args?: unknown[] = []; + #error?: IError; + #method_name: MethodOf; + #return?: ReturnValue; + #will_return = false; + #will_throw = false; + + constructor(methodName: MethodOf) { + this.#method_name = methodName; + } + + get args_as_string() { + return JSON.stringify(this.#args); + } + + get will_throw() { + return this.#will_throw; + } + + get will_return() { + return this.#will_return; + } + + setArgs(args: unknown[]): this { + this.#args = args ?? []; + return this; + } + + setReturnValue(returnValue: ReturnValue): this { + this.#will_return = true; + this.#return = returnValue; + return this; + } + + setErrorToThrow(error: IError): this { + this.#will_throw = true; + this.#error = error; + return this; + } + + return(): ReturnValue { if (this.#return === undefined) { const typeSafeMethodName = String(this.#method_name); @@ -61,21 +172,26 @@ export class PreProgrammedMethod { return this.#return; } - get will_return(): boolean { - return this.#will_return; - } + throw(): void { + const typeSafeMethodName = String(this.#method_name); - get will_throw(): boolean { - return this.#will_throw; - } + if (!this.#will_throw) { + throw new PreProgrammedMethodError( + `Pre-programmed method "${typeSafeMethodName}" is not set up to throw an error.`, + ); + } + if (this.#error === undefined) { + throw new PreProgrammedMethodError( + `Pre-programmed method "${typeSafeMethodName}" is set up to throw an error, but no error was provided.`, + ); + } - ////////////////////////////////////////////////////////////////////////////// - // FILE MARKER - METHODS - PUBLIC /////////////////////////////////////////// - ////////////////////////////////////////////////////////////////////////////// + throw this.#error; + } /** * Pre-program this method to return the given value. - + * * @param returnValue The value that should be returned when this object is * being used in place of an original method. */ diff --git a/tests/deno/unit/mod/fake_test.ts b/tests/deno/unit/mod/fake_test.ts index 2e64e07f..1c507be9 100644 --- a/tests/deno/unit/mod/fake_test.ts +++ b/tests/deno/unit/mod/fake_test.ts @@ -264,6 +264,80 @@ Deno.test("Fake()", async (t) => { }, }); }); + + await t.step(".withArgs(...)", async (t) => { + await t.step({ + name: `.willReturn(...) returns true or false depending on given args (one arg)`, + fn(): void { + const fakeFiveService = Fake(TestObjectFiveService) + .create(); + + const fakeFive = Fake(TestObjectFive) + .withConstructorArgs(fakeFiveService) + .create(); + + assertEquals(fakeFive.is_fake, true); + assertEquals(fakeFiveService.is_fake, true); + + fakeFiveService + .method("get") + .withArgs("host") + .willReturn("locaaaaaal"); + + fakeFiveService + .method("get") + .withArgs("port") + .willReturn(3000); + + // `false` because `fakeFiveService.get("port") == 3000` + assertEquals(fakeFive.send(), false); + + fakeFiveService + .method("get") + .withArgs("port") + .willReturn(4000); + + // `true` because `fakeFiveService.get("port") != 3000` + assertEquals(fakeFive.send(), true); + }, + }); + + await t.step({ + name: `.willReturn(...) returns true or false depending on given args (multiple args)`, + fn(): void { + const fakeFiveService = Fake(TestObjectFiveServiceMultipleArgs) + .create(); + + const fakeFive = Fake(TestObjectFiveMultipleArgs) + .withConstructorArgs(fakeFiveService) + .create(); + + assertEquals(fakeFive.is_fake, true); + assertEquals(fakeFiveService.is_fake, true); + + fakeFiveService + .method("get") + .withArgs("host", "localhost") + .willReturn("locaaaaaal"); + + fakeFiveService + .method("get") + .withArgs("port", 5000) + .willReturn(3000); + + // `false` because `fakeFiveService.get("port") == 3000` + assertEquals(fakeFive.send(), false); + + fakeFiveService + .method("get") + .withArgs("port", 5000) + .willReturn(4000); + + // `true` because `fakeFiveService.get("port") != 3000` + assertEquals(fakeFive.send(), true); + }, + }); + }); }); //////////////////////////////////////////////////////////////////////////////// @@ -311,30 +385,6 @@ class TestObjectFour { } } -class PrivateService { - public doSomething(): boolean { - return true; - } -} - -class Resource { - #repository: Repository; - - constructor( - serviceOne: Repository, - ) { - this.#repository = serviceOne; - } - - public getUsers() { - this.#repository.findAllUsers(); - } - - public getUser(id: number) { - this.#repository.findUserById(id); - } -} - class TestObjectFourBuilder { #something_one?: string; #something_two?: string; @@ -366,6 +416,94 @@ class TestObjectFourBuilder { } } +class TestObjectFive { + private readonly service: TestObjectFiveService; + + public constructor(service: TestObjectFiveService) { + this.service = service; + } + + public send(): boolean { + const host = this.service.get("host"); + const port = this.service.get("port"); + + if (host == null) { + return false; + } + + if (port === 3000) { + return false; + } + + return true; + } +} + +class TestObjectFiveService { + #map = new Map(); + constructor() { + this.#map.set("host", "locaaaaaal"); + this.#map.set("port", 3000); + } + public get(item: string): T { + return this.#map.get(item) as T; + } +} + +class TestObjectFiveMultipleArgs { + private readonly service: TestObjectFiveServiceMultipleArgs; + + public constructor(service: TestObjectFiveServiceMultipleArgs) { + this.service = service; + } + + public send(): boolean { + const host = this.service.get("host", "localhost"); + const port = this.service.get("port", 5000); + + if (host == null) { + return false; + } + + if (port === 3000) { + return false; + } + + return true; + } +} + +class TestObjectFiveServiceMultipleArgs { + #map = new Map(); + public get(key: string, defaultValue: T): T { + return this.#map.get(key) as T ?? defaultValue; + } +} + +class PrivateService { + public doSomething(): boolean { + return true; + } +} + +class Resource { + #repository: Repository; + + constructor( + serviceOne: Repository, + ) { + this.#repository = serviceOne; + } + + public getUsers() { + this.#repository.findAllUsers(); + } + + public getUser(id: number) { + this.#repository.findUserById(id); + } +} + class Repository { public anotha_one_called = false; public do_something_called = false; diff --git a/tests/deno/unit/mod/mock_test.ts b/tests/deno/unit/mod/mock_test.ts index 80f3aa20..8efe3596 100644 --- a/tests/deno/unit/mod/mock_test.ts +++ b/tests/deno/unit/mod/mock_test.ts @@ -264,6 +264,80 @@ Deno.test("Mock()", async (t) => { }, }); + await t.step(".withArgs(...)", async (t) => { + await t.step({ + name: `.willReturn(...) returns true or false depending on given args`, + fn(): void { + const mockFiveService = Mock(TestObjectFiveService) + .create(); + + const mockFive = Mock(TestObjectFive) + .withConstructorArgs(mockFiveService) + .create(); + + assertEquals(mockFive.is_mock, true); + assertEquals(mockFiveService.is_mock, true); + + mockFiveService + .method("get") + .withArgs("host") + .willReturn("locaaaaaal"); + + mockFiveService + .method("get") + .withArgs("port") + .willReturn(3000); + + // `false` because `mockFiveService.get("port") == 3000` + assertEquals(mockFive.send(), false); + + mockFiveService + .method("get") + .withArgs("port") + .willReturn(4000); + + // `true` because `mockFiveService.get("port") != 3000` + assertEquals(mockFive.send(), true); + }, + }); + + await t.step({ + name: `.willReturn(...) returns true or false depending on given args (multiple args)`, + fn(): void { + const mockFiveService = Mock(TestObjectFiveServiceMultipleArgs) + .create(); + + const mockFive = Mock(TestObjectFiveMultipleArgs) + .withConstructorArgs(mockFiveService) + .create(); + + assertEquals(mockFive.is_mock, true); + assertEquals(mockFiveService.is_mock, true); + + mockFiveService + .method("get") + .withArgs("host", "localhost") + .willReturn("locaaaaaal"); + + mockFiveService + .method("get") + .withArgs("port", 5000) + .willReturn(3000); + + // `false` because `mockFiveService.get("port") == 3000` + assertEquals(mockFive.send(), false); + + mockFiveService + .method("get") + .withArgs("port", 5000) + .willReturn(4000); + + // `true` because `mockFiveService.get("port") != 3000` + assertEquals(mockFive.send(), true); + }, + }); + }); + // TODO(crookse) Uncomment and fix test if needed when // toBeCalledWithoutArgs() is released // @@ -422,6 +496,71 @@ class TestObjectFourBuilder { } } +class TestObjectFive { + private readonly service: TestObjectFiveService; + + public constructor(service: TestObjectFiveService) { + this.service = service; + } + + public send(): boolean { + const host = this.service.get("host"); + const port = this.service.get("port"); + + if (host == null) { + return false; + } + + if (port === 3000) { + return false; + } + + return true; + } +} + +class TestObjectFiveService { + #map = new Map(); + constructor() { + this.#map.set("host", "locaaaaaal"); + this.#map.set("port", 3000); + } + public get(item: string): T { + return this.#map.get(item) as T; + } +} + + +class TestObjectFiveMultipleArgs { + private readonly service: TestObjectFiveServiceMultipleArgs; + + public constructor(service: TestObjectFiveServiceMultipleArgs) { + this.service = service; + } + + public send(): boolean { + const host = this.service.get("host", "localhost"); + const port = this.service.get("port", 5000); + + if (host == null) { + return false; + } + + if (port === 3000) { + return false; + } + + return true; + } +} + +class TestObjectFiveServiceMultipleArgs { + #map = new Map(); + public get(key: string, defaultValue: T): T { + return this.#map.get(key) as T ?? defaultValue; + } +} + class RandomError extends Error {} class RandomError2 extends Error { public name = "RandomError2Name"; From ba6a8ad0c32a71afce496581ac09b1c0bd6e2bd3 Mon Sep 17 00:00:00 2001 From: Eric Crooks Date: Fri, 21 Oct 2022 01:40:11 -0400 Subject: [PATCH 03/22] chore: deno fmt --- tests/deno/unit/mod/fake_test.ts | 6 ++-- tests/deno/unit/mod/mock_test.ts | 58 ++++++++++++++++---------------- 2 files changed, 33 insertions(+), 31 deletions(-) diff --git a/tests/deno/unit/mod/fake_test.ts b/tests/deno/unit/mod/fake_test.ts index 1c507be9..fdc13f8b 100644 --- a/tests/deno/unit/mod/fake_test.ts +++ b/tests/deno/unit/mod/fake_test.ts @@ -267,7 +267,8 @@ Deno.test("Fake()", async (t) => { await t.step(".withArgs(...)", async (t) => { await t.step({ - name: `.willReturn(...) returns true or false depending on given args (one arg)`, + name: + `.willReturn(...) returns true or false depending on given args (one arg)`, fn(): void { const fakeFiveService = Fake(TestObjectFiveService) .create(); @@ -303,7 +304,8 @@ Deno.test("Fake()", async (t) => { }); await t.step({ - name: `.willReturn(...) returns true or false depending on given args (multiple args)`, + name: + `.willReturn(...) returns true or false depending on given args (multiple args)`, fn(): void { const fakeFiveService = Fake(TestObjectFiveServiceMultipleArgs) .create(); diff --git a/tests/deno/unit/mod/mock_test.ts b/tests/deno/unit/mod/mock_test.ts index 8efe3596..1d286b66 100644 --- a/tests/deno/unit/mod/mock_test.ts +++ b/tests/deno/unit/mod/mock_test.ts @@ -301,41 +301,42 @@ Deno.test("Mock()", async (t) => { }, }); - await t.step({ - name: `.willReturn(...) returns true or false depending on given args (multiple args)`, - fn(): void { - const mockFiveService = Mock(TestObjectFiveServiceMultipleArgs) - .create(); + await t.step({ + name: + `.willReturn(...) returns true or false depending on given args (multiple args)`, + fn(): void { + const mockFiveService = Mock(TestObjectFiveServiceMultipleArgs) + .create(); - const mockFive = Mock(TestObjectFiveMultipleArgs) - .withConstructorArgs(mockFiveService) - .create(); + const mockFive = Mock(TestObjectFiveMultipleArgs) + .withConstructorArgs(mockFiveService) + .create(); - assertEquals(mockFive.is_mock, true); - assertEquals(mockFiveService.is_mock, true); + assertEquals(mockFive.is_mock, true); + assertEquals(mockFiveService.is_mock, true); - mockFiveService - .method("get") - .withArgs("host", "localhost") - .willReturn("locaaaaaal"); + mockFiveService + .method("get") + .withArgs("host", "localhost") + .willReturn("locaaaaaal"); - mockFiveService - .method("get") - .withArgs("port", 5000) - .willReturn(3000); + mockFiveService + .method("get") + .withArgs("port", 5000) + .willReturn(3000); - // `false` because `mockFiveService.get("port") == 3000` - assertEquals(mockFive.send(), false); + // `false` because `mockFiveService.get("port") == 3000` + assertEquals(mockFive.send(), false); - mockFiveService - .method("get") - .withArgs("port", 5000) - .willReturn(4000); + mockFiveService + .method("get") + .withArgs("port", 5000) + .willReturn(4000); - // `true` because `mockFiveService.get("port") != 3000` - assertEquals(mockFive.send(), true); - }, - }); + // `true` because `mockFiveService.get("port") != 3000` + assertEquals(mockFive.send(), true); + }, + }); }); // TODO(crookse) Uncomment and fix test if needed when @@ -530,7 +531,6 @@ class TestObjectFiveService { } } - class TestObjectFiveMultipleArgs { private readonly service: TestObjectFiveServiceMultipleArgs; From 15f5e7648e102306f9dc58083ffea92afec902a3 Mon Sep 17 00:00:00 2001 From: Eric Crooks Date: Sat, 29 Oct 2022 21:02:39 -0400 Subject: [PATCH 04/22] feat: add willCall(...) to execute a callback instead of returning a static value --- src/fake/fake_builder.ts | 14 +- src/interfaces.ts | 25 +--- src/mock/mock_builder.ts | 14 +- src/pre_programmed_method.ts | 211 ++++++++++++------------------- tests/deno/unit/mod/fake_test.ts | 156 +++++++++++++---------- tests/deno/unit/mod/mock_test.ts | 170 ++++++++++++++----------- 6 files changed, 280 insertions(+), 310 deletions(-) diff --git a/src/fake/fake_builder.ts b/src/fake/fake_builder.ts index b15e712f..9ce5b4d3 100644 --- a/src/fake/fake_builder.ts +++ b/src/fake/fake_builder.ts @@ -100,17 +100,11 @@ export class FakeBuilder extends TestDoubleBuilder { ...params: unknown[] ) => unknown); - // We need to check if the method was pre-preprogrammed to return - // something. If it was, then we make sure that this method we are - // currently defining returns that pre-programmed value. + // We need to check if the method was pre-preprogrammed to do something. + // If it was, then we make sure that this method we are currently + // defining returns that pre-programmed expectation. if (methodToCall instanceof PreProgrammedMethod) { - const methodSetup = methodToCall.findSetupByArgs(args); - - if (methodSetup?.will_throw) { - methodSetup.throw(); - } - - return methodSetup?.return(); + return methodToCall.run(args); } // When method calls its original self, let the `this` context of the diff --git a/src/interfaces.ts b/src/interfaces.ts index 832ad1e8..ba4f0201 100644 --- a/src/interfaces.ts +++ b/src/interfaces.ts @@ -16,7 +16,7 @@ export interface IError { name: string; /** - * The error message. + * The error message. Allows undefined in case there is no message. */ message?: string; } @@ -100,31 +100,14 @@ export interface IMethodExpectation { //////////////////////////////////////////////////////////////////////////////// export interface IMethodChanger { - /** - * Make the given method return the given `returnValue`. - * - * @param returnValue - The value to make the method return. - */ - willReturn(returnValue: T): IMethodSetup; - - /** - * Make the given method throw the given error. - * - * @param error - An error which extends the `Error` class or has the same - * interface as the `Error` class. - */ - willThrow(error: IError & T): IMethodSetup; - - withArgs(...args: unknown[]): IMethodSetup; -} + willCall(args: T): void; -export interface IMethodSetup { /** * Make the given method return the given `returnValue`. * * @param returnValue - The value to make the method return. */ - willReturn(returnValue: ReturnValue): IMethodSetup; + willReturn(returnValue: T): void; /** * Make the given method throw the given error. @@ -132,7 +115,7 @@ export interface IMethodSetup { * @param error - An error which extends the `Error` class or has the same * interface as the `Error` class. */ - willThrow(error: IError): IMethodSetup; + willThrow(error: IError & T): void; } //////////////////////////////////////////////////////////////////////////////// diff --git a/src/mock/mock_builder.ts b/src/mock/mock_builder.ts index 6a184fa7..f92bbd7c 100644 --- a/src/mock/mock_builder.ts +++ b/src/mock/mock_builder.ts @@ -108,17 +108,11 @@ export class MockBuilder extends TestDoubleBuilder { ...params: unknown[] ) => unknown); - // We need to check if the method was pre-preprogrammed to return - // something. If it was, then we make sure that this method we are - // currently defining returns that pre-programmed value. + // We need to check if the method was pre-preprogrammed to do something. + // If it was, then we make sure that this method we are currently + // defining returns that pre-programmed expectation. if (methodToCall instanceof PreProgrammedMethod) { - const methodSetup = methodToCall.findSetupByArgs(args); - - if (methodSetup?.will_throw) { - throw methodSetup.throw(); - } - - return methodSetup?.return(); + return methodToCall.run(args); } // When method calls its original self, let the `this` context of the diff --git a/src/pre_programmed_method.ts b/src/pre_programmed_method.ts index dfe3c82c..8e27056a 100644 --- a/src/pre_programmed_method.ts +++ b/src/pre_programmed_method.ts @@ -11,19 +11,15 @@ class PreProgrammedMethodError extends Error {} */ export class PreProgrammedMethod { /** - * Pre-programmed methods can have multiple setups. For example, a method can - * be pre-programmed to return or throw when it receives a specific set of - * arguments. Each setup is stored here. Setups are either created or updated. - * For example, if a setup for `someMethod("hello")` was not created yet, then - * it will be created; and if a setup for `someMethod("hello")` was already - * created and being set up again, then it will be updated. + * The original name of the method being pre-programmed. */ - #setups: Map> = new Map(); + #method_name: MethodOf; /** - * The original name of the method being pre-programmed. + * The object containing the pre-programmed setup details. This object is what + * runs under the hood to provide the pre-programmed expectation. */ - #method_name: MethodOf; + #method_setup?: MethodSetup; ////////////////////////////////////////////////////////////////////////////// // FILE MARKER - CONSTRUCTOR ///////////////////////////////////////////////// @@ -35,49 +31,17 @@ export class PreProgrammedMethod { */ constructor(methodName: MethodOf) { this.#method_name = methodName; - - // Add a method setup that takes no args for this pre-programmed method. - // Users are required to finish setting up this pre-programmed method. If - // the method is not finished being pre-programmed, then the - // `PreProgrammedMethodError` will be thrown. - this.#setups.set("[]", new MethodSetup(this.#method_name)); } ////////////////////////////////////////////////////////////////////////////// // FILE MARKER - METHODS - PUBLIC /////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// - /** - * Find the given method setup with the given arguments. - * @param args The arguments to use to query the method setup map. - * @returns A `MethodSetup` instance that holds the pre-programmed return - * value or error to throw. - */ - public findSetupByArgs( - args: unknown, - ): MethodSetup | undefined { - return this.#setups.get(JSON.stringify(args)); - } - - /** - * Create a method setup with a specific set of arguments. - * @param args The arguments to use for this setup. When this pre-programmed - * method is called with these arguments, it will return or throw the - * defined return value or error associated with these arguments. - * @returns A `MethodSetup` instance that can further configure this - * pre-programmed method to return a value or throw an error. - */ - public withArgs( - ...args: unknown[] - ): MethodSetup { - const methodSetup = new MethodSetup( + public willCall(action: (...args: unknown[]) => ReturnValue): void { + this.#method_setup = new MethodSetupCallsCallback( this.#method_name, + action, ); - methodSetup.setArgs(args); - - this.#setups.set(methodSetup.args_as_string, methodSetup); - - return methodSetup; } /** @@ -87,16 +51,12 @@ export class PreProgrammedMethod { * being used in place of an original method. */ public willReturn( - returnValue: ReturnValue, - ): MethodSetup { - const methodSetup = new MethodSetup( + returnValue: ReturnValue | ((...args: unknown[]) => ReturnValue), + ): void { + this.#method_setup = new MethodSetupReturnsStaticValue( this.#method_name, + returnValue, ); - methodSetup.setReturnValue(returnValue); - - this.#setups.set(methodSetup.args_as_string, methodSetup); - - return methodSetup; } /** @@ -104,109 +64,100 @@ export class PreProgrammedMethod { * * @param error - The error to throw. */ - public willThrow(error: IError): MethodSetup { - const methodSetup = new MethodSetup( + public willThrow(error: IError): void { + this.#method_setup = new MethodSetupThrowsError( this.#method_name, + error, ); - methodSetup.setErrorToThrow(error); - - this.#setups.set(methodSetup.args_as_string, methodSetup); + } - return methodSetup; + public run(args: unknown[]): unknown { + if (!this.#method_setup) { + throw new Error( + `Pre-programmed method "${ + String(this.#method_name) + }" does not have a return value.`, + ); + } + return this.#method_setup?.run(args); } } +enum MethodSetupExpectation { + ExecuteCallback, + ReturnStaticValue, + ThrowError, +} + /** * Class to hold information about a specific pre-programmed method setup. */ -class MethodSetup { - #args?: unknown[] = []; - #error?: IError; - #method_name: MethodOf; - #return?: ReturnValue; - #will_return = false; - #will_throw = false; +abstract class MethodSetup { + readonly id: string; + protected expectation: MethodSetupExpectation; + protected method_name: string; - constructor(methodName: MethodOf) { - this.#method_name = methodName; + constructor( + methodName: MethodOf, + expectation: MethodSetupExpectation, + ) { + this.id = this.run.toString(); + this.method_name = String(methodName); // Make the method name type-safe + this.expectation = expectation; } - get args_as_string() { - return JSON.stringify(this.#args); - } + abstract run(args?: unknown): unknown; +} - get will_throw() { - return this.#will_throw; - } +class MethodSetupThrowsError + extends MethodSetup { + #error: IError; - get will_return() { - return this.#will_return; + constructor( + methodName: MethodOf, + returnValue: IError, + ) { + super(methodName, MethodSetupExpectation.ThrowError); + this.#error = returnValue; } - setArgs(args: unknown[]): this { - this.#args = args ?? []; - return this; + run(): void { + throw this.#error; } +} - setReturnValue(returnValue: ReturnValue): this { - this.#will_return = true; - this.#return = returnValue; - return this; - } +class MethodSetupReturnsStaticValue + extends MethodSetup { + #return_value: ReturnValue; - setErrorToThrow(error: IError): this { - this.#will_throw = true; - this.#error = error; - return this; + constructor( + methodName: MethodOf, + returnValue: ReturnValue, + ) { + super(methodName, MethodSetupExpectation.ReturnStaticValue); + this.#return_value = returnValue; } - return(): ReturnValue { - if (this.#return === undefined) { - const typeSafeMethodName = String(this.#method_name); - - throw new PreProgrammedMethodError( - `Pre-programmed method "${typeSafeMethodName}" does not have a return value.`, - ); - } - - return this.#return; + run(): ReturnValue { + return this.#return_value; } +} - throw(): void { - const typeSafeMethodName = String(this.#method_name); +class MethodSetupCallsCallback< + OriginalObject, + Callback extends ((...args: unknown[]) => ReturnType), +> extends MethodSetup { + #callback: Callback; - if (!this.#will_throw) { - throw new PreProgrammedMethodError( - `Pre-programmed method "${typeSafeMethodName}" is not set up to throw an error.`, - ); - } - if (this.#error === undefined) { - throw new PreProgrammedMethodError( - `Pre-programmed method "${typeSafeMethodName}" is set up to throw an error, but no error was provided.`, - ); - } - - throw this.#error; + constructor( + methodName: MethodOf, + callback: Callback, + ) { + super(methodName, MethodSetupExpectation.ReturnStaticValue); + this.#callback = callback; } - /** - * Pre-program this method to return the given value. - * - * @param returnValue The value that should be returned when this object is - * being used in place of an original method. - */ - public willReturn(returnValue: ReturnValue): void { - this.#will_return = true; - this.#return = returnValue; - } - - /** - * Pre-program this method to throw the given error. - * - * @param error - The error to throw. - */ - public willThrow(error: IError): void { - this.#will_throw = true; - this.#error = error; + run(args: unknown[]): ReturnType { + return this.#callback(...args); } } diff --git a/tests/deno/unit/mod/fake_test.ts b/tests/deno/unit/mod/fake_test.ts index fdc13f8b..f8eee954 100644 --- a/tests/deno/unit/mod/fake_test.ts +++ b/tests/deno/unit/mod/fake_test.ts @@ -198,7 +198,8 @@ Deno.test("Fake()", async (t) => { assertEquals(fake1.something_one, "one"); assertEquals(fake1.something_two, "two"); - // Assert that the fake implementation will not set properties + // Assert that the fake implementation will not set properties due to + // taking a shortcut const fake2 = Fake(TestObjectFourBuilder).create(); assertEquals(fake2.is_fake, true); fake2 @@ -221,54 +222,7 @@ Deno.test("Fake()", async (t) => { }); await t.step({ - name: ".willThrow() causes throwing RandomError (with constructor)", - fn(): void { - const fake = Fake(TestObjectThree).create(); - assertEquals(fake.is_fake, true); - - // Original returns "World" - assertEquals(fake.test(), "World"); - - // Make the original method throw RandomError - fake - .method("test") - .willThrow(new RandomError("Random error message.")); - - assertThrows( - () => fake.test(), - RandomError, - "Random error message.", - ); - }, - }); - - await t.step({ - name: ".willThrow() causes throwing RandomError2 (no constructor)", - fn(): void { - const fake = Fake(TestObjectThree).create(); - assertEquals(fake.is_fake, true); - - // Original returns "World" - assertEquals(fake.test(), "World"); - - // Make the original method throw RandomError - fake - .method("test") - .willThrow(new RandomError2()); - - assertThrows( - () => fake.test(), - RandomError2, - "Some message not by the constructor.", - ); - }, - }); - }); - - await t.step(".withArgs(...)", async (t) => { - await t.step({ - name: - `.willReturn(...) returns true or false depending on given args (one arg)`, + name: `.willCall(...) returns true|false depending on given args`, fn(): void { const fakeFiveService = Fake(TestObjectFiveService) .create(); @@ -282,21 +236,34 @@ Deno.test("Fake()", async (t) => { fakeFiveService .method("get") - .withArgs("host") - .willReturn("locaaaaaal"); + .willCall((key: string, _defaultValue: number | string) => { + if (key == "host") { + return "locaaaaaal"; + } - fakeFiveService - .method("get") - .withArgs("port") - .willReturn(3000); + if (key == "port") { + return 3000; + } + + return undefined; + }); // `false` because `fakeFiveService.get("port") == 3000` assertEquals(fakeFive.send(), false); fakeFiveService .method("get") - .withArgs("port") - .willReturn(4000); + .willCall((key: string, _defaultValue: number | string) => { + if (key == "host") { + return "locaaaaaal"; + } + + if (key == "port") { + return 4000; + } + + return undefined; + }); // `true` because `fakeFiveService.get("port") != 3000` assertEquals(fakeFive.send(), true); @@ -305,7 +272,7 @@ Deno.test("Fake()", async (t) => { await t.step({ name: - `.willReturn(...) returns true or false depending on given args (multiple args)`, + `.willCall(...) returns true|false depending on given args (multiple args)`, fn(): void { const fakeFiveService = Fake(TestObjectFiveServiceMultipleArgs) .create(); @@ -319,26 +286,83 @@ Deno.test("Fake()", async (t) => { fakeFiveService .method("get") - .withArgs("host", "localhost") - .willReturn("locaaaaaal"); + .willCall((key: string, defaultValue: number | string) => { + if (key == "host" && defaultValue == "localhost") { + return null; + } - fakeFiveService - .method("get") - .withArgs("port", 5000) - .willReturn(3000); + if (key == "port" && defaultValue == 5000) { + return 4000; + } + + return undefined; + }); // `false` because `fakeFiveService.get("port") == 3000` assertEquals(fakeFive.send(), false); fakeFiveService .method("get") - .withArgs("port", 5000) - .willReturn(4000); + .willCall((key: string, defaultValue: string | number) => { + if (key == "host" && defaultValue == "localhost") { + return "locaaaaaal"; + } + + if (key == "port" && defaultValue == 5000) { + return 4000; + } + + return undefined; + }); // `true` because `fakeFiveService.get("port") != 3000` assertEquals(fakeFive.send(), true); }, }); + + await t.step({ + name: ".willThrow() causes throwing RandomError (with constructor)", + fn(): void { + const fake = Fake(TestObjectThree).create(); + assertEquals(fake.is_fake, true); + + // Original returns "World" + assertEquals(fake.test(), "World"); + + // Make the original method throw RandomError + fake + .method("test") + .willThrow(new RandomError("Random error message.")); + + assertThrows( + () => fake.test(), + RandomError, + "Random error message.", + ); + }, + }); + + await t.step({ + name: ".willThrow() causes throwing RandomError2 (no constructor)", + fn(): void { + const fake = Fake(TestObjectThree).create(); + assertEquals(fake.is_fake, true); + + // Original returns "World" + assertEquals(fake.test(), "World"); + + // Make the original method throw RandomError + fake + .method("test") + .willThrow(new RandomError2()); + + assertThrows( + () => fake.test(), + RandomError2, + "Some message not by the constructor.", + ); + }, + }); }); }); diff --git a/tests/deno/unit/mod/mock_test.ts b/tests/deno/unit/mod/mock_test.ts index 1d286b66..c11a1ab7 100644 --- a/tests/deno/unit/mod/mock_test.ts +++ b/tests/deno/unit/mod/mock_test.ts @@ -264,79 +264,103 @@ Deno.test("Mock()", async (t) => { }, }); - await t.step(".withArgs(...)", async (t) => { - await t.step({ - name: `.willReturn(...) returns true or false depending on given args`, - fn(): void { - const mockFiveService = Mock(TestObjectFiveService) - .create(); - - const mockFive = Mock(TestObjectFive) - .withConstructorArgs(mockFiveService) - .create(); - - assertEquals(mockFive.is_mock, true); - assertEquals(mockFiveService.is_mock, true); - - mockFiveService - .method("get") - .withArgs("host") - .willReturn("locaaaaaal"); - - mockFiveService - .method("get") - .withArgs("port") - .willReturn(3000); - - // `false` because `mockFiveService.get("port") == 3000` - assertEquals(mockFive.send(), false); - - mockFiveService - .method("get") - .withArgs("port") - .willReturn(4000); - - // `true` because `mockFiveService.get("port") != 3000` - assertEquals(mockFive.send(), true); - }, - }); - - await t.step({ - name: - `.willReturn(...) returns true or false depending on given args (multiple args)`, - fn(): void { - const mockFiveService = Mock(TestObjectFiveServiceMultipleArgs) - .create(); - - const mockFive = Mock(TestObjectFiveMultipleArgs) - .withConstructorArgs(mockFiveService) - .create(); - - assertEquals(mockFive.is_mock, true); - assertEquals(mockFiveService.is_mock, true); - - mockFiveService - .method("get") - .withArgs("host", "localhost") - .willReturn("locaaaaaal"); - - mockFiveService - .method("get") - .withArgs("port", 5000) - .willReturn(3000); - - // `false` because `mockFiveService.get("port") == 3000` - assertEquals(mockFive.send(), false); - - mockFiveService - .method("get") - .withArgs("port", 5000) - .willReturn(4000); - - // `true` because `mockFiveService.get("port") != 3000` - assertEquals(mockFive.send(), true); - }, - }); + await t.step({ + name: `.willCall(...) returns true|false depending on given args`, + fn(): void { + const mockFiveService = Mock(TestObjectFiveService) + .create(); + + const mockFive = Mock(TestObjectFive) + .withConstructorArgs(mockFiveService) + .create(); + + assertEquals(mockFive.is_mock, true); + assertEquals(mockFiveService.is_mock, true); + + mockFiveService + .method("get") + .willCall((key: string, _defaultValue: number | string) => { + if (key == "host") { + return "locaaaaaal"; + } + + if (key == "port") { + return 3000; + } + + return undefined; + }); + + // `false` because `mockFiveService.get("port") == 3000` + assertEquals(mockFive.send(), false); + + mockFiveService + .method("get") + .willCall((key: string, _defaultValue: number | string) => { + if (key == "host") { + return "locaaaaaal"; + } + + if (key == "port") { + return 4000; + } + + return undefined; + }); + + // `true` because `mockFiveService.get("port") != 3000` + assertEquals(mockFive.send(), true); + }, + }); + + await t.step({ + name: + `.willCall(...) returns true|false depending on given args (multiple args)`, + fn(): void { + const mockFiveService = Mock(TestObjectFiveServiceMultipleArgs) + .create(); + + const mockFive = Mock(TestObjectFiveMultipleArgs) + .withConstructorArgs(mockFiveService) + .create(); + + assertEquals(mockFive.is_mock, true); + assertEquals(mockFiveService.is_mock, true); + + mockFiveService + .method("get") + .willCall((key: string, defaultValue: number | string) => { + if (key == "host" && defaultValue == "localhost") { + return null; + } + + if (key == "port" && defaultValue == 5000) { + return 4000; + } + + return undefined; + }); + + // `false` because `mockFiveService.get("port") == 3000` + assertEquals(mockFive.send(), false); + + mockFiveService + .method("get") + .willCall((key: string, defaultValue: string | number) => { + if (key == "host" && defaultValue == "localhost") { + return "locaaaaaal"; + } + + if (key == "port" && defaultValue == 5000) { + return 4000; + } + + return undefined; + }); + + // `true` because `mockFiveService.get("port") != 3000` + assertEquals(mockFive.send(), true); + }, }); // TODO(crookse) Uncomment and fix test if needed when From 6e5bf29c22ee7657d7fc1af24030d661e42c6199 Mon Sep 17 00:00:00 2001 From: Eric Crooks Date: Thu, 3 Nov 2022 20:34:57 -0400 Subject: [PATCH 05/22] refactor: use willReturn() --- src/interfaces.ts | 18 ++++++++---- src/pre_programmed_method.ts | 49 ++++++++++++++++++-------------- tests/deno/unit/mod/fake_test.ts | 13 +++++---- tests/deno/unit/mod/mock_test.ts | 13 +++++---- 4 files changed, 53 insertions(+), 40 deletions(-) diff --git a/src/interfaces.ts b/src/interfaces.ts index ba4f0201..b94b41e1 100644 --- a/src/interfaces.ts +++ b/src/interfaces.ts @@ -100,18 +100,24 @@ export interface IMethodExpectation { //////////////////////////////////////////////////////////////////////////////// export interface IMethodChanger { - willCall(args: T): void; + // TODO(crookse) Think about introducing this as an alias to willReturn(). + // willCall(args: T): void; /** - * Make the given method return the given `returnValue`. - * - * @param returnValue - The value to make the method return. + * Pre-program a method to return (and call) the following action. + * @param action The action the method will return and call. + */ + willReturn(action: (...args: unknown[]) => ReturnValue): void; + + /** + * Pre-program this method to return the given value. + * @param returnValue The value that should be returned when this object is + * being used in place of an original method. */ willReturn(returnValue: T): void; /** - * Make the given method throw the given error. - * + * Pre-program this method to throw the given error. * @param error - An error which extends the `Error` class or has the same * interface as the `Error` class. */ diff --git a/src/pre_programmed_method.ts b/src/pre_programmed_method.ts index 8e27056a..1378ab84 100644 --- a/src/pre_programmed_method.ts +++ b/src/pre_programmed_method.ts @@ -1,7 +1,7 @@ -import type { IError } from "./interfaces.ts"; +import type { IError, IMethodChanger } from "./interfaces.ts"; import type { MethodOf } from "./types.ts"; -class PreProgrammedMethodError extends Error {} +type ReturnValueFunction = (...args: unknown[]) => ReturnValue; /** * Class that allows to be a "stand-in" for a method. For example, when used in @@ -9,7 +9,8 @@ class PreProgrammedMethodError extends Error {} * methods (using this class), and have a system under test use the * pre-programmed methods. */ -export class PreProgrammedMethod { +export class PreProgrammedMethod + implements IMethodChanger { /** * The original name of the method being pre-programmed. */ @@ -37,33 +38,32 @@ export class PreProgrammedMethod { // FILE MARKER - METHODS - PUBLIC /////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// - public willCall(action: (...args: unknown[]) => ReturnValue): void { - this.#method_setup = new MethodSetupCallsCallback( - this.#method_name, - action, - ); - } + // public willCall(action: (...args: unknown[]) => ReturnValue): void { + // this.#method_setup = new MethodSetupCallsCallback( + // this.#method_name, + // action, + // ); + // } + + // public willReturn(action: (...args: unknown[]) => ReturnValue): void; - /** - * Pre-program this method to return the given value. - * - * @param returnValue The value that should be returned when this object is - * being used in place of an original method. - */ public willReturn( - returnValue: ReturnValue | ((...args: unknown[]) => ReturnValue), + returnValue: ReturnValue | ReturnValueFunction, ): void { + if (typeof returnValue === "function") { + this.#method_setup = new MethodSetupCallsCallback( + this.#method_name, + returnValue as ReturnValueFunction, + ); + return; + } + this.#method_setup = new MethodSetupReturnsStaticValue( this.#method_name, returnValue, ); } - /** - * Pre-program this method to throw the given error. - * - * @param error - The error to throw. - */ public willThrow(error: IError): void { this.#method_setup = new MethodSetupThrowsError( this.#method_name, @@ -71,7 +71,12 @@ export class PreProgrammedMethod { ); } - public run(args: unknown[]): unknown { + /** + * Run this method. + * @param args + * @returns + */ + public run(args?: unknown[]): unknown { if (!this.#method_setup) { throw new Error( `Pre-programmed method "${ diff --git a/tests/deno/unit/mod/fake_test.ts b/tests/deno/unit/mod/fake_test.ts index f8eee954..da42ffa9 100644 --- a/tests/deno/unit/mod/fake_test.ts +++ b/tests/deno/unit/mod/fake_test.ts @@ -222,7 +222,8 @@ Deno.test("Fake()", async (t) => { }); await t.step({ - name: `.willCall(...) returns true|false depending on given args`, + name: + `.willReturn((...) => {...}) returns true|false depending on given args`, fn(): void { const fakeFiveService = Fake(TestObjectFiveService) .create(); @@ -236,7 +237,7 @@ Deno.test("Fake()", async (t) => { fakeFiveService .method("get") - .willCall((key: string, _defaultValue: number | string) => { + .willReturn((key: string, _defaultValue: number | string) => { if (key == "host") { return "locaaaaaal"; } @@ -253,7 +254,7 @@ Deno.test("Fake()", async (t) => { fakeFiveService .method("get") - .willCall((key: string, _defaultValue: number | string) => { + .willReturn((key: string, _defaultValue: number | string) => { if (key == "host") { return "locaaaaaal"; } @@ -272,7 +273,7 @@ Deno.test("Fake()", async (t) => { await t.step({ name: - `.willCall(...) returns true|false depending on given args (multiple args)`, + `.willReturn((...) => {...}) returns true|false depending on given args (multiple args)`, fn(): void { const fakeFiveService = Fake(TestObjectFiveServiceMultipleArgs) .create(); @@ -286,7 +287,7 @@ Deno.test("Fake()", async (t) => { fakeFiveService .method("get") - .willCall((key: string, defaultValue: number | string) => { + .willReturn((key: string, defaultValue: number | string) => { if (key == "host" && defaultValue == "localhost") { return null; } @@ -303,7 +304,7 @@ Deno.test("Fake()", async (t) => { fakeFiveService .method("get") - .willCall((key: string, defaultValue: string | number) => { + .willReturn((key: string, defaultValue: string | number) => { if (key == "host" && defaultValue == "localhost") { return "locaaaaaal"; } diff --git a/tests/deno/unit/mod/mock_test.ts b/tests/deno/unit/mod/mock_test.ts index c11a1ab7..c46a9b9e 100644 --- a/tests/deno/unit/mod/mock_test.ts +++ b/tests/deno/unit/mod/mock_test.ts @@ -265,7 +265,8 @@ Deno.test("Mock()", async (t) => { }); await t.step({ - name: `.willCall(...) returns true|false depending on given args`, + name: + `.willReturn((...) => {...}) returns true|false depending on given args`, fn(): void { const mockFiveService = Mock(TestObjectFiveService) .create(); @@ -279,7 +280,7 @@ Deno.test("Mock()", async (t) => { mockFiveService .method("get") - .willCall((key: string, _defaultValue: number | string) => { + .willReturn((key: string, _defaultValue: number | string) => { if (key == "host") { return "locaaaaaal"; } @@ -296,7 +297,7 @@ Deno.test("Mock()", async (t) => { mockFiveService .method("get") - .willCall((key: string, _defaultValue: number | string) => { + .willReturn((key: string, _defaultValue: number | string) => { if (key == "host") { return "locaaaaaal"; } @@ -315,7 +316,7 @@ Deno.test("Mock()", async (t) => { await t.step({ name: - `.willCall(...) returns true|false depending on given args (multiple args)`, + `.willReturn((...) => {...}) returns true|false depending on given args (multiple args)`, fn(): void { const mockFiveService = Mock(TestObjectFiveServiceMultipleArgs) .create(); @@ -329,7 +330,7 @@ Deno.test("Mock()", async (t) => { mockFiveService .method("get") - .willCall((key: string, defaultValue: number | string) => { + .willReturn((key: string, defaultValue: number | string) => { if (key == "host" && defaultValue == "localhost") { return null; } @@ -346,7 +347,7 @@ Deno.test("Mock()", async (t) => { mockFiveService .method("get") - .willCall((key: string, defaultValue: string | number) => { + .willReturn((key: string, defaultValue: string | number) => { if (key == "host" && defaultValue == "localhost") { return "locaaaaaal"; } From 99ccf21e7a29895a7e4847f9492bf14ef2a2880d Mon Sep 17 00:00:00 2001 From: Eric Crooks Date: Sat, 10 Dec 2022 10:46:52 -0500 Subject: [PATCH 06/22] fix: unmet peer dep babel/core --- .github/workflows/master.yml | 2 +- console/build_esm_lib | 1 + console/build_esm_lib.ts | 6 +++++- package.json | 5 +++-- 4 files changed, 10 insertions(+), 4 deletions(-) diff --git a/.github/workflows/master.yml b/.github/workflows/master.yml index 63f7f804..7674a539 100644 --- a/.github/workflows/master.yml +++ b/.github/workflows/master.yml @@ -31,7 +31,7 @@ jobs: os: [ubuntu-latest, windows-latest, macos-latest] # We only support until EOL # See https://nodejs.org/en/about/releases/ - node: ['14', '16', '17', '18'] + node: ['14', '16', '18'] runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v2 diff --git a/console/build_esm_lib b/console/build_esm_lib index 48529b43..7b952d0e 100755 --- a/console/build_esm_lib +++ b/console/build_esm_lib @@ -1,5 +1,6 @@ #!/bin/bash +# This script only exists to run Deno in different OSs. ( deno run --allow-read --allow-run --allow-write ./console/build_esm_lib.ts ) diff --git a/console/build_esm_lib.ts b/console/build_esm_lib.ts index 91dd846b..531f5af9 100644 --- a/console/build_esm_lib.ts +++ b/console/build_esm_lib.ts @@ -4,6 +4,7 @@ const encoder = new TextEncoder(); const workspace = "./tmp/conversion_workspace"; const debug = false; +const debugContents = false; function logDebug(msg: unknown): void { if (!debug) { @@ -15,6 +16,7 @@ function logDebug(msg: unknown): void { try { logDebug(`Creating ${workspace}.`); + emptyDirSync("./node_modules"); emptyDirSync(workspace); ensureDirSync(workspace); logDebug(`Copying Rhum source files to ${workspace}.`); @@ -84,7 +86,9 @@ function removeTsExtensions(filename: string): void { } logDebug(`New contents (without .ts extensions):`); - logDebug(contents); + if (debugContents) { + logDebug(contents); + } // Step 5: Rewrite the original file without .ts extensions logDebug(`Overwriting ${filename} with new contents.`); diff --git a/package.json b/package.json index 2b6cfeae..9a0e0783 100644 --- a/package.json +++ b/package.json @@ -8,16 +8,17 @@ "author": "Drash Land", "license": "MIT", "scripts": { - "build": "console/build_esm_lib && yarn build:cjs && yarn build:esm", + "build": "console/build_esm_lib && yarn && yarn build:cjs && yarn build:esm", "build:cjs": "tsc --project tsconfig.cjs.json", "build:conversion-workspace": "deno run --allow-read --allow-write ./console/build_esm_lib.ts", "build:esm": "tsc --project tsconfig.esm.json", - "build:windows": "bash console/build_esm_lib && yarn build:cjs && yarn build:esm", + "build:windows": "bash console/build_esm_lib && yarn && yarn build:cjs && yarn build:esm", "check": "rm -rf node_modules/ && rm yarn.lock && yarn install && yarn build && yarn test", "release": "yarn publish --access public", "test": "jest" }, "devDependencies": { + "@babel/core": "7.x", "@babel/preset-env": "7.x", "@types/jest": "27.x", "@types/node": "16.x", From 90d1d53454f2956b77cbc4e800f0fe3cc1c53424 Mon Sep 17 00:00:00 2001 From: Eric Crooks Date: Sat, 10 Dec 2022 11:07:15 -0500 Subject: [PATCH 07/22] fix: tsc mismatch --- package.json | 2 ++ 1 file changed, 2 insertions(+) diff --git a/package.json b/package.json index 9a0e0783..c0600939 100644 --- a/package.json +++ b/package.json @@ -23,9 +23,11 @@ "@types/jest": "27.x", "@types/node": "16.x", "babel-jest": "27.x", + "global": "4.x", "jest": "27.x", "ts-jest": "27.x", "ts-node": "10.x", + "tsc": "2.x", "typescript": "4.x" }, "files": [ From ce19c16e0d2f9254912ebd5c1c33984ea51b2b3d Mon Sep 17 00:00:00 2001 From: Eric Crooks Date: Sat, 10 Dec 2022 11:13:06 -0500 Subject: [PATCH 08/22] fix: OriginalObject not assignable to type object --- src/fake/fake_mixin.ts | 2 +- src/mock/mock_mixin.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/fake/fake_mixin.ts b/src/fake/fake_mixin.ts index bdc9552e..05d06f70 100644 --- a/src/fake/fake_mixin.ts +++ b/src/fake/fake_mixin.ts @@ -79,7 +79,7 @@ export function createFake( methodName, ); - if (!((methodName as string) in this.#original)) { + if (!((methodName as string) in (this.#original as Record))) { const typeSafeMethodName = String(methodName as string); throw new FakeError( diff --git a/src/mock/mock_mixin.ts b/src/mock/mock_mixin.ts index fb84cc75..fb8f716e 100644 --- a/src/mock/mock_mixin.ts +++ b/src/mock/mock_mixin.ts @@ -196,7 +196,7 @@ export function createMock( methodName, ); - if (!((methodName as string) in this.#original)) { + if (!((methodName as string) in (this.#original as Record))) { const typeSafeMethodName = String(methodName); throw new MockError( `Method "${typeSafeMethodName}" does not exist.`, From 3e79d16555609ea3ddf72c01bd1d21b5f6c02898 Mon Sep 17 00:00:00 2001 From: Eric Crooks Date: Sat, 10 Dec 2022 11:15:26 -0500 Subject: [PATCH 09/22] chore: deno fmt --- src/fake/fake_mixin.ts | 4 +++- src/mock/mock_mixin.ts | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/fake/fake_mixin.ts b/src/fake/fake_mixin.ts index 05d06f70..8f8dfcc8 100644 --- a/src/fake/fake_mixin.ts +++ b/src/fake/fake_mixin.ts @@ -79,7 +79,9 @@ export function createFake( methodName, ); - if (!((methodName as string) in (this.#original as Record))) { + if ( + !((methodName as string) in (this.#original as Record)) + ) { const typeSafeMethodName = String(methodName as string); throw new FakeError( diff --git a/src/mock/mock_mixin.ts b/src/mock/mock_mixin.ts index fb8f716e..187b4716 100644 --- a/src/mock/mock_mixin.ts +++ b/src/mock/mock_mixin.ts @@ -196,7 +196,9 @@ export function createMock( methodName, ); - if (!((methodName as string) in (this.#original as Record))) { + if ( + !((methodName as string) in (this.#original as Record)) + ) { const typeSafeMethodName = String(methodName); throw new MockError( `Method "${typeSafeMethodName}" does not exist.`, From 45248091155ce5f277954fe88f91fe7c671154ad Mon Sep 17 00:00:00 2001 From: Eric Crooks Date: Sun, 11 Dec 2022 00:19:12 -0500 Subject: [PATCH 10/22] chore: remove prelease workflow --- .github/workflows/pre_release.yml | 24 ------------------------ 1 file changed, 24 deletions(-) delete mode 100644 .github/workflows/pre_release.yml diff --git a/.github/workflows/pre_release.yml b/.github/workflows/pre_release.yml deleted file mode 100644 index e68771a0..00000000 --- a/.github/workflows/pre_release.yml +++ /dev/null @@ -1,24 +0,0 @@ -name: Pre-release - -on: - create - -jobs: - - # Make a PR to master from a new branch with changes, and delete the created one - pre-release: - - # Only run when a release-v* branch is created, and not by drashbot - if: contains(github.ref, 'release-v') && !contains(github.event.sender.login, 'drashbot') - - runs-on: ubuntu-latest - - steps: - - name: Notify the castle about this pre-release - run: | - curl -X POST \ - -u "${{ secrets.CI_USER_NAME }}:${{ secrets.CI_USER_PAT }}" \ - -H "Accept: application/vnd.github.everest-preview+json" \ - -H "Content-Type: application/json" \ - --data '{"event_type": "pre_release", "client_payload": { "repo": "rhum", "module": "rhum", "version": "${{ github.ref }}" }}' \ - https://api.github.com/repos/drashland/castle/dispatches From 5b52fb202d6dfb06528b844fe70c2c52a00fe423 Mon Sep 17 00:00:00 2001 From: Eric Crooks Date: Sun, 11 Dec 2022 00:19:23 -0500 Subject: [PATCH 11/22] chore: change master workflow to main --- .github/workflows/{master.yml => main.yml} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename .github/workflows/{master.yml => main.yml} (99%) diff --git a/.github/workflows/master.yml b/.github/workflows/main.yml similarity index 99% rename from .github/workflows/master.yml rename to .github/workflows/main.yml index 7674a539..a8820b92 100644 --- a/.github/workflows/master.yml +++ b/.github/workflows/main.yml @@ -1,4 +1,4 @@ -name: master +name: Main on: pull_request: From fdfe66a1dc1af0b2c6b0121d9fae31ce054f4ad7 Mon Sep 17 00:00:00 2001 From: Eric Crooks Date: Sun, 11 Dec 2022 00:20:30 -0500 Subject: [PATCH 12/22] chore: remove nest.land in workflow --- .github/workflows/release.yml | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 944b6483..0df2d788 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -69,15 +69,3 @@ jobs: run: yarn publish --access public env: NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - publish-egg: - runs-on: ubuntu-latest - steps: - - name: Notify the castle about this release - run: | - curl -X POST \ - -u "${{ secrets.CI_USER_NAME }}:${{ secrets.CI_USER_PAT }}" \ - -H "Accept: application/vnd.github.everest-preview+json" \ - -H "Content-Type: application/json" \ - --data '{"event_type": "release", "client_payload": { "repo": "rhum", "module": "rhum", "version": "${{ github.ref }}" }}' \ - https://api.github.com/repos/drashland/castle/dispatches From c2fd3d0c4865d21a914481cd57309ef4b7fd657a Mon Sep 17 00:00:00 2001 From: Eric Crooks Date: Sun, 11 Dec 2022 00:33:11 -0500 Subject: [PATCH 13/22] refactor: make build script flexible --- console/build_esm_lib | 2 +- console/build_esm_lib.ts | 148 +++++++++++++++++++++++++++++++-------- 2 files changed, 121 insertions(+), 29 deletions(-) diff --git a/console/build_esm_lib b/console/build_esm_lib index 7b952d0e..ae1cdf5b 100755 --- a/console/build_esm_lib +++ b/console/build_esm_lib @@ -2,5 +2,5 @@ # This script only exists to run Deno in different OSs. ( - deno run --allow-read --allow-run --allow-write ./console/build_esm_lib.ts + deno run --allow-read --allow-run --allow-write ./console/build_esm_lib.ts $@ ) diff --git a/console/build_esm_lib.ts b/console/build_esm_lib.ts index 531f5af9..520054bd 100644 --- a/console/build_esm_lib.ts +++ b/console/build_esm_lib.ts @@ -1,46 +1,89 @@ +/** + * @TODO(crookse) This can probably use Line. Line wouldn't necessariliy be a + * dependency of the lib. It would be a dependency of the build process. + * + * This script takes TypeScript files that follow Deno's requirements of (e.g., + * `import` statements require `.ts` extensions) and converts them to a portable + * format that other non-Deno processes can use. + * + * For example, running `deno run -A ./console/build_esm_lib.ts ./src ./mod.ts` + * will do the following: + * + * 1. Create a directory to build the portable TypeScript code. The directory + * in this context is called the "workspace directory" and lives at + * `./tmp/conversion_workspace` directory. + * 2. Copy `./src` into the workspace directory. + * 3. Copy `./mod.ts` into the workspace directory. + * 4. Remove all `.ts` extensions from all `import` and `export` statements in + * all files in the workspace directory. + * + * Now that all `.ts` extensions are removed from the `import` and `export` + * statements, the workspace directory can be used by other processes. For + * example, `tsc` can be used on the workspace directory to compile the code + * down to a specific format like CommonJS for Node applications and a plain + * JavaScript module syntax for browser applications. + * + * @param Deno.args A list of directories and files containing TypeScript files + * that this script should convert. + */ + import { copySync, emptyDirSync, ensureDirSync, walk } from "./deps.ts"; const decoder = new TextDecoder(); const encoder = new TextEncoder(); -const workspace = "./tmp/conversion_workspace"; - -const debug = false; -const debugContents = false; +const args = Deno.args.slice(); + +const debug = optionEnabled("--debug"); +const debugContents = optionEnabled("--debug-contents"); +const workspace = optionValue("--workspace"); + +Promise + .resolve((() => { + createWorkspace(); + logDebug(`Options:`, { debug, debugContents }); + })()) + .then(convertCode) + .then(() => Deno.exit(0)) + .catch((error) => { + logDebug(error); + Deno.exit(1); + }); -function logDebug(msg: unknown): void { - if (!debug) { - return; +/** + * Convert the code given to this script. + */ +async function convertCode(): Promise { + logDebug("\nStarting .ts extension removal process.\n"); + + for await (const entry of walk(workspace)) { + if (!entry.isFile) { + continue; + } + + logDebug(`Removing .ts extensions from ${entry.path}.`); + removeTsExtensions(entry.path); + logDebug("Moving to next file."); + logDebug(""); } - console.log(msg); + logDebug("Done removing .ts extensions from source files."); } -try { +/** + * Create the workspace directory. + */ +function createWorkspace() { logDebug(`Creating ${workspace}.`); emptyDirSync("./node_modules"); emptyDirSync(workspace); ensureDirSync(workspace); - logDebug(`Copying Rhum source files to ${workspace}.`); - copySync("./src", workspace + "/src", { overwrite: true }); - copySync("./mod.ts", workspace + "/mod.ts", { overwrite: true }); -} catch (error) { - logDebug(error); - Deno.exit(1); -} - -logDebug("Starting .ts extension removal process."); -for await (const entry of walk(workspace)) { - if (!entry.isFile) { - continue; + for (const item of args) { + const nonDotFilename = item.replace("./", "/"); + logDebug(`Copying ${item} to ${workspace}${nonDotFilename}.`); + copySync(item, workspace + nonDotFilename, { overwrite: true }); } - - logDebug(`Removing .ts extensions from ${entry.path}.`); - removeTsExtensions(entry.path); - logDebug("Moving to next file.\n\n"); } -logDebug("Done removing .ts extensions from source files."); - /** * Remove the .ts extensions for runtimes that do not require it. */ @@ -85,8 +128,8 @@ function removeTsExtensions(filename: string): void { }); } - logDebug(`New contents (without .ts extensions):`); if (debugContents) { + logDebug(`New contents (without .ts extensions):`); logDebug(contents); } @@ -95,3 +138,52 @@ function removeTsExtensions(filename: string): void { Deno.writeFileSync(filename, encoder.encode(contents)); logDebug("File written."); } + +/** + * Log output. + * @param msg The message to log. + */ +function logDebug(...msg: unknown[]): void { + if (!debug) { + return; + } + + console.log(...msg); +} + +/** + * Is the given option enabled? + * @param option The name of the option (e.g., `--debug`). + * @returns True if yes, false if no. + */ +function optionEnabled(option: string): boolean { + const optionIndex = args.indexOf(option); + const enabled = optionIndex != -1; + + if (enabled) { + args.splice(optionIndex, 1); + } + + return enabled; +} + +/** + * What is the given option value? + * @param option The name of the option (e.g., `--workspace`). + * @returns The value of the option if the option exists. + */ +function optionValue(option: string): boolean { + const extractedOption = args.filter((arg: string) => { + if (arg.includes(option)) { + return true; + } + }); + + if (!extractedOption.length) { + return; + } + + args.splice(args.indexOf(extractedOption[0], 1)); + + return extractedOption[0].replace(option + "=", ""); +} From b2d8b6610295e2580e5d6d079eafd49a7eb36ef9 Mon Sep 17 00:00:00 2001 From: Eric Crooks Date: Sun, 11 Dec 2022 00:33:22 -0500 Subject: [PATCH 14/22] chore: more scripts --- package.json | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index c0600939..7dec2778 100644 --- a/package.json +++ b/package.json @@ -8,14 +8,18 @@ "author": "Drash Land", "license": "MIT", "scripts": { - "build": "console/build_esm_lib && yarn && yarn build:cjs && yarn build:esm", + "build": "yarn build:esm-lib && yarn && yarn build:esm && yarn build:cjs", "build:cjs": "tsc --project tsconfig.cjs.json", "build:conversion-workspace": "deno run --allow-read --allow-write ./console/build_esm_lib.ts", "build:esm": "tsc --project tsconfig.esm.json", + "build:esm-lib": "console/build_esm_lib ./src ./mod.ts --workspace=./tmp/conversion_workspace", "build:windows": "bash console/build_esm_lib && yarn && yarn build:cjs && yarn build:esm", - "check": "rm -rf node_modules/ && rm yarn.lock && yarn install && yarn build && yarn test", "release": "yarn publish --access public", - "test": "jest" + "test": "yarn test:deno && yarn test:cjs && yarn test:esm", + "test:cjs": "yarn jest tests/cjs/", + "test:deno": "deno test -A tests/deno/", + "test:esm": "yarn jest tests/esm/", + "validate:nix": "rm -rf node_modules && rm yarn.lock && yarn build && yarn test" }, "devDependencies": { "@babel/core": "7.x", From 1f10b923e444c36d2dc36189c4afdf3313ad8c44 Mon Sep 17 00:00:00 2001 From: Eric Crooks Date: Sun, 11 Dec 2022 00:33:56 -0500 Subject: [PATCH 15/22] test: add CJS and ESM tests for willReturn callback --- tests/cjs/unit/mod/fake.test.js | 63 +++++++++++++ tests/cjs/unit/mod/mock.test.js | 161 +++++++++++++++++++++++++++++++ tests/esm/unit/mod/fake.test.ts | 162 ++++++++++++++++++++++++++++++++ tests/esm/unit/mod/mock.test.ts | 162 ++++++++++++++++++++++++++++++++ 4 files changed, 548 insertions(+) diff --git a/tests/cjs/unit/mod/fake.test.js b/tests/cjs/unit/mod/fake.test.js index 04eda05c..50197076 100644 --- a/tests/cjs/unit/mod/fake.test.js +++ b/tests/cjs/unit/mod/fake.test.js @@ -1,4 +1,5 @@ const { Fake } = require("../../../../lib/cjs/mod"); +const { assertEquals } = require("../../jest_assertions"); describe("Fake()", () => { it("creates fake builder", () => { @@ -263,6 +264,68 @@ class TestObjectFourBuilder { } } +class TestObjectFive { + constructor(service) { + this.service = service; + } + + send() { + const host = this.service.get("host"); + const port = this.service.get("port"); + + if (host == null) { + return false; + } + + if (port === 3000) { + return false; + } + + return true; + } +} + +class TestObjectFiveService { + map = new Map(); + constructor() { + this.map.set("host", "locaaaaaal"); + this.map.set("port", 3000); + } + get(item) { + return this.map.get(item); + } +} + +class TestObjectFiveMultipleArgs { + constructor(service) { + this.service = service; + } + + send() { + const host = this.service.get("host", "localhost"); + const port = this.service.get("port", 5000); + + if (host == null) { + return false; + } + + if (port === 3000) { + return false; + } + + return true; + } +} + +class TestObjectFiveServiceMultipleArgs { + map = new Map(); + get(key, defaultValue) { + return this.map.has(key) + ? this.map.get(key) + : defaultValue; + } +} + class Repository { constructor() { this.anotha_one_called = false; diff --git a/tests/cjs/unit/mod/mock.test.js b/tests/cjs/unit/mod/mock.test.js index 004b91bd..1889b7d2 100644 --- a/tests/cjs/unit/mod/mock.test.js +++ b/tests/cjs/unit/mod/mock.test.js @@ -1,4 +1,5 @@ const { Mock } = require("../../../../lib/cjs/mod"); +const { assertEquals } = require("../../jest_assertions"); class Request { constructor( @@ -65,6 +66,68 @@ class TestObjectFourBuilder { } } +class TestObjectFive { + constructor(service) { + this.service = service; + } + + send() { + const host = this.service.get("host"); + const port = this.service.get("port"); + + if (host == null) { + return false; + } + + if (port === 3000) { + return false; + } + + return true; + } +} + +class TestObjectFiveService { + map = new Map(); + constructor() { + this.map.set("host", "locaaaaaal"); + this.map.set("port", 3000); + } + get(item) { + return this.map.get(item); + } +} + +class TestObjectFiveMultipleArgs { + constructor(service) { + this.service = service; + } + + send() { + const host = this.service.get("host", "localhost"); + const port = this.service.get("port", 5000); + + if (host == null) { + return false; + } + + if (port === 3000) { + return false; + } + + return true; + } +} + +class TestObjectFiveServiceMultipleArgs { + map = new Map(); + get(key, defaultValue) { + return this.map.has(key) + ? this.map.get(key) + : defaultValue; + } +} + class RandomError extends Error {} class RandomError2 extends Error { name = "RandomError2Name"; @@ -307,6 +370,104 @@ describe("Mock()", () => { expect(mock3.calls.someComplexMethod).toBe(1); }); + it( + `.willReturn((...) => {...}) returns true|false depending on given args`, + () => { + const mockFiveService = Mock(TestObjectFiveService) + .create(); + + const mockFive = Mock(TestObjectFive) + .withConstructorArgs(mockFiveService) + .create(); + + assertEquals(mockFive.is_mock, true); + assertEquals(mockFiveService.is_mock, true); + + mockFiveService + .method("get") + .willReturn((key, _defaultValue) => { + if (key == "host") { + return "locaaaaaal"; + } + + if (key == "port") { + return 3000; + } + + return undefined; + }); + + // `false` because `mockFiveService.get("port") == 3000` + assertEquals(mockFive.send(), false); + + mockFiveService + .method("get") + .willReturn((key, _defaultValue) => { + if (key == "host") { + return "locaaaaaal"; + } + + if (key == "port") { + return 4000; + } + + return undefined; + }); + + // `true` because `mockFiveService.get("port") != 3000` + assertEquals(mockFive.send(), true); + }, + ); + + it( + `.willReturn((...) => {...}) returns true|false depending on given args (multiple args)`, + () => { + const mockFiveService = Mock(TestObjectFiveServiceMultipleArgs) + .create(); + + const mockFive = Mock(TestObjectFiveMultipleArgs) + .withConstructorArgs(mockFiveService) + .create(); + + assertEquals(mockFive.is_mock, true); + assertEquals(mockFiveService.is_mock, true); + + mockFiveService + .method("get") + .willReturn((key, defaultValue) => { + if (key == "host" && defaultValue == "localhost") { + return null; + } + + if (key == "port" && defaultValue == 5000) { + return 4000; + } + + return undefined; + }); + + // `false` because `mockFiveService.get("port") == 3000` + assertEquals(mockFive.send(), false); + + mockFiveService + .method("get") + .willReturn((key, defaultValue) => { + if (key == "host" && defaultValue == "localhost") { + return "locaaaaaal"; + } + + if (key == "port" && defaultValue == 5000) { + return 4000; + } + + return undefined; + }); + + // `true` because `mockFiveService.get("port") != 3000` + assertEquals(mockFive.send(), true); + }, + ); + it( ".willThrow() causes throwing RandomError (with constructor)", () => { diff --git a/tests/esm/unit/mod/fake.test.ts b/tests/esm/unit/mod/fake.test.ts index 0cdb8afa..891bb0b7 100644 --- a/tests/esm/unit/mod/fake.test.ts +++ b/tests/esm/unit/mod/fake.test.ts @@ -247,6 +247,104 @@ describe("Fake()", () => { ); }, ); + + it( + ".willReturn((...) => {...}) returns true|false depending on given args", + (): void => { + const mockFiveService = Fake(TestObjectFiveService) + .create(); + + const mockFive = Fake(TestObjectFive) + .withConstructorArgs(mockFiveService) + .create(); + + assertEquals(mockFive.is_fake, true); + assertEquals(mockFiveService.is_fake, true); + + mockFiveService + .method("get") + .willReturn((key: string, _defaultValue: number | string) => { + if (key == "host") { + return "locaaaaaal"; + } + + if (key == "port") { + return 3000; + } + + return undefined; + }); + + // `false` because `mockFiveService.get("port") == 3000` + assertEquals(mockFive.send(), false); + + mockFiveService + .method("get") + .willReturn((key: string, _defaultValue: number | string) => { + if (key == "host") { + return "locaaaaaal"; + } + + if (key == "port") { + return 4000; + } + + return undefined; + }); + + // `true` because `mockFiveService.get("port") != 3000` + assertEquals(mockFive.send(), true); + }, + ); + + it( + `.willReturn((...) => {...}) returns true|false depending on given args (multiple args)`, + (): void => { + const mockFiveService = Fake(TestObjectFiveServiceMultipleArgs) + .create(); + + const mockFive = Fake(TestObjectFiveMultipleArgs) + .withConstructorArgs(mockFiveService) + .create(); + + assertEquals(mockFive.is_fake, true); + assertEquals(mockFiveService.is_fake, true); + + mockFiveService + .method("get") + .willReturn((key: string, defaultValue: number | string) => { + if (key == "host" && defaultValue == "localhost") { + return null; + } + + if (key == "port" && defaultValue == 5000) { + return 4000; + } + + return undefined; + }); + + // `false` because `mockFiveService.get("port") == 3000` + assertEquals(mockFive.send(), false); + + mockFiveService + .method("get") + .willReturn((key: string, defaultValue: string | number) => { + if (key == "host" && defaultValue == "localhost") { + return "locaaaaaal"; + } + + if (key == "port" && defaultValue == 5000) { + return 4000; + } + + return undefined; + }); + + // `true` because `mockFiveService.get("port") != 3000` + assertEquals(mockFive.send(), true); + }, + ); }); }); @@ -334,6 +432,70 @@ class TestObjectFourBuilder { } } +class TestObjectFive { + private readonly service: TestObjectFiveService; + + public constructor(service: TestObjectFiveService) { + this.service = service; + } + + public send(): boolean { + const host = this.service.get("host"); + const port = this.service.get("port"); + + if (host == null) { + return false; + } + + if (port === 3000) { + return false; + } + + return true; + } +} + +class TestObjectFiveService { + #map = new Map(); + constructor() { + this.#map.set("host", "locaaaaaal"); + this.#map.set("port", 3000); + } + public get(item: string): T { + return this.#map.get(item) as T; + } +} + +class TestObjectFiveMultipleArgs { + private readonly service: TestObjectFiveServiceMultipleArgs; + + public constructor(service: TestObjectFiveServiceMultipleArgs) { + this.service = service; + } + + public send(): boolean { + const host = this.service.get("host", "localhost"); + const port = this.service.get("port", 5000); + + if (host == null) { + return false; + } + + if (port === 3000) { + return false; + } + + return true; + } +} + +class TestObjectFiveServiceMultipleArgs { + #map = new Map(); + public get(key: string, defaultValue: T): T { + return this.#map.get(key) as T ?? defaultValue; + } +} + class Repository { public anotha_one_called = false; public do_something_called = false; diff --git a/tests/esm/unit/mod/mock.test.ts b/tests/esm/unit/mod/mock.test.ts index ef5ef7db..9e3a40e1 100644 --- a/tests/esm/unit/mod/mock.test.ts +++ b/tests/esm/unit/mod/mock.test.ts @@ -94,6 +94,70 @@ class TestObjectFourBuilder { } } +class TestObjectFive { + private readonly service: TestObjectFiveService; + + public constructor(service: TestObjectFiveService) { + this.service = service; + } + + public send(): boolean { + const host = this.service.get("host"); + const port = this.service.get("port"); + + if (host == null) { + return false; + } + + if (port === 3000) { + return false; + } + + return true; + } +} + +class TestObjectFiveService { + #map = new Map(); + constructor() { + this.#map.set("host", "locaaaaaal"); + this.#map.set("port", 3000); + } + public get(item: string): T { + return this.#map.get(item) as T; + } +} + +class TestObjectFiveMultipleArgs { + private readonly service: TestObjectFiveServiceMultipleArgs; + + public constructor(service: TestObjectFiveServiceMultipleArgs) { + this.service = service; + } + + public send(): boolean { + const host = this.service.get("host", "localhost"); + const port = this.service.get("port", 5000); + + if (host == null) { + return false; + } + + if (port === 3000) { + return false; + } + + return true; + } +} + +class TestObjectFiveServiceMultipleArgs { + #map = new Map(); + public get(key: string, defaultValue: T): T { + return this.#map.get(key) as T ?? defaultValue; + } +} + class RandomError extends Error {} class RandomError2 extends Error { public name = "RandomError2Name"; @@ -356,6 +420,104 @@ describe("Mock()", () => { }, ); + it( + `.willReturn((...) => {...}) returns true|false depending on given args`, + (): void => { + const mockFiveService = Mock(TestObjectFiveService) + .create(); + + const mockFive = Mock(TestObjectFive) + .withConstructorArgs(mockFiveService) + .create(); + + assertEquals(mockFive.is_mock, true); + assertEquals(mockFiveService.is_mock, true); + + mockFiveService + .method("get") + .willReturn((key: string, _defaultValue: number | string) => { + if (key == "host") { + return "locaaaaaal"; + } + + if (key == "port") { + return 3000; + } + + return undefined; + }); + + // `false` because `mockFiveService.get("port") == 3000` + assertEquals(mockFive.send(), false); + + mockFiveService + .method("get") + .willReturn((key: string, _defaultValue: number | string) => { + if (key == "host") { + return "locaaaaaal"; + } + + if (key == "port") { + return 4000; + } + + return undefined; + }); + + // `true` because `mockFiveService.get("port") != 3000` + assertEquals(mockFive.send(), true); + }, + ); + + it( + `.willReturn((...) => {...}) returns true|false depending on given args (multiple args)`, + (): void => { + const mockFiveService = Mock(TestObjectFiveServiceMultipleArgs) + .create(); + + const mockFive = Mock(TestObjectFiveMultipleArgs) + .withConstructorArgs(mockFiveService) + .create(); + + assertEquals(mockFive.is_mock, true); + assertEquals(mockFiveService.is_mock, true); + + mockFiveService + .method("get") + .willReturn((key: string, defaultValue: number | string) => { + if (key == "host" && defaultValue == "localhost") { + return null; + } + + if (key == "port" && defaultValue == 5000) { + return 4000; + } + + return undefined; + }); + + // `false` because `mockFiveService.get("port") == 3000` + assertEquals(mockFive.send(), false); + + mockFiveService + .method("get") + .willReturn((key: string, defaultValue: string | number) => { + if (key == "host" && defaultValue == "localhost") { + return "locaaaaaal"; + } + + if (key == "port" && defaultValue == 5000) { + return 4000; + } + + return undefined; + }); + + // `true` because `mockFiveService.get("port") != 3000` + assertEquals(mockFive.send(), true); + }, + ); + it( ".willThrow() causes throwing RandomError (with constructor)", (): void => { From c45490946d4e2cb561b152af83b3ce33dea23c48 Mon Sep 17 00:00:00 2001 From: Eric Crooks Date: Sun, 11 Dec 2022 00:36:09 -0500 Subject: [PATCH 16/22] chore: release drafter note --- .github/release_drafter_config.yml | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/.github/release_drafter_config.yml b/.github/release_drafter_config.yml index 902e6c2b..e6e70a10 100644 --- a/.github/release_drafter_config.yml +++ b/.github/release_drafter_config.yml @@ -26,6 +26,20 @@ version-resolver: # What our release will look like. If no draft has been created, then this will be used, otherwise $CHANGES just gets addedd template: | + ``` + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + + Before releasing, make sure the version in package.json matches the version being released here. + + Delete this code block before publishing this release. + + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + ``` + ## Compatibility * Requires Deno v From 2490cdd4af41a63f230dd6e05f763b9b91d70c1e Mon Sep 17 00:00:00 2001 From: Eric Crooks Date: Sun, 11 Dec 2022 00:43:04 -0500 Subject: [PATCH 17/22] fix: test vars --- tests/cjs/unit/mod/fake.test.js | 102 +++++++++++++++++++++++++++++++- tests/cjs/unit/mod/mock.test.js | 4 +- tests/esm/unit/mod/fake.test.ts | 44 +++++++------- 3 files changed, 122 insertions(+), 28 deletions(-) diff --git a/tests/cjs/unit/mod/fake.test.js b/tests/cjs/unit/mod/fake.test.js index 50197076..a6fe54a3 100644 --- a/tests/cjs/unit/mod/fake.test.js +++ b/tests/cjs/unit/mod/fake.test.js @@ -164,6 +164,104 @@ describe("Fake()", () => { expect(fake3.something_one).toBe("you got changed"); }); + it( + `.willReturn((...) => {...}) returns true|false depending on given args`, + () => { + const fakeFiveService = Fake(TestObjectFiveService) + .create(); + + const fakeFive = Fake(TestObjectFive) + .withConstructorArgs(fakeFiveService) + .create(); + + assertEquals(fakeFive.is_fake, true); + assertEquals(fakeFiveService.is_fake, true); + + fakeFiveService + .method("get") + .willReturn((key, _defaultValue) => { + if (key == "host") { + return "locaaaaaal"; + } + + if (key == "port") { + return 3000; + } + + return undefined; + }); + + // `false` because `fakeFiveService.get("port") == 3000` + assertEquals(fakeFive.send(), false); + + fakeFiveService + .method("get") + .willReturn((key, _defaultValue) => { + if (key == "host") { + return "locaaaaaal"; + } + + if (key == "port") { + return 4000; + } + + return undefined; + }); + + // `true` because `fakeFiveService.get("port") != 3000` + assertEquals(fakeFive.send(), true); + }, + ); + + it( + `.willReturn((...) => {...}) returns true|false depending on given args (multiple args)`, + () => { + const fakeFiveService = Fake(TestObjectFiveServiceMultipleArgs) + .create(); + + const fakeFive = Fake(TestObjectFiveMultipleArgs) + .withConstructorArgs(fakeFiveService) + .create(); + + assertEquals(fakeFive.is_fake, true); + assertEquals(fakeFiveService.is_fake, true); + + fakeFiveService + .method("get") + .willReturn((key, defaultValue) => { + if (key == "host" && defaultValue == "localhost") { + return null; + } + + if (key == "port" && defaultValue == 5000) { + return 4000; + } + + return undefined; + }); + + // `false` because `fakeFiveService.get("port") == 3000` + assertEquals(fakeFive.send(), false); + + fakeFiveService + .method("get") + .willReturn((key, defaultValue) => { + if (key == "host" && defaultValue == "localhost") { + return "locaaaaaal"; + } + + if (key == "port" && defaultValue == 5000) { + return 4000; + } + + return undefined; + }); + + // `true` because `fakeFiveService.get("port") != 3000` + assertEquals(fakeFive.send(), true); + }, + ); + it(".willThrow() causes throwing RandomError (with constructor)", () => { const fake = Fake(TestObjectThree).create(); expect(fake.is_fake).toBe(true); @@ -320,9 +418,7 @@ class TestObjectFiveMultipleArgs { class TestObjectFiveServiceMultipleArgs { map = new Map(); get(key, defaultValue) { - return this.map.has(key) - ? this.map.get(key) - : defaultValue; + return this.map.has(key) ? this.map.get(key) : defaultValue; } } diff --git a/tests/cjs/unit/mod/mock.test.js b/tests/cjs/unit/mod/mock.test.js index 1889b7d2..009be773 100644 --- a/tests/cjs/unit/mod/mock.test.js +++ b/tests/cjs/unit/mod/mock.test.js @@ -122,9 +122,7 @@ class TestObjectFiveMultipleArgs { class TestObjectFiveServiceMultipleArgs { map = new Map(); get(key, defaultValue) { - return this.map.has(key) - ? this.map.get(key) - : defaultValue; + return this.map.has(key) ? this.map.get(key) : defaultValue; } } diff --git a/tests/esm/unit/mod/fake.test.ts b/tests/esm/unit/mod/fake.test.ts index 891bb0b7..f20f8382 100644 --- a/tests/esm/unit/mod/fake.test.ts +++ b/tests/esm/unit/mod/fake.test.ts @@ -251,17 +251,17 @@ describe("Fake()", () => { it( ".willReturn((...) => {...}) returns true|false depending on given args", (): void => { - const mockFiveService = Fake(TestObjectFiveService) + const fakeFiveService = Fake(TestObjectFiveService) .create(); - const mockFive = Fake(TestObjectFive) - .withConstructorArgs(mockFiveService) + const fakeFive = Fake(TestObjectFive) + .withConstructorArgs(fakeFiveService) .create(); - assertEquals(mockFive.is_fake, true); - assertEquals(mockFiveService.is_fake, true); + assertEquals(fakeFive.is_fake, true); + assertEquals(fakeFiveService.is_fake, true); - mockFiveService + fakeFiveService .method("get") .willReturn((key: string, _defaultValue: number | string) => { if (key == "host") { @@ -275,10 +275,10 @@ describe("Fake()", () => { return undefined; }); - // `false` because `mockFiveService.get("port") == 3000` - assertEquals(mockFive.send(), false); + // `false` because `fakeFiveService.get("port") == 3000` + assertEquals(fakeFive.send(), false); - mockFiveService + fakeFiveService .method("get") .willReturn((key: string, _defaultValue: number | string) => { if (key == "host") { @@ -292,25 +292,25 @@ describe("Fake()", () => { return undefined; }); - // `true` because `mockFiveService.get("port") != 3000` - assertEquals(mockFive.send(), true); + // `true` because `fakeFiveService.get("port") != 3000` + assertEquals(fakeFive.send(), true); }, ); it( `.willReturn((...) => {...}) returns true|false depending on given args (multiple args)`, (): void => { - const mockFiveService = Fake(TestObjectFiveServiceMultipleArgs) + const fakeFiveService = Fake(TestObjectFiveServiceMultipleArgs) .create(); - const mockFive = Fake(TestObjectFiveMultipleArgs) - .withConstructorArgs(mockFiveService) + const fakeFive = Fake(TestObjectFiveMultipleArgs) + .withConstructorArgs(fakeFiveService) .create(); - assertEquals(mockFive.is_fake, true); - assertEquals(mockFiveService.is_fake, true); + assertEquals(fakeFive.is_fake, true); + assertEquals(fakeFiveService.is_fake, true); - mockFiveService + fakeFiveService .method("get") .willReturn((key: string, defaultValue: number | string) => { if (key == "host" && defaultValue == "localhost") { @@ -324,10 +324,10 @@ describe("Fake()", () => { return undefined; }); - // `false` because `mockFiveService.get("port") == 3000` - assertEquals(mockFive.send(), false); + // `false` because `fakeFiveService.get("port") == 3000` + assertEquals(fakeFive.send(), false); - mockFiveService + fakeFiveService .method("get") .willReturn((key: string, defaultValue: string | number) => { if (key == "host" && defaultValue == "localhost") { @@ -341,8 +341,8 @@ describe("Fake()", () => { return undefined; }); - // `true` because `mockFiveService.get("port") != 3000` - assertEquals(mockFive.send(), true); + // `true` because `fakeFiveService.get("port") != 3000` + assertEquals(fakeFive.send(), true); }, ); }); From 5143459d88f3ac9a66838037f89efbb589af0f95 Mon Sep 17 00:00:00 2001 From: Eric Crooks Date: Sun, 11 Dec 2022 01:02:35 -0500 Subject: [PATCH 18/22] chore: bump deps; fix broken tests after bump --- console/deps.ts | 4 ++-- tests/deno/deps.ts | 2 +- tests/deno/unit/mod/mock_test.ts | 2 +- tests/deno/unit/mod/spy_test.ts | 18 +++++++++--------- tests/deno/unit/mod/stub_test.ts | 8 ++++---- tests/deps.ts | 2 +- 6 files changed, 18 insertions(+), 18 deletions(-) diff --git a/console/deps.ts b/console/deps.ts index 2f16eb44..3c73a138 100644 --- a/console/deps.ts +++ b/console/deps.ts @@ -2,5 +2,5 @@ export { emptyDirSync, ensureDirSync, walk, -} from "https://deno.land/std@0.141.0/fs/mod.ts"; -export { copySync } from "https://deno.land/std@0.141.0/fs/copy.ts"; +} from "https://deno.land/std@0.167.0/fs/mod.ts"; +export { copySync } from "https://deno.land/std@0.167.0/fs/copy.ts"; diff --git a/tests/deno/deps.ts b/tests/deno/deps.ts index 28112501..3e64c5ee 100644 --- a/tests/deno/deps.ts +++ b/tests/deno/deps.ts @@ -1,4 +1,4 @@ export { assertEquals, assertThrows, -} from "https://deno.land/std@0.134.0/testing/asserts.ts"; +} from "https://deno.land/std@0.167.0/testing/asserts.ts"; diff --git a/tests/deno/unit/mod/mock_test.ts b/tests/deno/unit/mod/mock_test.ts index c46a9b9e..f0d91478 100644 --- a/tests/deno/unit/mod/mock_test.ts +++ b/tests/deno/unit/mod/mock_test.ts @@ -162,7 +162,7 @@ Deno.test("Mock()", async (t) => { name: "something", }); - assertEquals(mock.test(), { name: "something" }); + assertEquals(mock.test() as unknown, { name: "something" }); assertEquals(mock.calls.test, 2); assertEquals(mock.calls.hello, 2); }, diff --git a/tests/deno/unit/mod/spy_test.ts b/tests/deno/unit/mod/spy_test.ts index 0db3c81f..1f67bc7a 100644 --- a/tests/deno/unit/mod/spy_test.ts +++ b/tests/deno/unit/mod/spy_test.ts @@ -177,7 +177,7 @@ Deno.test("Spy()", async (t) => { ); // Fake's have working implementations, so we expect a real call - assertEquals(resource.methodThatGets(), { + assertEquals(resource.methodThatGets() as unknown, { stubbed: "return", value: "here", }); @@ -237,7 +237,7 @@ Deno.test("Spy()", async (t) => { ); // Fake's have working implementations, so we expect a real call - assertEquals(resource.methodThatGets("param1", "param2"), { + assertEquals(resource.methodThatGets("param1", "param2") as unknown, { hello: "world", }); @@ -249,7 +249,7 @@ Deno.test("Spy()", async (t) => { // Also, you can call it again and do further verification. Since we are calling the same method again, the call count should be incremented by 1. assertEquals( - resource.methodThatGets("anotherParam1", "anotherParam2"), + resource.methodThatGets("anotherParam1", "anotherParam2") as unknown, { hello: "world" }, ); spyMethod.verify() @@ -292,7 +292,7 @@ Deno.test("Spy()", async (t) => { ); // Fake's have working implementations, so we expect a real call - assertEquals(fake.methodThatGets(), { + assertEquals(fake.methodThatGets() as unknown, { hello: "world", }); @@ -304,7 +304,7 @@ Deno.test("Spy()", async (t) => { // Also, you can call it again and do further verification. Since we are calling the same method again, the call count should be incremented by 1. assertEquals( - fake.methodThatGets(), + fake.methodThatGets() as unknown, { hello: "world" }, ); spyMethod.verify() @@ -362,7 +362,7 @@ Deno.test("Spy()", async (t) => { ); // Fake's have working implementations, so we expect a real call - assertEquals(fake.methodThatGets("param1", "param2"), { + assertEquals(fake.methodThatGets("param1", "param2") as unknown, { hello: "world", }); @@ -374,7 +374,7 @@ Deno.test("Spy()", async (t) => { // Also, you can call it again and do further verification. Since we are calling the same method again, the call count should be incremented by 1. assertEquals( - fake.methodThatGets("anotherParam1", "anotherParam2"), + fake.methodThatGets("anotherParam1", "anotherParam2") as unknown, { hello: "world" }, ); spyMethod.verify() @@ -410,7 +410,7 @@ Deno.test("Spy()", async (t) => { // Since no return value was specified, the default "spy-stubbed" should // be used - assertEquals(spyHello(), "spy-stubbed"); + assertEquals(spyHello() as unknown, "spy-stubbed"); // Here we give `hello` a new return value. The return value must be of // the same return type. @@ -454,7 +454,7 @@ Deno.test("Spy()", async (t) => { // Since no return value was specified, the default "spy-stubbed" should // be used assertEquals( - spyHello("doth mother know you weareth her drapes", true), + spyHello("doth mother know you weareth her drapes", true) as unknown, "spy-stubbed", ); diff --git a/tests/deno/unit/mod/stub_test.ts b/tests/deno/unit/mod/stub_test.ts index bc1ca8fc..a438b314 100644 --- a/tests/deno/unit/mod/stub_test.ts +++ b/tests/deno/unit/mod/stub_test.ts @@ -18,10 +18,10 @@ Deno.test("Stub()", async (t) => { Stub(server, "greeting", null); assertEquals(server.greeting, null); Stub(server, "greeting", true); - assertEquals(server.greeting, true); + assertEquals(server.greeting as unknown, true); const obj = { test: "hello" }; Stub(server, "greeting", obj); - assertEquals(server.greeting, obj); + assertEquals(server.greeting as unknown, obj); }); await t.step("can stub a class method", () => { @@ -32,10 +32,10 @@ Deno.test("Stub()", async (t) => { Stub(server, "methodThatLogs", null); assertEquals(server.methodThatLogs(), null); Stub(server, "methodThatLogs", true); - assertEquals(server.methodThatLogs(), true); + assertEquals(server.methodThatLogs() as unknown, true); const obj = { test: "hello" }; Stub(server, "methodThatLogs", obj); - assertEquals(server.methodThatLogs(), obj); + assertEquals(server.methodThatLogs() as unknown, obj); }); await t.step("can return a stubbed function", () => { diff --git a/tests/deps.ts b/tests/deps.ts index 81d6d28d..3e64c5ee 100644 --- a/tests/deps.ts +++ b/tests/deps.ts @@ -1,4 +1,4 @@ export { assertEquals, assertThrows, -} from "https://deno.land/std@0.135.0/testing/asserts.ts"; +} from "https://deno.land/std@0.167.0/testing/asserts.ts"; From 1e4b15a9a3de0621939a6c4b79666cb8f3ed075c Mon Sep 17 00:00:00 2001 From: Eric Crooks Date: Sun, 11 Dec 2022 01:09:53 -0500 Subject: [PATCH 19/22] chore: debug --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 7dec2778..13766ca3 100644 --- a/package.json +++ b/package.json @@ -12,8 +12,8 @@ "build:cjs": "tsc --project tsconfig.cjs.json", "build:conversion-workspace": "deno run --allow-read --allow-write ./console/build_esm_lib.ts", "build:esm": "tsc --project tsconfig.esm.json", - "build:esm-lib": "console/build_esm_lib ./src ./mod.ts --workspace=./tmp/conversion_workspace", - "build:windows": "bash console/build_esm_lib && yarn && yarn build:cjs && yarn build:esm", + "build:esm-lib": "console/build_esm_lib ./src ./mod.ts --workspace=./tmp/conversion_workspace --debug", + "build:windows": "bash console/build_esm_lib ./src ./mod.ts --workspace=./tmp/conversion_workspace --debug && yarn && yarn build:cjs && yarn build:esm", "release": "yarn publish --access public", "test": "yarn test:deno && yarn test:cjs && yarn test:esm", "test:cjs": "yarn jest tests/cjs/", From b2da8c3b3a42866a10001e5250bd8f45426e6df7 Mon Sep 17 00:00:00 2001 From: Breno Salles Date: Sat, 17 Dec 2022 18:41:33 +0000 Subject: [PATCH 20/22] Apply suggestions from code review Co-authored-by: Edward Bebbington <47337480+ebebbington@users.noreply.github.com> --- console/build_esm_lib.ts | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/console/build_esm_lib.ts b/console/build_esm_lib.ts index 520054bd..eaf6aa59 100644 --- a/console/build_esm_lib.ts +++ b/console/build_esm_lib.ts @@ -158,7 +158,7 @@ function logDebug(...msg: unknown[]): void { */ function optionEnabled(option: string): boolean { const optionIndex = args.indexOf(option); - const enabled = optionIndex != -1; + const enabled = optionIndex !== -1; if (enabled) { args.splice(optionIndex, 1); @@ -173,11 +173,7 @@ function optionEnabled(option: string): boolean { * @returns The value of the option if the option exists. */ function optionValue(option: string): boolean { - const extractedOption = args.filter((arg: string) => { - if (arg.includes(option)) { - return true; - } - }); + const extractedOption = args.filter(arg => arg.includes(option)); if (!extractedOption.length) { return; From c7b4c995cef77e8b9f2dabddbb18651d69d27df1 Mon Sep 17 00:00:00 2001 From: Eric Crooks Date: Sun, 18 Dec 2022 19:43:26 -0500 Subject: [PATCH 21/22] chore: deno fmt --- console/build_esm_lib.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/console/build_esm_lib.ts b/console/build_esm_lib.ts index eaf6aa59..e22b2464 100644 --- a/console/build_esm_lib.ts +++ b/console/build_esm_lib.ts @@ -173,7 +173,7 @@ function optionEnabled(option: string): boolean { * @returns The value of the option if the option exists. */ function optionValue(option: string): boolean { - const extractedOption = args.filter(arg => arg.includes(option)); + const extractedOption = args.filter((arg) => arg.includes(option)); if (!extractedOption.length) { return; From 474eba0ee2044dc77c78d05d877936d214044d95 Mon Sep 17 00:00:00 2001 From: Eric Crooks Date: Sat, 24 Dec 2022 16:12:42 -0500 Subject: [PATCH 22/22] chore: accidental package using 'yarn add global' --- package.json | 1 - 1 file changed, 1 deletion(-) diff --git a/package.json b/package.json index 13766ca3..698d253a 100644 --- a/package.json +++ b/package.json @@ -27,7 +27,6 @@ "@types/jest": "27.x", "@types/node": "16.x", "babel-jest": "27.x", - "global": "4.x", "jest": "27.x", "ts-jest": "27.x", "ts-node": "10.x",