From 39bfc7df02d6d915c04731591dc82842e58a7e44 Mon Sep 17 00:00:00 2001 From: Dummy Date: Sun, 3 Mar 2019 10:59:44 +0100 Subject: [PATCH] fix for bug where arg.is was called twice. --- dist/spec/issues/11.test.d.ts | 1 + dist/spec/issues/11.test.js | 64 +++++++++++++++++++++++++ dist/spec/issues/11.test.js.map | 1 + dist/spec/issues/8.test.js | 2 +- dist/spec/issues/8.test.js.map | 2 +- dist/src/states/ContextState.d.ts | 5 +- dist/src/states/ContextState.js.map | 2 +- dist/src/states/FunctionState.d.ts | 2 +- dist/src/states/FunctionState.js | 10 ++-- dist/src/states/FunctionState.js.map | 2 +- dist/src/states/GetPropertyState.js | 7 +-- dist/src/states/GetPropertyState.js.map | 2 +- spec/issues/11.test.ts | 28 +++++++++++ src/states/ContextState.ts | 5 +- src/states/FunctionState.ts | 11 +++-- src/states/GetPropertyState.ts | 10 ++-- 16 files changed, 130 insertions(+), 24 deletions(-) create mode 100644 dist/spec/issues/11.test.d.ts create mode 100644 dist/spec/issues/11.test.js create mode 100644 dist/spec/issues/11.test.js.map create mode 100644 spec/issues/11.test.ts diff --git a/dist/spec/issues/11.test.d.ts b/dist/spec/issues/11.test.d.ts new file mode 100644 index 0000000..cb0ff5c --- /dev/null +++ b/dist/spec/issues/11.test.d.ts @@ -0,0 +1 @@ +export {}; diff --git a/dist/spec/issues/11.test.js b/dist/spec/issues/11.test.js new file mode 100644 index 0000000..ee27853 --- /dev/null +++ b/dist/spec/issues/11.test.js @@ -0,0 +1,64 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +var __generator = (this && this.__generator) || function (thisArg, body) { + var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; + return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; + function verb(n) { return function (v) { return step([n, v]); }; } + function step(op) { + if (f) throw new TypeError("Generator is already executing."); + while (_) try { + if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; + if (y = 0, t) op = [op[0] & 2, t.value]; + switch (op[0]) { + case 0: case 1: t = op; break; + case 4: _.label++; return { value: op[1], done: false }; + case 5: _.label++; y = op[1]; op = [0]; continue; + case 7: op = _.ops.pop(); _.trys.pop(); continue; + default: + if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } + if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } + if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } + if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } + if (t[2]) _.ops.pop(); + _.trys.pop(); continue; + } + op = body.call(thisArg, _); + } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } + if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; + } +}; +var _this = this; +Object.defineProperty(exports, "__esModule", { value: true }); +var ava_1 = require("ava"); +var index_1 = require("../../src/index"); +var RealCalculator = /** @class */ (function () { + function RealCalculator() { + } + RealCalculator.prototype.add = function (addands) { + return addands.op1 + addands.op2; + }; + return RealCalculator; +}()); +ava_1.default('issue 11: arg.is is only called once', function (t) { return __awaiter(_this, void 0, void 0, function () { + var mockedCalculator, count; + return __generator(this, function (_a) { + mockedCalculator = index_1.Substitute.for(); + mockedCalculator.add(index_1.Arg.any()).returns(4); + count = 0; + mockedCalculator.add({ op1: 1, op2: 2 }); + mockedCalculator.received(1).add(index_1.Arg.is(function (a) { + count++; + return a.op1 === 1 && a.op2 === 2; + })); + t.is(count, 1); + return [2 /*return*/]; + }); +}); }); +//# sourceMappingURL=11.test.js.map \ No newline at end of file diff --git a/dist/spec/issues/11.test.js.map b/dist/spec/issues/11.test.js.map new file mode 100644 index 0000000..7c46132 --- /dev/null +++ b/dist/spec/issues/11.test.js.map @@ -0,0 +1 @@ +{"version":3,"file":"11.test.js","sourceRoot":"","sources":["../../../spec/issues/11.test.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,iBA2BG;;AA3BH,2BAAuB;AACvB,yCAAkD;AAOlD;IAAA;IAIA,CAAC;IAHG,4BAAG,GAAH,UAAI,OAAgB;QAChB,OAAO,OAAO,CAAC,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC;IACrC,CAAC;IACL,qBAAC;AAAD,CAAC,AAJD,IAIC;AAED,aAAI,CAAC,sCAAsC,EAAE,UAAM,CAAC;;;QAC5C,gBAAgB,GAAG,kBAAU,CAAC,GAAG,EAAkB,CAAC;QACxD,gBAAgB,CAAC,GAAG,CAAC,WAAG,CAAC,GAAG,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;QAEvC,KAAK,GAAG,CAAC,CAAC;QACd,gBAAgB,CAAC,GAAG,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,CAAC,CAAC;QAEzC,gBAAgB,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,WAAG,CAAC,EAAE,CAAC,UAAA,CAAC;YACrC,KAAK,EAAE,CAAC;YACR,OAAO,CAAC,CAAC,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC;QACtC,CAAC,CAAC,CAAC,CAAC;QAEJ,CAAC,CAAC,EAAE,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;;;KAClB,CAAC,CAAC","sourcesContent":["import test from 'ava';\r\nimport { Substitute, Arg } from '../../src/index';\r\n\r\ntype Addands = {\r\n op1: number;\r\n op2: number;\r\n}\r\n\r\nclass RealCalculator {\r\n add(addands: Addands): number {\r\n return addands.op1 + addands.op2;\r\n }\r\n}\r\n\r\ntest('issue 11: arg.is is only called once', async t => {\r\n let mockedCalculator = Substitute.for();\r\n mockedCalculator.add(Arg.any()).returns(4);\r\n\r\n let count = 0;\r\n mockedCalculator.add({ op1: 1, op2: 2 });\r\n\r\n mockedCalculator.received(1).add(Arg.is(a => {\r\n count++;\r\n return a.op1 === 1 && a.op2 === 2;\r\n }));\r\n\r\n t.is(count, 1);\r\n});"]} \ No newline at end of file diff --git a/dist/spec/issues/8.test.js b/dist/spec/issues/8.test.js index 233fd19..3150472 100644 --- a/dist/spec/issues/8.test.js +++ b/dist/spec/issues/8.test.js @@ -67,7 +67,7 @@ var ClassC = /** @class */ (function () { }; return ClassC; }()); -ava_1.default('issue 9: can record method with 0 arguments', function (t) { return __awaiter(_this, void 0, void 0, function () { +ava_1.default('issue 8: can use substitute in arguments', function (t) { return __awaiter(_this, void 0, void 0, function () { var classBMock, classC; return __generator(this, function (_a) { classBMock = Index_1.default.for(); diff --git a/dist/spec/issues/8.test.js.map b/dist/spec/issues/8.test.js.map index 25c0e61..50c44e0 100644 --- a/dist/spec/issues/8.test.js.map +++ b/dist/spec/issues/8.test.js.map @@ -1 +1 @@ -{"version":3,"file":"8.test.js","sourceRoot":"","sources":["../../../spec/issues/8.test.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,iBAuCG;;AAvCH,yCAAyC;AACzC,2BAAuB;AAEvB;IACC;IAAc,CAAC;IAEf,wBAAO,GAAP;QACC,OAAO,KAAK,CAAA;IACb,CAAC;IACF,aAAC;AAAD,CAAC,AAND,IAMC;AAED;IACC,gBACS,MAAc;QAAd,WAAM,GAAN,MAAM,CAAQ;IACpB,CAAC;IAEJ,wBAAO,GAAP;QACC,OAAO,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;IAC9B,CAAC;IAED,yBAAQ,GAAR;QACC,OAAO,KAAK,CAAA;IACb,CAAC;IACF,aAAC;AAAD,CAAC,AAZD,IAYC;AAED;IACC,gBACS,MAAc;QAAd,WAAM,GAAN,MAAM,CAAQ;IACpB,CAAC;IAEJ,wBAAO,GAAP;QACC,OAAO,IAAI,CAAC,MAAM,CAAC,QAAQ,EAAE,CAAC;IAC/B,CAAC;IACF,aAAC;AAAD,CAAC,AARD,IAQC;AAED,aAAI,CAAC,6CAA6C,EAAE,UAAM,CAAC;;;QACjD,UAAU,GAAG,eAAU,CAAC,GAAG,EAAU,CAAC;QACtC,MAAM,GAAG,IAAI,MAAM,CAAC,UAAU,CAAC,CAAC;QACtC,CAAC,CAAC,GAAG,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;;;KACvB,CAAC,CAAC","sourcesContent":["import Substitute from \"../../src/Index\";\r\nimport test from 'ava';\r\n\r\nclass ClassA {\r\n\tconstructor(){}\r\n\r\n\tmethodA(): string {\r\n\t\treturn 'abc'\r\n\t}\r\n}\r\n\r\nclass ClassB {\r\n\tconstructor(\r\n\t\tprivate classA: ClassA\r\n\t) {}\r\n\r\n\tmethodB(): string {\r\n\t\treturn this.classA.methodA();\r\n\t}\r\n\r\n\tmethodB2(): string {\r\n\t\treturn 'def'\r\n\t}\r\n}\r\n\r\nclass ClassC {\r\n\tconstructor(\r\n\t\tprivate classB: ClassB\r\n\t) {}\r\n\r\n\tmethodC(): string {\r\n\t\treturn this.classB.methodB2();\r\n\t}\r\n}\r\n\r\ntest('issue 9: can record method with 0 arguments', async t => {\r\n const classBMock = Substitute.for();\r\n const classC = new ClassC(classBMock);\r\n t.not(classC, null);\r\n});"]} \ No newline at end of file +{"version":3,"file":"8.test.js","sourceRoot":"","sources":["../../../spec/issues/8.test.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,iBAuCG;;AAvCH,yCAAyC;AACzC,2BAAuB;AAEvB;IACC;IAAc,CAAC;IAEf,wBAAO,GAAP;QACC,OAAO,KAAK,CAAA;IACb,CAAC;IACF,aAAC;AAAD,CAAC,AAND,IAMC;AAED;IACC,gBACS,MAAc;QAAd,WAAM,GAAN,MAAM,CAAQ;IACpB,CAAC;IAEJ,wBAAO,GAAP;QACC,OAAO,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;IAC9B,CAAC;IAED,yBAAQ,GAAR;QACC,OAAO,KAAK,CAAA;IACb,CAAC;IACF,aAAC;AAAD,CAAC,AAZD,IAYC;AAED;IACC,gBACS,MAAc;QAAd,WAAM,GAAN,MAAM,CAAQ;IACpB,CAAC;IAEJ,wBAAO,GAAP;QACC,OAAO,IAAI,CAAC,MAAM,CAAC,QAAQ,EAAE,CAAC;IAC/B,CAAC;IACF,aAAC;AAAD,CAAC,AARD,IAQC;AAED,aAAI,CAAC,0CAA0C,EAAE,UAAM,CAAC;;;QAC9C,UAAU,GAAG,eAAU,CAAC,GAAG,EAAU,CAAC;QACtC,MAAM,GAAG,IAAI,MAAM,CAAC,UAAU,CAAC,CAAC;QACtC,CAAC,CAAC,GAAG,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;;;KACvB,CAAC,CAAC","sourcesContent":["import Substitute from \"../../src/Index\";\r\nimport test from 'ava';\r\n\r\nclass ClassA {\r\n\tconstructor(){}\r\n\r\n\tmethodA(): string {\r\n\t\treturn 'abc'\r\n\t}\r\n}\r\n\r\nclass ClassB {\r\n\tconstructor(\r\n\t\tprivate classA: ClassA\r\n\t) {}\r\n\r\n\tmethodB(): string {\r\n\t\treturn this.classA.methodA();\r\n\t}\r\n\r\n\tmethodB2(): string {\r\n\t\treturn 'def'\r\n\t}\r\n}\r\n\r\nclass ClassC {\r\n\tconstructor(\r\n\t\tprivate classB: ClassB\r\n\t) {}\r\n\r\n\tmethodC(): string {\r\n\t\treturn this.classB.methodB2();\r\n\t}\r\n}\r\n\r\ntest('issue 8: can use substitute in arguments', async t => {\r\n const classBMock = Substitute.for();\r\n const classC = new ClassC(classBMock);\r\n t.not(classC, null);\r\n});"]} \ No newline at end of file diff --git a/dist/src/states/ContextState.d.ts b/dist/src/states/ContextState.d.ts index e95482f..e2faf5c 100644 --- a/dist/src/states/ContextState.d.ts +++ b/dist/src/states/ContextState.d.ts @@ -1,8 +1,9 @@ -import { Context } from "src/Context"; +import { Context } from "../Context"; +import { FunctionState } from "./FunctionState"; export declare type PropertyKey = string | number | symbol; export interface ContextState { onSwitchedTo?(context: Context): void; - apply(context: Context, args: any[]): any; + apply(context: Context, args: any[], matchingFunctionStates?: FunctionState[]): any; set(context: Context, property: PropertyKey, value: any): void; get(context: Context, property: PropertyKey): any; } diff --git a/dist/src/states/ContextState.js.map b/dist/src/states/ContextState.js.map index daad1d3..583c9f6 100644 --- a/dist/src/states/ContextState.js.map +++ b/dist/src/states/ContextState.js.map @@ -1 +1 @@ -{"version":3,"file":"ContextState.js","sourceRoot":"","sources":["../../../src/states/ContextState.ts"],"names":[],"mappings":"","sourcesContent":["import { Context } from \"src/Context\";\r\n\r\nexport type PropertyKey = string|number|symbol;\r\n\r\nexport interface ContextState {\r\n onSwitchedTo?(context: Context): void;\r\n apply(context: Context, args: any[]): any;\r\n set(context: Context, property: PropertyKey, value: any): void;\r\n get(context: Context, property: PropertyKey): any;\r\n}"]} \ No newline at end of file +{"version":3,"file":"ContextState.js","sourceRoot":"","sources":["../../../src/states/ContextState.ts"],"names":[],"mappings":"","sourcesContent":["import { Context } from \"../Context\";\r\nimport { FunctionState } from \"./FunctionState\";\r\n\r\nexport type PropertyKey = string|number|symbol;\r\n\r\nexport interface ContextState {\r\n onSwitchedTo?(context: Context): void;\r\n apply(context: Context, args: any[], matchingFunctionStates?: FunctionState[]): any;\r\n set(context: Context, property: PropertyKey, value: any): void;\r\n get(context: Context, property: PropertyKey): any;\r\n}"]} \ No newline at end of file diff --git a/dist/src/states/FunctionState.d.ts b/dist/src/states/FunctionState.d.ts index 9f8e17a..0d28356 100644 --- a/dist/src/states/FunctionState.d.ts +++ b/dist/src/states/FunctionState.d.ts @@ -11,7 +11,7 @@ export declare class FunctionState implements ContextState { readonly callCount: number; readonly property: string | number | symbol; constructor(_getPropertyState: GetPropertyState, ...args: any[]); - apply(context: Context, args: any[]): any; + apply(context: Context, args: any[], matchingFunctionStates: FunctionState[]): any; set(context: Context, property: PropertyKey, value: any): void; get(context: Context, property: PropertyKey): any; } diff --git a/dist/src/states/FunctionState.js b/dist/src/states/FunctionState.js index 1325401..b5709a8 100644 --- a/dist/src/states/FunctionState.js +++ b/dist/src/states/FunctionState.js @@ -46,13 +46,15 @@ var FunctionState = /** @class */ (function () { enumerable: true, configurable: true }); - FunctionState.prototype.apply = function (context, args) { + FunctionState.prototype.apply = function (context, args, matchingFunctionStates) { var e_1, _a, e_2, _b; var callCount = this._callCount; var hasExpectations = context.initialState.hasExpectations; - var matchingFunctionStates = this._getPropertyState - .recordedFunctionStates - .filter(function (x) { return Utilities_1.areArgumentArraysEqual(x.arguments, args); }); + if (!matchingFunctionStates) { + matchingFunctionStates = this._getPropertyState + .recordedFunctionStates + .filter(function (x) { return Utilities_1.areArgumentArraysEqual(x.arguments, args); }); + } if (hasExpectations) { callCount = matchingFunctionStates .map(function (x) { return x.callCount; }) diff --git a/dist/src/states/FunctionState.js.map b/dist/src/states/FunctionState.js.map index 84a0e5d..dfcd85e 100644 --- a/dist/src/states/FunctionState.js.map +++ b/dist/src/states/FunctionState.js.map @@ -1 +1 @@ -{"version":3,"file":"FunctionState.js","sourceRoot":"","sources":["../../../src/states/FunctionState.ts"],"names":[],"mappings":"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","sourcesContent":["import { ContextState, PropertyKey } from \"./ContextState\";\r\nimport { Context } from \"src/Context\";\r\nimport { stringifyArguments, stringifyCalls, areArgumentsEqual, areArgumentArraysEqual } from \"../Utilities\";\r\nimport { GetPropertyState } from \"./GetPropertyState\";\r\nimport { Argument } from \"../Arguments\";\r\n\r\nconst Nothing = Symbol();\r\n\r\nexport class FunctionState implements ContextState {\r\n private returns: any[]|Symbol;\r\n private mimicks: Function|null;\r\n\r\n private _callCount: number;\r\n private _arguments: any[];\r\n\r\n public get arguments() {\r\n return this._arguments;\r\n }\r\n\r\n public get callCount() {\r\n return this._callCount;\r\n }\r\n\r\n public get property() {\r\n return this._getPropertyState.property;\r\n }\r\n\r\n constructor(private _getPropertyState: GetPropertyState, ...args: any[]) {\r\n this.returns = Nothing;\r\n this.mimicks = null;\r\n\r\n this._arguments = args;\r\n this._callCount = 0;\r\n }\r\n\r\n apply(context: Context, args: any[]) {\r\n let callCount = this._callCount;\r\n const hasExpectations = context.initialState.hasExpectations;\r\n const matchingFunctionStates = this._getPropertyState\r\n .recordedFunctionStates\r\n .filter(x => areArgumentArraysEqual(x.arguments, args));\r\n if(hasExpectations) {\r\n callCount = matchingFunctionStates\r\n .map(x => x.callCount)\r\n .reduce((a, b) => a + b, 0);\r\n }\r\n\r\n context.initialState.assertCallCountMatchesExpectations(\r\n this._getPropertyState.recordedFunctionStates,\r\n callCount,\r\n 'method',\r\n this.property,\r\n args);\r\n\r\n if(!hasExpectations) {\r\n this._callCount++;\r\n\r\n for(let matchingFunctionState of matchingFunctionStates)\r\n for(let argument of matchingFunctionState.arguments) {\r\n if(!(argument instanceof Argument))\r\n continue;\r\n\r\n const indexOffset = matchingFunctionState\r\n .arguments\r\n .indexOf(argument);\r\n const myArg = args[indexOffset];\r\n if(myArg instanceof Argument)\r\n continue;\r\n\r\n argument.encounteredValues.push(myArg);\r\n }\r\n }\r\n\r\n if(this.mimicks)\r\n return this.mimicks.apply(this.mimicks, args);\r\n\r\n if(this.returns === Nothing)\r\n return context.proxy;\r\n\r\n var returnsArray = this.returns as any[];\r\n if(returnsArray.length === 1)\r\n return returnsArray[0];\r\n\r\n return returnsArray[this._callCount-1];\r\n }\r\n\r\n set(context: Context, property: PropertyKey, value: any) {\r\n }\r\n\r\n get(context: Context, property: PropertyKey) {\r\n if (property === 'then')\r\n return void 0;\r\n\r\n if(property === 'mimicks') {\r\n return (input: Function) => {\r\n this.mimicks = input;\r\n this._callCount--;\r\n\r\n context.state = context.initialState;\r\n }\r\n }\r\n\r\n if(property === 'returns') {\r\n if(this.returns !== Nothing)\r\n throw new Error('The return value for the function ' + this._getPropertyState.toString() + ' with ' + stringifyArguments(this._arguments) + ' has already been set to ' + this.returns);\r\n\r\n return (...returns: any[]) => {\r\n this.returns = returns;\r\n this._callCount--;\r\n\r\n context.state = context.initialState;\r\n };\r\n }\r\n\r\n return context.proxy;\r\n }\r\n}"]} \ No newline at end of file +{"version":3,"file":"FunctionState.js","sourceRoot":"","sources":["../../../src/states/FunctionState.ts"],"names":[],"mappings":"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","sourcesContent":["import { ContextState, PropertyKey } from \"./ContextState\";\r\nimport { Context } from \"src/Context\";\r\nimport { stringifyArguments, stringifyCalls, areArgumentsEqual, areArgumentArraysEqual } from \"../Utilities\";\r\nimport { GetPropertyState } from \"./GetPropertyState\";\r\nimport { Argument } from \"../Arguments\";\r\n\r\nconst Nothing = Symbol();\r\n\r\nexport class FunctionState implements ContextState {\r\n private returns: any[]|Symbol;\r\n private mimicks: Function|null;\r\n\r\n private _callCount: number;\r\n private _arguments: any[];\r\n\r\n public get arguments() {\r\n return this._arguments;\r\n }\r\n\r\n public get callCount() {\r\n return this._callCount;\r\n }\r\n\r\n public get property() {\r\n return this._getPropertyState.property;\r\n }\r\n\r\n constructor(private _getPropertyState: GetPropertyState, ...args: any[]) {\r\n this.returns = Nothing;\r\n this.mimicks = null;\r\n\r\n this._arguments = args;\r\n this._callCount = 0;\r\n }\r\n\r\n apply(context: Context, args: any[], matchingFunctionStates: FunctionState[]) {\r\n let callCount = this._callCount;\r\n const hasExpectations = context.initialState.hasExpectations;\r\n if(!matchingFunctionStates) {\r\n matchingFunctionStates = this._getPropertyState\r\n .recordedFunctionStates\r\n .filter(x => areArgumentArraysEqual(x.arguments, args));\r\n }\r\n\r\n if(hasExpectations) {\r\n callCount = matchingFunctionStates\r\n .map(x => x.callCount)\r\n .reduce((a, b) => a + b, 0);\r\n }\r\n\r\n context.initialState.assertCallCountMatchesExpectations(\r\n this._getPropertyState.recordedFunctionStates,\r\n callCount,\r\n 'method',\r\n this.property,\r\n args);\r\n\r\n if(!hasExpectations) {\r\n this._callCount++;\r\n\r\n for(let matchingFunctionState of matchingFunctionStates)\r\n for(let argument of matchingFunctionState.arguments) {\r\n if(!(argument instanceof Argument))\r\n continue;\r\n\r\n const indexOffset = matchingFunctionState\r\n .arguments\r\n .indexOf(argument);\r\n const myArg = args[indexOffset];\r\n if(myArg instanceof Argument)\r\n continue;\r\n\r\n argument.encounteredValues.push(myArg);\r\n }\r\n }\r\n\r\n if(this.mimicks)\r\n return this.mimicks.apply(this.mimicks, args);\r\n\r\n if(this.returns === Nothing)\r\n return context.proxy;\r\n\r\n var returnsArray = this.returns as any[];\r\n if(returnsArray.length === 1)\r\n return returnsArray[0];\r\n\r\n return returnsArray[this._callCount-1];\r\n }\r\n\r\n set(context: Context, property: PropertyKey, value: any) {\r\n }\r\n\r\n get(context: Context, property: PropertyKey) {\r\n if (property === 'then')\r\n return void 0;\r\n\r\n if(property === 'mimicks') {\r\n return (input: Function) => {\r\n this.mimicks = input;\r\n this._callCount--;\r\n\r\n context.state = context.initialState;\r\n }\r\n }\r\n\r\n if(property === 'returns') {\r\n if(this.returns !== Nothing)\r\n throw new Error('The return value for the function ' + this._getPropertyState.toString() + ' with ' + stringifyArguments(this._arguments) + ' has already been set to ' + this.returns);\r\n\r\n return (...returns: any[]) => {\r\n this.returns = returns;\r\n this._callCount--;\r\n\r\n context.state = context.initialState;\r\n };\r\n }\r\n\r\n return context.proxy;\r\n }\r\n}"]} \ No newline at end of file diff --git a/dist/src/states/GetPropertyState.js b/dist/src/states/GetPropertyState.js index 4707cd5..781b11a 100644 --- a/dist/src/states/GetPropertyState.js +++ b/dist/src/states/GetPropertyState.js @@ -61,9 +61,10 @@ var GetPropertyState = /** @class */ (function () { }); GetPropertyState.prototype.apply = function (context, args) { this._callCount = 0; - var matchingFunctionState = this._recordedFunctionStates.find(function (x) { return Utilities_1.areArgumentArraysEqual(x.arguments, args); }); - if (matchingFunctionState) { - return matchingFunctionState.apply(context, args); + var matchingFunctionStates = this._recordedFunctionStates.filter(function (x) { return Utilities_1.areArgumentArraysEqual(x.arguments, args); }); + if (matchingFunctionStates.length > 0) { + var matchingFunctionState = matchingFunctionStates[0]; + return matchingFunctionState.apply(context, args, matchingFunctionStates); } var functionState = new (FunctionState_1.FunctionState.bind.apply(FunctionState_1.FunctionState, __spread([void 0, this], args)))(); context.state = functionState; diff --git a/dist/src/states/GetPropertyState.js.map b/dist/src/states/GetPropertyState.js.map index b22a9ab..206444a 100644 --- a/dist/src/states/GetPropertyState.js.map +++ b/dist/src/states/GetPropertyState.js.map @@ -1 +1 @@ -{"version":3,"file":"GetPropertyState.js","sourceRoot":"","sources":["../../../src/states/GetPropertyState.ts"],"names":[],"mappings":"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","sourcesContent":["import { ContextState, PropertyKey } from \"./ContextState\";\r\nimport { Context } from \"src/Context\";\r\nimport { FunctionState } from \"./FunctionState\";\r\nimport { areArgumentsEqual, areArgumentArraysEqual } from \"../Utilities\";\r\n\r\nconst Nothing = Symbol();\r\n\r\nexport class GetPropertyState implements ContextState {\r\n private returns: any[]|Symbol;\r\n private mimicks: Function|null;\r\n\r\n private _callCount: number;\r\n private _recordedFunctionStates: FunctionState[];\r\n\r\n private get isFunction() {\r\n return this._recordedFunctionStates.length > 0;\r\n }\r\n\r\n public get property() {\r\n return this._property;\r\n }\r\n\r\n public get callCount() {\r\n return this._callCount;\r\n }\r\n\r\n public get recordedFunctionStates() {\r\n return [...this._recordedFunctionStates];\r\n }\r\n\r\n constructor(private _property: PropertyKey) {\r\n this.returns = Nothing;\r\n this.mimicks = null;\r\n\r\n this._recordedFunctionStates = [];\r\n this._callCount = 0;\r\n }\r\n\r\n apply(context: Context, args: any[]) {\r\n this._callCount = 0;\r\n\r\n const matchingFunctionState = this._recordedFunctionStates.find(x => areArgumentArraysEqual(x.arguments, args));\r\n if(matchingFunctionState) {\r\n return matchingFunctionState.apply(context, args);\r\n }\r\n\r\n var functionState = new FunctionState(this, ...args);\r\n context.state = functionState;\r\n\r\n this._recordedFunctionStates.push(functionState);\r\n\r\n return context.apply(args);\r\n }\r\n\r\n set(context: Context, property: PropertyKey, value: any) {\r\n }\r\n\r\n get(context: Context, property: PropertyKey) {\r\n const hasExpectations = context.initialState.hasExpectations;\r\n\r\n if (property === 'then')\r\n return void 0;\r\n\r\n if(this.isFunction)\r\n return context.proxy;\r\n\r\n if(property === 'mimicks') {\r\n return (input: Function) => {\r\n this.mimicks = input;\r\n this._callCount--;\r\n\r\n context.state = context.initialState;\r\n }\r\n }\r\n\r\n if(property === 'returns') {\r\n if(this.returns !== Nothing)\r\n throw new Error('The return value for the property ' + this._property.toString() + ' has already been set to ' + this.returns);\r\n\r\n return (...returns: any[]) => {\r\n this.returns = returns;\r\n this._callCount--;\r\n\r\n context.state = context.initialState;\r\n };\r\n }\r\n\r\n if(!hasExpectations) {\r\n this._callCount++;\r\n\r\n if(this.mimicks)\r\n return this.mimicks.apply(this.mimicks);\r\n\r\n if(this.returns !== Nothing) {\r\n var returnsArray = this.returns as any[];\r\n if(returnsArray.length === 1)\r\n return returnsArray[0];\r\n \r\n return returnsArray[this._callCount-1];\r\n }\r\n }\r\n\r\n context.initialState.assertCallCountMatchesExpectations(\r\n context.initialState.getPropertyStates,\r\n this.callCount,\r\n 'property',\r\n this.property,\r\n []);\r\n\r\n return context.proxy;\r\n }\r\n}"]} \ No newline at end of file +{"version":3,"file":"GetPropertyState.js","sourceRoot":"","sources":["../../../src/states/GetPropertyState.ts"],"names":[],"mappings":"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","sourcesContent":["import { ContextState, PropertyKey } from \"./ContextState\";\r\nimport { Context } from \"src/Context\";\r\nimport { FunctionState } from \"./FunctionState\";\r\nimport { areArgumentsEqual, areArgumentArraysEqual } from \"../Utilities\";\r\n\r\nconst Nothing = Symbol();\r\n\r\nexport class GetPropertyState implements ContextState {\r\n private returns: any[]|Symbol;\r\n private mimicks: Function|null;\r\n\r\n private _callCount: number;\r\n private _recordedFunctionStates: FunctionState[];\r\n\r\n private get isFunction() {\r\n return this._recordedFunctionStates.length > 0;\r\n }\r\n\r\n public get property() {\r\n return this._property;\r\n }\r\n\r\n public get callCount() {\r\n return this._callCount;\r\n }\r\n\r\n public get recordedFunctionStates() {\r\n return [...this._recordedFunctionStates];\r\n }\r\n\r\n constructor(private _property: PropertyKey) {\r\n this.returns = Nothing;\r\n this.mimicks = null;\r\n\r\n this._recordedFunctionStates = [];\r\n this._callCount = 0;\r\n }\r\n\r\n apply(context: Context, args: any[]) {\r\n this._callCount = 0;\r\n\r\n const matchingFunctionStates = this._recordedFunctionStates.filter(x => areArgumentArraysEqual(x.arguments, args));\r\n if(matchingFunctionStates.length > 0) {\r\n const matchingFunctionState = matchingFunctionStates[0];\r\n return matchingFunctionState.apply(\r\n context, \r\n args, \r\n matchingFunctionStates);\r\n }\r\n\r\n var functionState = new FunctionState(this, ...args);\r\n context.state = functionState;\r\n\r\n this._recordedFunctionStates.push(functionState);\r\n\r\n return context.apply(args);\r\n }\r\n\r\n set(context: Context, property: PropertyKey, value: any) {\r\n }\r\n\r\n get(context: Context, property: PropertyKey) {\r\n const hasExpectations = context.initialState.hasExpectations;\r\n\r\n if (property === 'then')\r\n return void 0;\r\n\r\n if(this.isFunction)\r\n return context.proxy;\r\n\r\n if(property === 'mimicks') {\r\n return (input: Function) => {\r\n this.mimicks = input;\r\n this._callCount--;\r\n\r\n context.state = context.initialState;\r\n }\r\n }\r\n\r\n if(property === 'returns') {\r\n if(this.returns !== Nothing)\r\n throw new Error('The return value for the property ' + this._property.toString() + ' has already been set to ' + this.returns);\r\n\r\n return (...returns: any[]) => {\r\n this.returns = returns;\r\n this._callCount--;\r\n\r\n context.state = context.initialState;\r\n };\r\n }\r\n\r\n if(!hasExpectations) {\r\n this._callCount++;\r\n\r\n if(this.mimicks)\r\n return this.mimicks.apply(this.mimicks);\r\n\r\n if(this.returns !== Nothing) {\r\n var returnsArray = this.returns as any[];\r\n if(returnsArray.length === 1)\r\n return returnsArray[0];\r\n \r\n return returnsArray[this._callCount-1];\r\n }\r\n }\r\n\r\n context.initialState.assertCallCountMatchesExpectations(\r\n context.initialState.getPropertyStates,\r\n this.callCount,\r\n 'property',\r\n this.property,\r\n []);\r\n\r\n return context.proxy;\r\n }\r\n}"]} \ No newline at end of file diff --git a/spec/issues/11.test.ts b/spec/issues/11.test.ts new file mode 100644 index 0000000..eb4d4cd --- /dev/null +++ b/spec/issues/11.test.ts @@ -0,0 +1,28 @@ +import test from 'ava'; +import { Substitute, Arg } from '../../src/index'; + +type Addands = { + op1: number; + op2: number; +} + +class RealCalculator { + add(addands: Addands): number { + return addands.op1 + addands.op2; + } +} + +test('issue 11: arg.is is only called once', async t => { + let mockedCalculator = Substitute.for(); + mockedCalculator.add(Arg.any()).returns(4); + + let count = 0; + mockedCalculator.add({ op1: 1, op2: 2 }); + + mockedCalculator.received(1).add(Arg.is(a => { + count++; + return a.op1 === 1 && a.op2 === 2; + })); + + t.is(count, 1); +}); \ No newline at end of file diff --git a/src/states/ContextState.ts b/src/states/ContextState.ts index 544c065..78d5da5 100644 --- a/src/states/ContextState.ts +++ b/src/states/ContextState.ts @@ -1,10 +1,11 @@ -import { Context } from "src/Context"; +import { Context } from "../Context"; +import { FunctionState } from "./FunctionState"; export type PropertyKey = string|number|symbol; export interface ContextState { onSwitchedTo?(context: Context): void; - apply(context: Context, args: any[]): any; + apply(context: Context, args: any[], matchingFunctionStates?: FunctionState[]): any; set(context: Context, property: PropertyKey, value: any): void; get(context: Context, property: PropertyKey): any; } \ No newline at end of file diff --git a/src/states/FunctionState.ts b/src/states/FunctionState.ts index 474d804..3989dce 100644 --- a/src/states/FunctionState.ts +++ b/src/states/FunctionState.ts @@ -33,12 +33,15 @@ export class FunctionState implements ContextState { this._callCount = 0; } - apply(context: Context, args: any[]) { + apply(context: Context, args: any[], matchingFunctionStates: FunctionState[]) { let callCount = this._callCount; const hasExpectations = context.initialState.hasExpectations; - const matchingFunctionStates = this._getPropertyState - .recordedFunctionStates - .filter(x => areArgumentArraysEqual(x.arguments, args)); + if(!matchingFunctionStates) { + matchingFunctionStates = this._getPropertyState + .recordedFunctionStates + .filter(x => areArgumentArraysEqual(x.arguments, args)); + } + if(hasExpectations) { callCount = matchingFunctionStates .map(x => x.callCount) diff --git a/src/states/GetPropertyState.ts b/src/states/GetPropertyState.ts index 1de783a..057e47a 100644 --- a/src/states/GetPropertyState.ts +++ b/src/states/GetPropertyState.ts @@ -39,9 +39,13 @@ export class GetPropertyState implements ContextState { apply(context: Context, args: any[]) { this._callCount = 0; - const matchingFunctionState = this._recordedFunctionStates.find(x => areArgumentArraysEqual(x.arguments, args)); - if(matchingFunctionState) { - return matchingFunctionState.apply(context, args); + const matchingFunctionStates = this._recordedFunctionStates.filter(x => areArgumentArraysEqual(x.arguments, args)); + if(matchingFunctionStates.length > 0) { + const matchingFunctionState = matchingFunctionStates[0]; + return matchingFunctionState.apply( + context, + args, + matchingFunctionStates); } var functionState = new FunctionState(this, ...args);