• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/*
2 * Copyright (c) 2022 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 *     http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16import ExtendInterface from "./ExtendInterface";
17import VerificationMode from "./VerificationMode";
18import ArgumentMatchers from "./ArgumentMatchers";
19
20class MockKit {
21
22    constructor() {
23        this.mFunctions = [];
24        this.stubs = new Map();
25        this.recordCalls = new Map();
26        this.currentSetKey = null;
27        this.mockObj = null;
28        this.recordMockedMethod = new Map();
29    }
30
31    init() {
32        this.reset();
33    }
34
35    reset() {
36        this.mFunctions = [];
37        this.stubs = {};
38        this.recordCalls = {};
39        this.currentSetKey = null;
40        this.mockObj = null;
41        this.recordMockedMethod = new Map();
42    }
43
44    clearAll() {
45        this.reset();
46        var props = Object.keys(this);
47        for (var i = 0; i < props.length; i++) {
48            delete this[props[i]];
49        }
50
51        var props = Object.getOwnPropertyNames(this);
52        for (var i = 0; i < props.length; i++) {
53            delete this[props[i]];
54        }
55        for (var key in this) {
56            delete this[key];
57        }
58    }
59
60    clear(obj) {
61        if (!obj) throw Error("Please enter an object to be cleaned");
62        if (typeof (obj) != 'object') throw new Error('Not a object');
63        this.recordMockedMethod.forEach(function (value, key, map) {
64            if (key) {
65                obj[key] = value;
66            }
67        });
68    }
69
70    ignoreMock(obj, method) {
71        if (typeof (obj) != 'object') throw new Error('Not a object');
72        if (typeof (method) != 'function') throw new Error('Not a function');
73        let og = this.recordMockedMethod.get(method.propName);
74        if (og) {
75            obj[method.propName] = og;
76            this.recordMockedMethod.set(method.propName, undefined);
77        }
78    }
79
80    extend(dest, source) {
81        dest["stub"] = source["stub"];
82        dest["afterReturn"] = source["afterReturn"];
83        dest["afterReturnNothing"] = source["afterReturnNothing"];
84        dest["afterAction"] = source["afterAction"];
85        dest["afterThrow"] = source["afterThrow"];
86        dest["stubMockedCall"] = source["stubMockedCall"];
87        dest["clear"] = source["clear"];
88        return dest;
89    }
90
91    stubApply(f, params, returnInfo) {
92        let values = this.stubs.get(f);
93        if (!values) {
94            values = new Map();
95        }
96        let key = params[0];
97        if (typeof key == "undefined") {
98            key = "anonymous-mock-" + f.propName;
99        }
100        let matcher = new ArgumentMatchers();
101        if (matcher.matcheStubKey(key)) {
102            key = matcher.matcheStubKey(key);
103            if (key) {
104                this.currentSetKey = key;
105            }
106        }
107        values.set(key, returnInfo);
108        this.stubs.set(f, values);
109    }
110
111    getReturnInfo(f, params) {
112        let values = this.stubs.get(f);
113        if (!values) {
114            return undefined;
115        }
116        let retrunKet = params[0];
117        if (typeof retrunKet == "undefined") {
118            retrunKet = "anonymous-mock-" + f.propName;
119        }
120        let stubSetKey = this.currentSetKey;
121
122        if (this.currentSetKey && (typeof (retrunKet) != "undefined")) {
123            retrunKet = stubSetKey;
124        }
125        let matcher = new ArgumentMatchers();
126        if (matcher.matcheReturnKey(params[0], undefined, stubSetKey) && matcher.matcheReturnKey(params[0], undefined, stubSetKey) != stubSetKey) {
127            retrunKet = params[0];
128        }
129
130        values.forEach(function (value, key, map) {
131            if (ArgumentMatchers.isRegExp(key) && matcher.matcheReturnKey(params[0], key)) {
132                retrunKet = key;
133            }
134        });
135
136        return values.get(retrunKet);
137    }
138
139    findName(obj, value) {
140        let properties = this.findProperties(obj);
141        let name = null;
142        properties.forEach(
143            function (va1, idx, array) {
144                if (obj[va1] === value) {
145                    name = va1;
146                }
147            }
148        );
149        return name;
150    }
151
152    isFunctionFromPrototype(f, container, propName) {
153        if (container.constructor != Object && container.constructor.prototype !== container) {
154            return container.constructor.prototype[propName] === f;
155        }
156        return false;
157    }
158
159    findProperties(obj, ...arg) {
160        function getProperty(new_obj) {
161            if (new_obj.__proto__ === null) {
162                return [];
163            }
164            let properties = Object.getOwnPropertyNames(new_obj);
165            return [...properties, ...getProperty(new_obj.__proto__)];
166        }
167        return getProperty(obj);
168    }
169
170    recordMethodCall(originalMethod, args) {
171        Function.prototype.getName = function () {
172            return this.name || this.toString().match(/function\s*([^(]*)\(/)[1];
173        };
174        let name = originalMethod.getName();
175        let arglistString = name + '(' + Array.from(args).toString() + ')';
176        let records = this.recordCalls.get(arglistString);
177        if (!records) {
178            records = 0;
179        }
180        records++;
181        this.recordCalls.set(arglistString, records);
182    }
183
184    mockFunc(originalObject, originalMethod) {
185        let tmp = this;
186        this.originalMethod = originalMethod;
187        let f = function () {
188            let args = arguments;
189            let action = tmp.getReturnInfo(f, args);
190            if (originalMethod) {
191                tmp.recordMethodCall(originalMethod, args);
192            }
193            if (action) {
194                return action.apply(this, args);
195            }
196        };
197
198        f.container = null || originalObject;
199        f.original = originalMethod || null;
200
201        if (originalObject && originalMethod) {
202            if (typeof (originalMethod) != 'function') throw new Error('Not a function');
203            var name = this.findName(originalObject, originalMethod);
204            originalObject[name] = f;
205            this.recordMockedMethod.set(name, originalMethod);
206            f.propName = name;
207            f.originalFromPrototype = this.isFunctionFromPrototype(f.original, originalObject, f.propName);
208        }
209        f.mocker = this;
210        this.mFunctions.push(f);
211        this.extend(f, new ExtendInterface(this));
212        return f;
213    }
214
215    verify(methodName, argsArray) {
216        if (!methodName) {
217            throw Error("not a function name");
218        }
219        let a = this.recordCalls.get(methodName + '(' + argsArray.toString() + ')');
220        return new VerificationMode(a ? a : 0);
221    }
222
223    mockObject(object) {
224        if (!object || typeof object === "string") {
225            throw Error(`this ${object} cannot be mocked`);
226        }
227        const _this = this;
228        let mockedObject = {};
229        let keys = Reflect.ownKeys(object);
230        keys.filter(key => (typeof Reflect.get(object, key)) === 'function')
231            .forEach(key => {
232                mockedObject[key] = object[key];
233                mockedObject[key] = _this.mockFunc(mockedObject, mockedObject[key]);
234            });
235        return mockedObject;
236    }
237}
238
239function ifMockedFunction(f) {
240    if (Object.prototype.toString.call(f) != "[object Function]" &&
241        Object.prototype.toString.call(f) != "[object AsyncFunction]") {
242        throw Error("not a function");
243    }
244    if (!f.stub) {
245        throw Error("not a mock function");
246    }
247    return true;
248}
249
250function when(f) {
251    if (ifMockedFunction(f)) {
252        return f.stub.bind(f);
253    }
254}
255
256export {MockKit, when};