• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/*
2 * Copyright (c) 2022-2024 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.js';
17import VerificationMode from './VerificationMode.js';
18import { ArgumentMatchers } from './ArgumentMatchers.js';
19
20class MockKit {
21    constructor() {
22        this.mFunctions = [];
23        this.stubs = new Map();
24        this.recordCalls = new Map();
25        this.currentSetKey = new Map();
26        this.mockObj = null;
27        this.recordMockedMethod = new Map();
28        this.mFunctions = [];
29        this.stubs = new Map();
30        this.recordCalls = new Map();
31        this.currentSetKey = new Map();
32        this.mockObj = null;
33        this.recordMockedMethod = new Map();
34    }
35    init() {
36        this.reset();
37    }
38    reset() {
39        this.mFunctions = [];
40        this.stubs = new Map();
41        this.recordCalls = new Map();
42        this.currentSetKey = new Map();
43        this.mockObj = null;
44        this.recordMockedMethod = new Map();
45    }
46    clearAll() {
47        this.reset();
48    }
49    clear(obj) {
50        if (!obj) {
51            throw Error('Please enter an object to be cleaned');
52        }
53        if (typeof (obj) !== 'object' && typeof (obj) !== 'function') {
54            throw new Error('Not a object or static class');
55        }
56        this.recordMockedMethod.forEach(function (value, key, map) {
57            if (key) {
58                obj[key] = value;
59            }
60        });
61    }
62    ignoreMock(obj, method) {
63        if (typeof (obj) !== 'object' && typeof (obj) !== 'function') {
64            throw new Error('Not a object or static class');
65        }
66        if (typeof (method) !== 'function') {
67            throw new Error('Not a function');
68        }
69        let og = this.recordMockedMethod.get(method.propName);
70        if (og) {
71            obj[method.propName] = og;
72            this.recordMockedMethod.set(method.propName, undefined);
73        }
74    }
75    extend(dest, source) {
76        dest['stub'] = source['stub'];
77        dest['afterReturn'] = source['afterReturn'];
78        dest['afterReturnNothing'] = source['afterReturnNothing'];
79        dest['afterAction'] = source['afterAction'];
80        dest['afterThrow'] = source['afterThrow'];
81        dest['stubMockedCall'] = source['stubMockedCall'];
82        dest['clear'] = source['clear'];
83        return dest;
84    }
85    stubApply(f, params, returnInfo) {
86        let values = this.stubs.get(f);
87        if (!values) {
88            values = new Map();
89        }
90        let key = params[0];
91        if (typeof key == 'undefined') {
92            key = 'anonymous-mock-' + f.propName;
93        }
94        let matcher = new ArgumentMatchers();
95        if (matcher.matcheStubKey(key)) {
96            key = matcher.matcheStubKey(key);
97            if (key) {
98                this.currentSetKey.set(f, key);
99            }
100        }
101        values.set(key, returnInfo);
102        this.stubs.set(f, values);
103    }
104    getReturnInfo(f, params) {
105        let values = this.stubs.get(f);
106        if (!values) {
107            return undefined;
108        }
109        let retrunKet = params[0];
110        if (typeof retrunKet == 'undefined') {
111            retrunKet = 'anonymous-mock-' + f.propName;
112        }
113        let stubSetKey = this.currentSetKey.get(f);
114
115        if (stubSetKey && (typeof (retrunKet) !== 'undefined')) {
116            retrunKet = stubSetKey;
117        }
118        let matcher = new ArgumentMatchers();
119        if (matcher.matcheReturnKey(params[0], undefined, stubSetKey) && matcher.matcheReturnKey(params[0], undefined, stubSetKey) !== stubSetKey) {
120            retrunKet = params[0];
121        }
122        values.forEach(function (value, key, map) {
123            if (ArgumentMatchers.isRegExp(key) && matcher.matcheReturnKey(params[0], key)) {
124                retrunKet = key;
125            }
126        });
127        return values.get(retrunKet);
128    }
129    findName(obj, value) {
130        let properties = this.findProperties(obj);
131        let name = '';
132        properties.filter((item) => (item !== 'caller' && item !== 'arguments')).forEach(function (va1, idx, array) {
133            if (obj[va1] === value) {
134                name = va1;
135            }
136        });
137        return name;
138    }
139    isFunctionFromPrototype(f, container, propName) {
140        if (container.constructor !== Object && container.constructor.prototype !== container) {
141            return container.constructor.prototype[propName] === f;
142        }
143        return false;
144    }
145    findProperties(obj, ...arg) {
146        function getProperty(newObj) {
147            if (newObj.__proto__ === null) {
148                return [];
149            }
150            let properties = Object.getOwnPropertyNames(newObj);
151            return [...properties, ...getProperty(newObj.__proto__)];
152        }
153        return getProperty(obj);
154    }
155    recordMethodCall(originalMethod, args) {
156        originalMethod['getName'] = function () {
157            return this.name || this.toString().match(/function\s*([^(]*)\(/)[1];
158        };
159        let name = originalMethod.getName();
160        let arglistString = name + '(' + Array.from(args).toString() + ')';
161        let records = this.recordCalls.get(arglistString);
162        if (!records) {
163            records = 0;
164        }
165        records++;
166        this.recordCalls.set(arglistString, records);
167    }
168    mockFunc(originalObject, originalMethod) {
169        let tmp = this;
170        this.originalMethod = originalMethod;
171        const _this = this;
172        let f = function () {
173            let args = arguments;
174            let action = tmp.getReturnInfo(f, args);
175            if (originalMethod) {
176                tmp.recordMethodCall(originalMethod, args);
177            }
178            if (action) {
179                return action.apply(_this, args);
180            }
181        };
182        f.container = null || originalObject;
183        f.original = originalMethod || null;
184        if (originalObject && originalMethod) {
185            if (typeof (originalMethod) !== 'function') {
186                throw new Error('Not a function');
187            }
188            var name = this.findName(originalObject, originalMethod);
189            originalObject[name] = f;
190            this.recordMockedMethod.set(name, originalMethod);
191            f.propName = name;
192            f.originalFromPrototype = this.isFunctionFromPrototype(f.original, originalObject, f.propName);
193        }
194        f.mocker = this;
195        this.mFunctions.push(f);
196        this.extend(f, new ExtendInterface(this));
197        return f;
198    }
199    verify(methodName, argsArray) {
200        if (!methodName) {
201            throw Error('not a function name');
202        }
203        let a = this.recordCalls.get(methodName + '(' + argsArray.toString() + ')');
204        return new VerificationMode(a ? a : 0);
205    }
206    mockObject(object) {
207        if (!object || typeof object === 'string') {
208            throw Error(`this ${object} cannot be mocked`);
209        }
210        const _this = this;
211        let mockedObject = {};
212        let keys = Reflect.ownKeys(object);
213        keys.filter(key => (typeof Reflect.get(object, key)) === 'function')
214            .forEach((key) => {
215            mockedObject[key] = object[key];
216            mockedObject[key] = _this.mockFunc(mockedObject, mockedObject[key]);
217        });
218        return mockedObject;
219    }
220}
221function ifMockedFunction(f) {
222    if (Object.prototype.toString.call(f) !== '[object Function]' &&
223        Object.prototype.toString.call(f) !== '[object AsyncFunction]') {
224        throw Error('not a function');
225    }
226    if (!f.stub) {
227        throw Error('not a mock function');
228    }
229    return true;
230}
231function when(f) {
232    if (!ifMockedFunction(f)) {
233        throw Error('not a mock function');
234    }
235        return f.stub.bind(f);
236}
237function MockSetup(target, propertyName, descriptor) {
238    const aboutToAppearOrigin = target.aboutToAppear;
239    const setup = descriptor.value;
240    target.aboutToAppear = function (...args) {
241        if (target.__Param) { // copy attributes and params of the original context
242            try {
243                const map = target.__Param;
244                for (const [key, val] of map) {
245                    this[key] = val; // 'this' refers to context of current function
246                }
247            }
248            catch (e) {
249                throw new Error(`Mock setup param error: ${e}`);
250            }
251        }
252        if (setup) { // apply the mock content
253            try {
254                setup.apply(this);
255            }
256            catch (e) {
257                throw new Error(`Mock setup apply error: ${e}`);
258            }
259        }
260        if (aboutToAppearOrigin) { // append to aboutToAppear function of the original context
261            aboutToAppearOrigin.apply(this, args);
262        }
263    };
264}
265export { MockSetup, MockKit, when };
266