tsmockit 1.2.0 → 2.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Mock/Any.d.ts +6 -0
- package/Mock/Any.js +13 -0
- package/Mock/Constants.d.ts +1 -0
- package/Mock/Constants.js +2 -1
- package/Mock/FunctionMap.d.ts +7 -0
- package/Mock/{TypeLiterals.js → FunctionMap.js} +1 -1
- package/Mock/Mock.d.ts +33 -3
- package/Mock/Mock.js +89 -47
- package/Mock/SignatureMap.d.ts +5 -0
- package/Mock/SignatureMap.js +3 -0
- package/Mock/SignatureService.d.ts +3 -2
- package/Mock/SignatureService.js +20 -3
- package/Mock/module.d.ts +3 -0
- package/Mock/module.js +9 -0
- package/README.md +60 -81
- package/Utility/EmitEventAtElement.d.ts +6 -0
- package/Utility/EmitEventAtElement.js +15 -0
- package/Utility/EmitKeyEventAtElement.d.ts +7 -0
- package/Utility/EmitKeyEventAtElement.js +18 -0
- package/Utility/Expect.d.ts +7 -0
- package/Utility/Expect.js +29 -0
- package/Utility/module.d.ts +3 -0
- package/Utility/module.js +7 -0
- package/package.json +1 -1
- package/public_api.d.ts +2 -3
- package/public_api.js +2 -6
- package/Mock/TypeLiterals.d.ts +0 -9
- package/Utility/TestHelpers.d.ts +0 -30
- package/Utility/TestHelpers.js +0 -97
package/Mock/Any.d.ts
ADDED
package/Mock/Any.js
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Any = exports.ANY_VALUE = void 0;
|
|
4
|
+
exports.ANY_VALUE = 'ANYTHING';
|
|
5
|
+
/**
|
|
6
|
+
* Returns a value Mock understands to represent "ANYTHING" casted to "T"
|
|
7
|
+
* @returns
|
|
8
|
+
*/
|
|
9
|
+
function Any() {
|
|
10
|
+
return exports.ANY_VALUE;
|
|
11
|
+
}
|
|
12
|
+
exports.Any = Any;
|
|
13
|
+
//# sourceMappingURL=Any.js.map
|
package/Mock/Constants.d.ts
CHANGED
package/Mock/Constants.js
CHANGED
|
@@ -4,6 +4,7 @@ exports.Regex = void 0;
|
|
|
4
4
|
exports.Regex = {
|
|
5
5
|
Operation: /\s[\w\d_]*\.([\w\d$_]*)\(([\w\W\d$_]*)\)/,
|
|
6
6
|
Property: /\s[\w\d_]*\.([\w\d$_]*)/,
|
|
7
|
-
Params: /({.*?}|[^,]+)/g
|
|
7
|
+
Params: /({.*?}|[^,]+)/g,
|
|
8
|
+
AnyValue: /\((0, (.*?).Any)\)\(\)/g
|
|
8
9
|
};
|
|
9
10
|
//# sourceMappingURL=Constants.js.map
|
package/Mock/Mock.d.ts
CHANGED
|
@@ -2,13 +2,43 @@ import { Times } from './Times';
|
|
|
2
2
|
export declare class Mock<T> {
|
|
3
3
|
private memberSignatureMaps;
|
|
4
4
|
private object;
|
|
5
|
+
/**
|
|
6
|
+
* The mock object of the given type "T" to inject as a substitute to a concrete implementation
|
|
7
|
+
*/
|
|
5
8
|
get Object(): T;
|
|
9
|
+
/**
|
|
10
|
+
* Configure what the mock object will return when a given member is accessed
|
|
11
|
+
* @param member
|
|
12
|
+
* @param returns
|
|
13
|
+
*/
|
|
6
14
|
Setup(member: (func: T) => any, returns?: any): void;
|
|
15
|
+
/**
|
|
16
|
+
* Configure a setup that will only resolve on the first time the member is registered
|
|
17
|
+
* @param member
|
|
18
|
+
* @param returns
|
|
19
|
+
*/
|
|
20
|
+
SetupOnce(member: (func: T) => any, returns?: any): void;
|
|
21
|
+
/**
|
|
22
|
+
* Configure a set of setups that will only resolve on the first time the member is registered
|
|
23
|
+
* @param setups
|
|
24
|
+
*/
|
|
25
|
+
SetupSequence(setups: [(func: T) => any, any][]): void;
|
|
26
|
+
/**
|
|
27
|
+
* Return the number of times a given member was referenced
|
|
28
|
+
* @param member
|
|
29
|
+
* @returns
|
|
30
|
+
*/
|
|
7
31
|
TimesMemberCalled(member: (func: T) => any): number;
|
|
32
|
+
/**
|
|
33
|
+
* Make an assertion that a given member was referenced a given number of times
|
|
34
|
+
* @param member
|
|
35
|
+
* @param times
|
|
36
|
+
*/
|
|
8
37
|
Verify(member: (func: T) => any, times: Times | number): void;
|
|
9
|
-
private
|
|
10
|
-
private
|
|
38
|
+
private setup;
|
|
39
|
+
private getReturnValueForProperty;
|
|
40
|
+
private getReturnForFunction;
|
|
41
|
+
private getFunctionMapsFromSignature;
|
|
11
42
|
private updateMemberSignatureMaps;
|
|
12
43
|
private updateSignatureMapFunctions;
|
|
13
|
-
private getFunctionMapFromSignatureMap;
|
|
14
44
|
}
|
package/Mock/Mock.js
CHANGED
|
@@ -2,70 +2,127 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.Mock = void 0;
|
|
4
4
|
var SignatureService_1 = require("./SignatureService");
|
|
5
|
+
var Any_1 = require("./Any");
|
|
5
6
|
var Mock = /** @class */ (function () {
|
|
6
7
|
function Mock() {
|
|
7
8
|
this.memberSignatureMaps = new Array();
|
|
8
9
|
this.object = {};
|
|
9
10
|
}
|
|
10
11
|
Object.defineProperty(Mock.prototype, "Object", {
|
|
12
|
+
/**
|
|
13
|
+
* The mock object of the given type "T" to inject as a substitute to a concrete implementation
|
|
14
|
+
*/
|
|
11
15
|
get: function () {
|
|
12
16
|
return this.object;
|
|
13
17
|
},
|
|
14
18
|
enumerable: false,
|
|
15
19
|
configurable: true
|
|
16
20
|
});
|
|
21
|
+
/**
|
|
22
|
+
* Configure what the mock object will return when a given member is accessed
|
|
23
|
+
* @param member
|
|
24
|
+
* @param returns
|
|
25
|
+
*/
|
|
17
26
|
Mock.prototype.Setup = function (member, returns) {
|
|
27
|
+
if (returns === void 0) { returns = null; }
|
|
28
|
+
this.setup(member, returns);
|
|
29
|
+
};
|
|
30
|
+
/**
|
|
31
|
+
* Configure a setup that will only resolve on the first time the member is registered
|
|
32
|
+
* @param member
|
|
33
|
+
* @param returns
|
|
34
|
+
*/
|
|
35
|
+
Mock.prototype.SetupOnce = function (member, returns) {
|
|
36
|
+
if (returns === void 0) { returns = null; }
|
|
37
|
+
this.setup(member, returns, true);
|
|
38
|
+
};
|
|
39
|
+
/**
|
|
40
|
+
* Configure a set of setups that will only resolve on the first time the member is registered
|
|
41
|
+
* @param setups
|
|
42
|
+
*/
|
|
43
|
+
Mock.prototype.SetupSequence = function (setups) {
|
|
44
|
+
var _this = this;
|
|
45
|
+
setups.forEach(function (setup) {
|
|
46
|
+
_this.SetupOnce(setup[0], setup[1]);
|
|
47
|
+
});
|
|
48
|
+
};
|
|
49
|
+
/**
|
|
50
|
+
* Return the number of times a given member was referenced
|
|
51
|
+
* @param member
|
|
52
|
+
* @returns
|
|
53
|
+
*/
|
|
54
|
+
Mock.prototype.TimesMemberCalled = function (member) {
|
|
55
|
+
var _a;
|
|
56
|
+
var memberSignatureMap = SignatureService_1.SignatureService.GetMemberSignatureMap(member);
|
|
57
|
+
var functionMap = this.getFunctionMapsFromSignature(memberSignatureMap, (_a = memberSignatureMap.functionMaps[0]) === null || _a === void 0 ? void 0 : _a.state).functionMapForArgs;
|
|
58
|
+
return (functionMap === null || functionMap === void 0 ? void 0 : functionMap.timesCalled) || 0;
|
|
59
|
+
};
|
|
60
|
+
/**
|
|
61
|
+
* Make an assertion that a given member was referenced a given number of times
|
|
62
|
+
* @param member
|
|
63
|
+
* @param times
|
|
64
|
+
*/
|
|
65
|
+
Mock.prototype.Verify = function (member, times) {
|
|
66
|
+
var timesCalled = this.TimesMemberCalled(member);
|
|
67
|
+
var signature = SignatureService_1.SignatureService.GetMemberSignatureMap(member).signature;
|
|
68
|
+
var memberSignatureMap = this.memberSignatureMaps.find(function (m) { return m.signature === signature; });
|
|
69
|
+
if (timesCalled !== times) {
|
|
70
|
+
// eslint-disable-next-line no-console
|
|
71
|
+
console.log("Actual calls made for, \"" + signature + ":", memberSignatureMap === null || memberSignatureMap === void 0 ? void 0 : memberSignatureMap.functionMaps.map(function (m) { return m.originalSignature + " x " + m.timesCalled; }));
|
|
72
|
+
}
|
|
73
|
+
expect(timesCalled).toEqual(times, timesCalled !== times ? '' : undefined);
|
|
74
|
+
};
|
|
75
|
+
Mock.prototype.setup = function (member, returns, singleUse) {
|
|
18
76
|
var _this = this;
|
|
19
77
|
if (returns === void 0) { returns = null; }
|
|
20
|
-
|
|
78
|
+
if (singleUse === void 0) { singleUse = false; }
|
|
79
|
+
var memberSignatureMap = SignatureService_1.SignatureService.GetMemberSignatureMap(member, returns, singleUse);
|
|
21
80
|
this.updateMemberSignatureMaps(memberSignatureMap);
|
|
22
81
|
var memberName = SignatureService_1.SignatureService.GetMemberNameFromSignature(memberSignatureMap.signature);
|
|
23
82
|
if (SignatureService_1.SignatureService.MemberSignatureIsProperty(memberSignatureMap.signature)) {
|
|
24
|
-
this.object[memberName] = this.
|
|
83
|
+
this.object[memberName] = this.getReturnValueForProperty(memberSignatureMap);
|
|
25
84
|
}
|
|
26
85
|
else {
|
|
27
86
|
this.object[memberName] = (function () {
|
|
87
|
+
var _a;
|
|
28
88
|
var args = [];
|
|
29
89
|
for (var _i = 0; _i < arguments.length; _i++) {
|
|
30
90
|
args[_i] = arguments[_i];
|
|
31
91
|
}
|
|
32
|
-
|
|
33
|
-
return returnFunction ? returnFunction() :
|
|
34
|
-
(function () { return console.error('Unable to resolve setup function'); })();
|
|
92
|
+
return (_a = _this.getReturnForFunction(memberSignatureMap, args)) === null || _a === void 0 ? void 0 : _a();
|
|
35
93
|
});
|
|
36
94
|
}
|
|
37
95
|
};
|
|
38
|
-
Mock.prototype.
|
|
39
|
-
var
|
|
40
|
-
|
|
41
|
-
var timesCalled = functionMap ? functionMap.timesCalled : 0;
|
|
42
|
-
return timesCalled;
|
|
43
|
-
};
|
|
44
|
-
Mock.prototype.Verify = function (member, times) {
|
|
45
|
-
var timesCalled = this.TimesMemberCalled(member);
|
|
46
|
-
expect(timesCalled).toEqual(times);
|
|
96
|
+
Mock.prototype.getReturnValueForProperty = function (memberSignatureMap) {
|
|
97
|
+
var _a, _b;
|
|
98
|
+
return ((_b = (_a = this.memberSignatureMaps.find(function (s) { return s.signature === memberSignatureMap.signature; })) === null || _a === void 0 ? void 0 : _a.functionMaps[0]) === null || _b === void 0 ? void 0 : _b.returns) || null;
|
|
47
99
|
};
|
|
48
|
-
Mock.prototype.
|
|
49
|
-
var
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
100
|
+
Mock.prototype.getReturnForFunction = function (memberSignatureMap, args) {
|
|
101
|
+
var _a = this.getFunctionMapsFromSignature(memberSignatureMap, args), functionMapForArgs = _a.functionMapForArgs, signatureFunctionMaps = _a.signatureFunctionMaps;
|
|
102
|
+
return functionMapForArgs ? (function () {
|
|
103
|
+
functionMapForArgs.timesCalled++;
|
|
104
|
+
if (signatureFunctionMaps && functionMapForArgs.singleUse) {
|
|
105
|
+
var indexToDelete = signatureFunctionMaps.indexOf(functionMapForArgs);
|
|
106
|
+
signatureFunctionMaps.splice(indexToDelete, 1);
|
|
107
|
+
}
|
|
108
|
+
return functionMapForArgs.returns;
|
|
109
|
+
}) : undefined;
|
|
55
110
|
};
|
|
56
|
-
Mock.prototype.
|
|
57
|
-
var returnFunction;
|
|
111
|
+
Mock.prototype.getFunctionMapsFromSignature = function (memberSignatureMap, args) {
|
|
58
112
|
var existingMemberSignatureMap = this.memberSignatureMaps.find(function (s) { return s.signature === memberSignatureMap.signature; });
|
|
59
|
-
var
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
113
|
+
var signatureFunctionMaps = existingMemberSignatureMap === null || existingMemberSignatureMap === void 0 ? void 0 : existingMemberSignatureMap.functionMaps;
|
|
114
|
+
var functionMapForArgs = signatureFunctionMaps === null || signatureFunctionMaps === void 0 ? void 0 : signatureFunctionMaps.find(function (m) { return JSON.stringify(m.state) === JSON.stringify(args); });
|
|
115
|
+
var functionMapsUsingAny = signatureFunctionMaps === null || signatureFunctionMaps === void 0 ? void 0 : signatureFunctionMaps.filter(function (m) { return m.state.includes(Any_1.ANY_VALUE); });
|
|
116
|
+
if (!functionMapForArgs && (functionMapsUsingAny === null || functionMapsUsingAny === void 0 ? void 0 : functionMapsUsingAny.length)) {
|
|
117
|
+
functionMapsUsingAny.forEach(function (element) {
|
|
118
|
+
if (!functionMapForArgs) {
|
|
119
|
+
var anyTransposedState_1 = new Array();
|
|
120
|
+
args.forEach(function (a, i) { return anyTransposedState_1[i] = element.state[i] === Any_1.ANY_VALUE ? Any_1.ANY_VALUE : a; });
|
|
121
|
+
functionMapForArgs = signatureFunctionMaps === null || signatureFunctionMaps === void 0 ? void 0 : signatureFunctionMaps.find(function (m) { return JSON.stringify(m.state) === JSON.stringify(anyTransposedState_1); });
|
|
122
|
+
}
|
|
66
123
|
});
|
|
67
124
|
}
|
|
68
|
-
return
|
|
125
|
+
return { functionMapForArgs: functionMapForArgs, signatureFunctionMaps: signatureFunctionMaps };
|
|
69
126
|
};
|
|
70
127
|
Mock.prototype.updateMemberSignatureMaps = function (memberSignatureMap) {
|
|
71
128
|
var existingSignatureMap = this.memberSignatureMaps.find(function (m) { return m.signature === memberSignatureMap.signature; });
|
|
@@ -79,7 +136,7 @@ var Mock = /** @class */ (function () {
|
|
|
79
136
|
};
|
|
80
137
|
Mock.prototype.updateSignatureMapFunctions = function (existingSignatureMap, newFunctionMap) {
|
|
81
138
|
var existingFunctionMap = existingSignatureMap.functionMaps.find(function (fm) { return JSON.stringify(fm.state) === JSON.stringify(newFunctionMap.state); });
|
|
82
|
-
if (existingFunctionMap) {
|
|
139
|
+
if (existingFunctionMap && !newFunctionMap.singleUse) {
|
|
83
140
|
var functionMaps = existingSignatureMap.functionMaps;
|
|
84
141
|
functionMaps.splice(functionMaps.indexOf(existingFunctionMap), 1, newFunctionMap);
|
|
85
142
|
}
|
|
@@ -87,21 +144,6 @@ var Mock = /** @class */ (function () {
|
|
|
87
144
|
existingSignatureMap.functionMaps.push(newFunctionMap);
|
|
88
145
|
}
|
|
89
146
|
};
|
|
90
|
-
Mock.prototype.getFunctionMapFromSignatureMap = function (memberSignature) {
|
|
91
|
-
var functionMap;
|
|
92
|
-
var existingMember = this.memberSignatureMaps.find(function (m) { return m.signature === memberSignature.signature; });
|
|
93
|
-
var functionMapToFind = memberSignature.functionMaps[0];
|
|
94
|
-
if (existingMember && functionMapToFind) {
|
|
95
|
-
var functionMapCount = existingMember.functionMaps ? existingMember.functionMaps.length : 0;
|
|
96
|
-
if (functionMapCount === 1) {
|
|
97
|
-
functionMap = existingMember.functionMaps[0];
|
|
98
|
-
}
|
|
99
|
-
else {
|
|
100
|
-
functionMap = existingMember.functionMaps.find(function (m) { return JSON.stringify(m.state) === JSON.stringify(functionMapToFind.state); });
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
return functionMap;
|
|
104
|
-
};
|
|
105
147
|
return Mock;
|
|
106
148
|
}());
|
|
107
149
|
exports.Mock = Mock;
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import { SignatureMap } from './
|
|
1
|
+
import { SignatureMap } from './SignatureMap';
|
|
2
2
|
export declare class SignatureService {
|
|
3
|
-
static GetMemberSignatureMap(value: (obj: any) => any, returns?: any): SignatureMap;
|
|
3
|
+
static GetMemberSignatureMap(value: (obj: any) => any, returns?: any, singleUse?: boolean): SignatureMap;
|
|
4
4
|
static MemberSignatureIsProperty(memberSignatureString: string): boolean;
|
|
5
5
|
static GetMemberNameFromSignature(memberSignatureString: string): string;
|
|
6
|
+
private static getOriginalSignature;
|
|
6
7
|
private static getPropertyMemberSignature;
|
|
7
8
|
private static getOperationMemberSignature;
|
|
8
9
|
private static getStateForMemberSignature;
|
package/Mock/SignatureService.js
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.SignatureService = void 0;
|
|
4
|
+
var Any_1 = require("./Any");
|
|
4
5
|
var Constants_1 = require("./Constants");
|
|
5
6
|
var SignatureService = /** @class */ (function () {
|
|
6
7
|
function SignatureService() {
|
|
7
8
|
}
|
|
8
|
-
SignatureService.GetMemberSignatureMap = function (value, returns) {
|
|
9
|
+
SignatureService.GetMemberSignatureMap = function (value, returns, singleUse) {
|
|
10
|
+
if (singleUse === void 0) { singleUse = false; }
|
|
9
11
|
var memberSignature = '';
|
|
10
12
|
memberSignature =
|
|
11
13
|
this.getOperationMemberSignature(value, memberSignature) ||
|
|
@@ -14,7 +16,13 @@ var SignatureService = /** @class */ (function () {
|
|
|
14
16
|
SignatureService.getStateForMemberSignature(memberSignature, value);
|
|
15
17
|
return {
|
|
16
18
|
signature: memberSignature,
|
|
17
|
-
functionMaps: [{
|
|
19
|
+
functionMaps: [{
|
|
20
|
+
state: state,
|
|
21
|
+
returns: returns,
|
|
22
|
+
timesCalled: 0,
|
|
23
|
+
singleUse: singleUse,
|
|
24
|
+
originalSignature: this.getOriginalSignature(value)
|
|
25
|
+
}]
|
|
18
26
|
};
|
|
19
27
|
};
|
|
20
28
|
SignatureService.MemberSignatureIsProperty = function (memberSignatureString) {
|
|
@@ -24,6 +32,13 @@ var SignatureService = /** @class */ (function () {
|
|
|
24
32
|
return SignatureService.MemberSignatureIsProperty(memberSignatureString) ?
|
|
25
33
|
memberSignatureString : memberSignatureString.split('(')[0];
|
|
26
34
|
};
|
|
35
|
+
SignatureService.getOriginalSignature = function (value) {
|
|
36
|
+
var originalSignature = value.toString();
|
|
37
|
+
originalSignature = (originalSignature === null || originalSignature === void 0 ? void 0 : originalSignature.indexOf('i.')) ? originalSignature.split('i.')[1] : originalSignature;
|
|
38
|
+
originalSignature = (originalSignature === null || originalSignature === void 0 ? void 0 : originalSignature.indexOf(';')) ? originalSignature.split(';')[0] : originalSignature;
|
|
39
|
+
return originalSignature === null || originalSignature === void 0 ? void 0 : originalSignature.trim();
|
|
40
|
+
};
|
|
41
|
+
;
|
|
27
42
|
SignatureService.getPropertyMemberSignature = function (value, memberSignature) {
|
|
28
43
|
var propertyMemberMatches = SignatureService.getMatchesForRegex(value, Constants_1.Regex.Property);
|
|
29
44
|
if (propertyMemberMatches && propertyMemberMatches[1]) {
|
|
@@ -64,7 +79,9 @@ var SignatureService = /** @class */ (function () {
|
|
|
64
79
|
return matches;
|
|
65
80
|
};
|
|
66
81
|
SignatureService.getParamString = function (operationNameMatches) {
|
|
67
|
-
var paramStrings = operationNameMatches[2]
|
|
82
|
+
var paramStrings = operationNameMatches[2]
|
|
83
|
+
.replace(Constants_1.Regex.AnyValue, Any_1.ANY_VALUE)
|
|
84
|
+
.match(Constants_1.Regex.Params);
|
|
68
85
|
var params = '';
|
|
69
86
|
for (var index = 0; index < (paramStrings ? paramStrings.length : 0); index++) {
|
|
70
87
|
params += ((index > 0 ? ', ' : '') + "p" + index);
|
package/Mock/module.d.ts
ADDED
package/Mock/module.js
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Any = void 0;
|
|
4
|
+
var tslib_1 = require("tslib");
|
|
5
|
+
(0, tslib_1.__exportStar)(require("./Mock"), exports);
|
|
6
|
+
(0, tslib_1.__exportStar)(require("./Times"), exports);
|
|
7
|
+
var Any_1 = require("./Any");
|
|
8
|
+
Object.defineProperty(exports, "Any", { enumerable: true, get: function () { return Any_1.Any; } });
|
|
9
|
+
//# sourceMappingURL=module.js.map
|
package/README.md
CHANGED
|
@@ -4,64 +4,18 @@ Generic mocking library for TypeScript
|
|
|
4
4
|
[](https://github.com/bayes343/tsmockit/actions/workflows/ci.yml)
|
|
5
5
|

|
|
6
6
|
|
|
7
|
-
|
|
8
7
|
Helpful links:
|
|
9
8
|
- [GitHub](https://github.com/bayes343/tsmockit)
|
|
10
9
|
- [npm](https://www.npmjs.com/package/tsmockit)
|
|
10
|
+
- [Docs](https://bayes343.github.io/tsmockit/modules.html)
|
|
11
11
|
|
|
12
|
-
This library exposes a generic class, `Mock<T>`, which allows you to mock dependencies and verify usage in an intuitive and type safe manner.
|
|
12
|
+
This library exposes a generic class, `Mock<T>`, which allows you to mock dependencies and verify usage in an intuitive and type safe manner. Its API is based on the C# "moq" library.
|
|
13
13
|
|
|
14
|
-
|
|
14
|
+
The below example demonstrates some of the features of this library. Please explore the "docs" linked above for further details.
|
|
15
15
|
|
|
16
16
|
```typescript
|
|
17
|
-
|
|
18
|
-
```
|
|
19
|
-
```typescript
|
|
20
|
-
Setup(
|
|
21
|
-
member: (func: T) => any,
|
|
22
|
-
returns: any = null
|
|
23
|
-
): void
|
|
24
|
-
```
|
|
25
|
-
```typescript
|
|
26
|
-
TimesMemberCalled(
|
|
27
|
-
member: (func: T) => any
|
|
28
|
-
): number
|
|
29
|
-
```
|
|
30
|
-
```typescript
|
|
31
|
-
Verify(
|
|
32
|
-
member: (func: T) => any,
|
|
33
|
-
times: Times | number
|
|
34
|
-
): void
|
|
35
|
-
```
|
|
17
|
+
// https://github.com/bayes343/tsmockit/blob/master/src/Mock/tests/Mock-Car-Example.spec.ts
|
|
36
18
|
|
|
37
|
-
```typescript
|
|
38
|
-
class TestHelpers
|
|
39
|
-
```
|
|
40
|
-
```typescript
|
|
41
|
-
static EmitEventAtElement(element: HTMLElement, eventType: string): void
|
|
42
|
-
```
|
|
43
|
-
```typescript
|
|
44
|
-
static EmitKeyEventAtElement(
|
|
45
|
-
element: HTMLInputElement,
|
|
46
|
-
key: string,
|
|
47
|
-
keyEvent: 'keydown' | 'keypress' | 'keyup' | 'input'
|
|
48
|
-
): void
|
|
49
|
-
```
|
|
50
|
-
```typescript
|
|
51
|
-
static async Expect<T>(
|
|
52
|
-
selector: () => T,
|
|
53
|
-
assertion: (m: jasmine.Matchers<T>) => void,
|
|
54
|
-
interval = 0,
|
|
55
|
-
getTimeFunc = () => Date.now()
|
|
56
|
-
): Promise<void>
|
|
57
|
-
```
|
|
58
|
-
|
|
59
|
-
## Usage
|
|
60
|
-
|
|
61
|
-
### Scenario
|
|
62
|
-
Consider this dependency injection scenario.
|
|
63
|
-
|
|
64
|
-
```ts
|
|
65
19
|
interface IEngine {
|
|
66
20
|
Start(): void;
|
|
67
21
|
Stop(): void;
|
|
@@ -113,37 +67,62 @@ class Car {
|
|
|
113
67
|
return this.stereo.SetStation(frequency);
|
|
114
68
|
}
|
|
115
69
|
}
|
|
116
|
-
```
|
|
117
|
-
|
|
118
|
-
The `Car` class above uses dependency injection for its engine, odometer, and stereo dependencies.
|
|
119
|
-
|
|
120
|
-
It's a best practice to use dependency injection over 'newing' up concretions inside a class instance. This allows true unit testing as well as widely opening the door to future extendability.
|
|
121
|
-
|
|
122
|
-
### Example
|
|
123
|
-
Here's how you would use `tsmockit` to mock the above dependencies and test the `Car` class.
|
|
124
|
-
|
|
125
|
-
```ts
|
|
126
|
-
// Instantiate mocks
|
|
127
|
-
const mockIEngine = new Mock<IEngine>();
|
|
128
|
-
const mockIOdometer = new Mock<IOdometer>();
|
|
129
|
-
const mockIStereo = new Mock<IStereo>();
|
|
130
|
-
|
|
131
|
-
// Instantiate car, passing mock 'Objects' as its dependencies
|
|
132
|
-
const car = new Car(mockIEngine.Object, mockIOdometer.Object, mockIStereo.Object);
|
|
133
|
-
|
|
134
|
-
// 'Setup' the mock odometer to return 100
|
|
135
|
-
mockIOdometer.Setup(o => o.GetMileage(), 100);
|
|
136
|
-
|
|
137
|
-
// Assert that the Car's mileage property returns 100 and that our mock GetMileage method is called exactly once
|
|
138
|
-
expect(car.Mileage).toEqual(100);
|
|
139
|
-
mockIOdometer.Verify(o => o.GetMileage(), 1);
|
|
140
70
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
71
|
+
describe('Car', () => {
|
|
72
|
+
let car: Car;
|
|
73
|
+
const mockIEngine = new Mock<IEngine>();
|
|
74
|
+
const mockIOdometer = new Mock<IOdometer>();
|
|
75
|
+
const mockIStereo = new Mock<IStereo>();
|
|
76
|
+
|
|
77
|
+
beforeEach(() => {
|
|
78
|
+
car = new Car(mockIEngine.Object, mockIOdometer.Object, mockIStereo.Object);
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
it('should call Engine.Start when StartEngine is called', () => {
|
|
82
|
+
mockIEngine.Setup(e => e.Start());
|
|
83
|
+
car.StartEngine();
|
|
84
|
+
mockIEngine.Verify(e => e.Start(), Times.Once);
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
it('should call Engine.Stop when StopEngine is called', () => {
|
|
88
|
+
mockIEngine.Setup(e => e.Stop());
|
|
89
|
+
car.StopEngine();
|
|
90
|
+
mockIEngine.Verify(e => e.Stop(), Times.Once);
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
it('should return the result of Odometer.GetMileage on referencing the Mileage property', () => {
|
|
94
|
+
mockIOdometer.Setup(o => o.GetMileage(), 100);
|
|
95
|
+
|
|
96
|
+
const mileage = car.Mileage;
|
|
97
|
+
|
|
98
|
+
expect(mileage).toEqual(100);
|
|
99
|
+
mockIOdometer.Verify(o => o.GetMileage(), Times.Once);
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
it('should call Stereo.SetStation on calling ChangeRadioStation returning the string from Stereo', () => {
|
|
103
|
+
mockIStereo.Setup(s => s.SetStation(Any<number>()), 'Station set'); // default fallback setup when a more specific setup isn't available
|
|
104
|
+
mockIStereo.Setup(s => s.SetStation(3), 'Station 3');
|
|
105
|
+
|
|
106
|
+
expect(car.ChangeRadioStation(3)).toEqual('Station 3');
|
|
107
|
+
expect(car.ChangeRadioStation(0)).toEqual('Station set');
|
|
108
|
+
expect(car.ChangeRadioStation(2)).toEqual('Station set');
|
|
109
|
+
|
|
110
|
+
mockIStereo.Verify(s => s.SetStation(3), Times.Once);
|
|
111
|
+
mockIStereo.Verify(s => s.SetStation(Any<number>()), 2);
|
|
112
|
+
});
|
|
113
|
+
});
|
|
144
114
|
```
|
|
145
115
|
|
|
146
|
-
##
|
|
147
|
-
|
|
148
|
-
-
|
|
149
|
-
-
|
|
116
|
+
## Version 2 notes
|
|
117
|
+
|
|
118
|
+
- Any\<T\>
|
|
119
|
+
- A helper function which allows clients to create "Setups" on methods to be used disregarding all, or some, of the exact values the method is called with.
|
|
120
|
+
```typescript
|
|
121
|
+
mockIStereo.Setup(s => s.SetStation(Any<number>()), 'Station set');
|
|
122
|
+
```
|
|
123
|
+
- SetupOnce
|
|
124
|
+
- Creates a setup the same as the regular `Setup` method except for once the setup is used, it will de-register itself.
|
|
125
|
+
- One use could be an inferred guarantee that a given method on a dependency is not being called more than you expect. Using `SetupOnce` for a setup that should only be used once, will have the inherent effect of leading to a runtime error at test time on the second execution.
|
|
126
|
+
- SetupSequence
|
|
127
|
+
- Creates several, "one time" setups for a given method.
|
|
128
|
+
- You'll occasionally want the first execution of a given method on a dependency to return "x" but then "y" on the following execution. Use `SetupSequence` to achieve this.
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.EmitEventAtElement = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Emit an event at a given element
|
|
6
|
+
* @param element
|
|
7
|
+
* @param eventType
|
|
8
|
+
*/
|
|
9
|
+
function EmitEventAtElement(element, eventType) {
|
|
10
|
+
var event = document.createEvent('Event');
|
|
11
|
+
event.initEvent(eventType);
|
|
12
|
+
element.dispatchEvent(event);
|
|
13
|
+
}
|
|
14
|
+
exports.EmitEventAtElement = EmitEventAtElement;
|
|
15
|
+
//# sourceMappingURL=EmitEventAtElement.js.map
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.EmitKeyEventAtElement = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Emit a key event at a given element
|
|
6
|
+
* @param element
|
|
7
|
+
* @param key
|
|
8
|
+
* @param keyEvent
|
|
9
|
+
*/
|
|
10
|
+
function EmitKeyEventAtElement(element, key, keyEvent) {
|
|
11
|
+
var event = document.createEvent('Event');
|
|
12
|
+
event['keyCode'] = key;
|
|
13
|
+
event['key'] = key;
|
|
14
|
+
event.initEvent(keyEvent);
|
|
15
|
+
element.dispatchEvent(event);
|
|
16
|
+
}
|
|
17
|
+
exports.EmitKeyEventAtElement = EmitKeyEventAtElement;
|
|
18
|
+
//# sourceMappingURL=EmitKeyEventAtElement.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/// <reference types="jasmine" />
|
|
2
|
+
/**
|
|
3
|
+
* Performs an asynchronous assertion, allowing up to one second to pass before failing
|
|
4
|
+
* @param selector A function that returns the subject of the assertion once it is expected to pass.
|
|
5
|
+
* @param assertion A function that is given the result of the selector function for normal jasmine assertions.
|
|
6
|
+
*/
|
|
7
|
+
export declare function Expect<T>(selector: () => T, assertion: (m: jasmine.Matchers<T>) => void, interval?: number, getTimeFunc?: () => number): Promise<void>;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Expect = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Performs an asynchronous assertion, allowing up to one second to pass before failing
|
|
6
|
+
* @param selector A function that returns the subject of the assertion once it is expected to pass.
|
|
7
|
+
* @param assertion A function that is given the result of the selector function for normal jasmine assertions.
|
|
8
|
+
*/
|
|
9
|
+
function Expect(selector, assertion, interval, getTimeFunc) {
|
|
10
|
+
if (interval === void 0) { interval = 0; }
|
|
11
|
+
if (getTimeFunc === void 0) { getTimeFunc = function () { return Date.now(); }; }
|
|
12
|
+
return new Promise(function (resolve) {
|
|
13
|
+
var startTime = getTimeFunc();
|
|
14
|
+
var timedOut = function () { return getTimeFunc() - startTime > 1000; };
|
|
15
|
+
var execute = function () {
|
|
16
|
+
var selection = selector();
|
|
17
|
+
if (selection || timedOut()) {
|
|
18
|
+
assertion(expect(selection));
|
|
19
|
+
resolve();
|
|
20
|
+
}
|
|
21
|
+
else {
|
|
22
|
+
setTimeout(function () { return execute(); }, interval);
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
execute();
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
exports.Expect = Expect;
|
|
29
|
+
//# sourceMappingURL=Expect.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
var tslib_1 = require("tslib");
|
|
4
|
+
(0, tslib_1.__exportStar)(require("./EmitEventAtElement"), exports);
|
|
5
|
+
(0, tslib_1.__exportStar)(require("./EmitKeyEventAtElement"), exports);
|
|
6
|
+
(0, tslib_1.__exportStar)(require("./Expect"), exports);
|
|
7
|
+
//# sourceMappingURL=module.js.map
|
package/package.json
CHANGED
package/public_api.d.ts
CHANGED
|
@@ -1,3 +1,2 @@
|
|
|
1
|
-
export * from './Mock/
|
|
2
|
-
export * from './
|
|
3
|
-
export * from './Utility/TestHelpers';
|
|
1
|
+
export * from './Mock/module';
|
|
2
|
+
export * from './Utility/module';
|
package/public_api.js
CHANGED
|
@@ -1,10 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
/*
|
|
3
|
-
* Public API Surface of tsmockit
|
|
4
|
-
*/
|
|
5
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
3
|
var tslib_1 = require("tslib");
|
|
7
|
-
(0, tslib_1.__exportStar)(require("./Mock/
|
|
8
|
-
(0, tslib_1.__exportStar)(require("./
|
|
9
|
-
(0, tslib_1.__exportStar)(require("./Utility/TestHelpers"), exports);
|
|
4
|
+
(0, tslib_1.__exportStar)(require("./Mock/module"), exports);
|
|
5
|
+
(0, tslib_1.__exportStar)(require("./Utility/module"), exports);
|
|
10
6
|
//# sourceMappingURL=public_api.js.map
|
package/Mock/TypeLiterals.d.ts
DELETED
package/Utility/TestHelpers.d.ts
DELETED
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
/// <reference types="jasmine" />
|
|
2
|
-
export declare class TestHelpers {
|
|
3
|
-
/**
|
|
4
|
-
* Emit an event at a given element
|
|
5
|
-
* @param element
|
|
6
|
-
* @param eventType
|
|
7
|
-
*/
|
|
8
|
-
static EmitEventAtElement(element: HTMLElement, eventType: string): void;
|
|
9
|
-
/**
|
|
10
|
-
* Emit a key event at a given element
|
|
11
|
-
* @param element
|
|
12
|
-
* @param key
|
|
13
|
-
* @param keyEvent
|
|
14
|
-
*/
|
|
15
|
-
static EmitKeyEventAtElement(element: HTMLInputElement, key: string, keyEvent: 'keydown' | 'keypress' | 'keyup' | 'input'): void;
|
|
16
|
-
/**
|
|
17
|
-
* Wait up to 1 second for a given condition to be true
|
|
18
|
-
* @deprecated Will be removed in version 2 - Use "Expect" instead
|
|
19
|
-
* @param condition
|
|
20
|
-
* @param interval
|
|
21
|
-
* @returns true if condition is met before 1 second limit, false otherwise
|
|
22
|
-
*/
|
|
23
|
-
static TimeLapsedCondition(condition: () => boolean, interval?: number): Promise<boolean>;
|
|
24
|
-
/**
|
|
25
|
-
* Performs an asynchronous assertion, allowing up to one second to pass before failing
|
|
26
|
-
* @param selector A function that returns the subject of the assertion once it is expected to pass.
|
|
27
|
-
* @param assertion A function that is given the result of the selector function for normal jasmine assertions.
|
|
28
|
-
*/
|
|
29
|
-
static Expect<T>(selector: () => T, assertion: (m: jasmine.Matchers<T>) => void, interval?: number, getTimeFunc?: () => number): Promise<void>;
|
|
30
|
-
}
|
package/Utility/TestHelpers.js
DELETED
|
@@ -1,97 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.TestHelpers = void 0;
|
|
4
|
-
var tslib_1 = require("tslib");
|
|
5
|
-
var TestHelpers = /** @class */ (function () {
|
|
6
|
-
function TestHelpers() {
|
|
7
|
-
}
|
|
8
|
-
/**
|
|
9
|
-
* Emit an event at a given element
|
|
10
|
-
* @param element
|
|
11
|
-
* @param eventType
|
|
12
|
-
*/
|
|
13
|
-
TestHelpers.EmitEventAtElement = function (element, eventType) {
|
|
14
|
-
var event = document.createEvent('Event');
|
|
15
|
-
event.initEvent(eventType);
|
|
16
|
-
element.dispatchEvent(event);
|
|
17
|
-
};
|
|
18
|
-
/**
|
|
19
|
-
* Emit a key event at a given element
|
|
20
|
-
* @param element
|
|
21
|
-
* @param key
|
|
22
|
-
* @param keyEvent
|
|
23
|
-
*/
|
|
24
|
-
TestHelpers.EmitKeyEventAtElement = function (element, key, keyEvent) {
|
|
25
|
-
var event = document.createEvent('Event');
|
|
26
|
-
event['keyCode'] = key;
|
|
27
|
-
event['key'] = key;
|
|
28
|
-
event.initEvent(keyEvent);
|
|
29
|
-
element.dispatchEvent(event);
|
|
30
|
-
};
|
|
31
|
-
/**
|
|
32
|
-
* Wait up to 1 second for a given condition to be true
|
|
33
|
-
* @deprecated Will be removed in version 2 - Use "Expect" instead
|
|
34
|
-
* @param condition
|
|
35
|
-
* @param interval
|
|
36
|
-
* @returns true if condition is met before 1 second limit, false otherwise
|
|
37
|
-
*/
|
|
38
|
-
TestHelpers.TimeLapsedCondition = function (condition, interval) {
|
|
39
|
-
if (interval === void 0) { interval = 10; }
|
|
40
|
-
return (0, tslib_1.__awaiter)(this, void 0, void 0, function () {
|
|
41
|
-
var _this = this;
|
|
42
|
-
return (0, tslib_1.__generator)(this, function (_a) {
|
|
43
|
-
return [2 /*return*/, new Promise(function (resolve) {
|
|
44
|
-
var assertionPassed = false;
|
|
45
|
-
var elapsedTime = 0;
|
|
46
|
-
var enabled = (function () {
|
|
47
|
-
return !assertionPassed && elapsedTime < 1000;
|
|
48
|
-
});
|
|
49
|
-
var executer = setInterval(function () { return (0, tslib_1.__awaiter)(_this, void 0, void 0, function () {
|
|
50
|
-
return (0, tslib_1.__generator)(this, function (_a) {
|
|
51
|
-
elapsedTime += interval;
|
|
52
|
-
if (enabled()) {
|
|
53
|
-
assertionPassed = condition();
|
|
54
|
-
}
|
|
55
|
-
else {
|
|
56
|
-
clearInterval(executer);
|
|
57
|
-
resolve(assertionPassed);
|
|
58
|
-
}
|
|
59
|
-
return [2 /*return*/];
|
|
60
|
-
});
|
|
61
|
-
}); }, interval);
|
|
62
|
-
})];
|
|
63
|
-
});
|
|
64
|
-
});
|
|
65
|
-
};
|
|
66
|
-
/**
|
|
67
|
-
* Performs an asynchronous assertion, allowing up to one second to pass before failing
|
|
68
|
-
* @param selector A function that returns the subject of the assertion once it is expected to pass.
|
|
69
|
-
* @param assertion A function that is given the result of the selector function for normal jasmine assertions.
|
|
70
|
-
*/
|
|
71
|
-
TestHelpers.Expect = function (selector, assertion, interval, getTimeFunc) {
|
|
72
|
-
if (interval === void 0) { interval = 0; }
|
|
73
|
-
if (getTimeFunc === void 0) { getTimeFunc = function () { return Date.now(); }; }
|
|
74
|
-
return (0, tslib_1.__awaiter)(this, void 0, void 0, function () {
|
|
75
|
-
return (0, tslib_1.__generator)(this, function (_a) {
|
|
76
|
-
return [2 /*return*/, new Promise(function (resolve) {
|
|
77
|
-
var startTime = getTimeFunc();
|
|
78
|
-
var timedOut = function () { return getTimeFunc() - startTime > 1000; };
|
|
79
|
-
var execute = function () {
|
|
80
|
-
var selection = selector();
|
|
81
|
-
if (selection || timedOut()) {
|
|
82
|
-
assertion(expect(selection));
|
|
83
|
-
resolve();
|
|
84
|
-
}
|
|
85
|
-
else {
|
|
86
|
-
setTimeout(function () { return execute(); }, interval);
|
|
87
|
-
}
|
|
88
|
-
};
|
|
89
|
-
execute();
|
|
90
|
-
})];
|
|
91
|
-
});
|
|
92
|
-
});
|
|
93
|
-
};
|
|
94
|
-
return TestHelpers;
|
|
95
|
-
}());
|
|
96
|
-
exports.TestHelpers = TestHelpers;
|
|
97
|
-
//# sourceMappingURL=TestHelpers.js.map
|