第一次提交
This commit is contained in:
@@ -0,0 +1,4 @@
|
||||
## 1.0.0
|
||||
- 修复once断言问题
|
||||
## 1.0.0-rc
|
||||
- 提供DevEco Studio预览器场景使能的MockSetup装饰器
|
||||
@@ -0,0 +1,177 @@
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
@@ -0,0 +1,82 @@
|
||||
# Hamock
|
||||
|
||||
## 简介
|
||||
|
||||
Hamock 是 OpenHarmony 上的模拟框架,提供预览场景的模拟功能。
|
||||
|
||||
## 下载安装
|
||||
|
||||
```bash
|
||||
ohpm install @ohos/hamock
|
||||
```
|
||||
|
||||
OpenHarmony ohpm 环境配置等更多内容,请参考[如何安装 OpenHarmony ohpm 包](https://gitee.com/openharmony-tpc/docs/blob/master/OpenHarmony_har_usage.md)
|
||||
|
||||
## 使用示例
|
||||
|
||||
Hamock 提供了 @MockSetup 用于修饰 Mock 方法,仅支持声明式范式的组件。当开发者预览该组件时,预览运行时将在组件初始化时执行被 @MockSetup 修饰的方法。因此,开发者可以在这个被修饰的方法内重定义组件的方法或重赋值组件的属性,其将在预览时生效。
|
||||
|
||||
> 说明:
|
||||
> @MockSetup 修饰的方法仅在预览场景会自动触发,并先于组件的 aboutToAppear 执行。
|
||||
|
||||
### UI组件的方法
|
||||
|
||||
在 ArkTS 页面代码中引入 Hamock。在目标组件中定义一个方法,并用 @MockSetup 修饰该方法。在这个方法中,使用 MockKit 模拟目标方法。
|
||||
|
||||
```typescript
|
||||
import { MockKit, when, MockSetup } from '@ohos/hamock';
|
||||
|
||||
@Entry
|
||||
@Component
|
||||
struct Index {
|
||||
...
|
||||
@MockSetup
|
||||
randomName() {
|
||||
let mocker: MockKit = new MockKit();
|
||||
let mockfunc: Object = mocker.mockFunc(this, this.method1);
|
||||
// mock 指定的方法在指定入参的返回值
|
||||
when(mockfunc)('test').afterReturn(1);
|
||||
}
|
||||
...
|
||||
// 业务场景调用方法
|
||||
const result: number = this.method1('test'); // in previewer, result = 1
|
||||
}
|
||||
```
|
||||
|
||||
### UI组件的属性
|
||||
|
||||
在 ArkTS 页面代码中引入 Hamock。在目标组件中定义一个方法,并用 @MockSetup 修饰该方法。在这个方法中,对于需要 Mock 的属性,可以重新赋值。
|
||||
|
||||
```typescript
|
||||
import { MockSetup } from '@ohos/hamock';
|
||||
|
||||
@Component
|
||||
struct Person {
|
||||
@Prop species: string;
|
||||
...
|
||||
// 在 @MockSetup 片段中,定义对象属性
|
||||
@MockSetup
|
||||
randomName() {
|
||||
this.species = 'primates';
|
||||
}
|
||||
...
|
||||
// 业务场景调用属性(如果从初始化到调用期间,该属性无变化)
|
||||
const result: string = this.species; // in previewer, result = primates
|
||||
}
|
||||
```
|
||||
|
||||
## 约束与限制
|
||||
|
||||
在下述版本验证通过:
|
||||
|
||||
DevEco Studio: 4.1 (4.1.3.400), SDK: API11 (4.1.0.36)
|
||||
|
||||
MockSetup 仅在 API11 支持。
|
||||
|
||||
## 贡献代码
|
||||
|
||||
使用过程中发现任何问题都可以提[Issue](https://gitee.com/openharmony/testfwk_arkxtest/issues) 给我们,当然,我们也非常欢迎你给我们提[PR](https://gitee.com/openharmony/testfwk_arkxtest/pulls) 。
|
||||
|
||||
## 开源协议
|
||||
|
||||
本项目基于 [Apache License 2.0](https://gitee.com/openharmony/testfwk_arkxtest/blob/master/hamock/LICENSE) ,请自由地享受和参与开源。
|
||||
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Huawei Device Co., Ltd.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
{
|
||||
"apiType": "stageMode",
|
||||
"buildOption": {
|
||||
},
|
||||
"targets": [
|
||||
{
|
||||
"name": "default"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Huawei Device Co., Ltd.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
// Script for compiling build behavior. It is built in the build plug-in and cannot be modified currently.
|
||||
export { harTasks } from '@ohos/hvigor-ohos-plugin';
|
||||
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Huawei Device Co., Ltd.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License")
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
// Script for compiling build behavior. It is built in the build plug-in and cannot be modified currently.
|
||||
export { harTasks } from '@ohos/hvigor-ohos-plugin';
|
||||
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Huawei Device Co., Ltd.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License")
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export class ArgumentMatchers {
|
||||
static any;
|
||||
static anyString;
|
||||
static anyBoolean;
|
||||
static anyNumber;
|
||||
static anyObj;
|
||||
static anyFunction;
|
||||
static matchRegexs(Regex: RegExp): void
|
||||
}
|
||||
|
||||
declare interface when {
|
||||
afterReturn(value: any): any
|
||||
afterReturnNothing(): undefined
|
||||
afterAction(action: any): any
|
||||
afterThrow(e_msg: string): string
|
||||
(argMatchers?: any): when;
|
||||
}
|
||||
|
||||
export const when: when;
|
||||
|
||||
export interface VerificationMode {
|
||||
times(count: Number): void
|
||||
never(): void
|
||||
once(): void
|
||||
atLeast(count: Number): void
|
||||
atMost(count: Number): void
|
||||
}
|
||||
|
||||
export class MockKit {
|
||||
constructor()
|
||||
mockFunc(obj: Object, func: Function): Function
|
||||
mockObject(obj: Object): Object
|
||||
verify(methodName: String, argsArray: Array<any>): VerificationMode
|
||||
ignoreMock(obj: Object, func: Function): void
|
||||
clear(obj: Object): void
|
||||
clearAll(): void
|
||||
}
|
||||
|
||||
export declare function MockSetup(
|
||||
target: Object,
|
||||
propertyName: string | Symbol,
|
||||
descriptor: TypedPropertyDescriptor<() => void>
|
||||
): void;
|
||||
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Huawei Device Co., Ltd.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License")
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export { MockSetup, MockKit, when } from './src/main/mock/MockKit';
|
||||
export { ArgumentMatchers } from './src/main/mock/ArgumentMatchers';
|
||||
@@ -0,0 +1,16 @@
|
||||
/*
|
||||
* Copyright (c) 2021-2023 Huawei Device Co., Ltd.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License")
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
export { MockSetup, MockKit, when } from './src/main/mock/MockKit.js';
|
||||
export { ArgumentMatchers } from './src/main/mock/ArgumentMatchers.js';
|
||||
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* Copyright (c) 2021-2023 Huawei Device Co., Ltd.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License")
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export { MockSetup, MockKit, when } from './src/main/mock/MockKit.js';
|
||||
export { ArgumentMatchers } from './src/main/mock/ArgumentMatchers.js';
|
||||
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Huawei Device Co., Ltd.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
{
|
||||
name: '@ohos/hamock',
|
||||
version: '1.0.0',
|
||||
description: 'A mock framework for OpenHarmony application.',
|
||||
main: 'index.ets',
|
||||
author: 'huawei',
|
||||
license: 'Apache-2.0',
|
||||
dependencies: {},
|
||||
ohos: {
|
||||
org: 'ohos',
|
||||
},
|
||||
types: 'index.d.ts'
|
||||
}
|
||||
+97
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
* Copyright (c) 2022 Huawei Device Co., Ltd.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
export class ArgumentMatchers {
|
||||
constructor() {
|
||||
this.ANY = "<any>";
|
||||
this.ANY_STRING = "<any String>";
|
||||
this.ANY_BOOLEAN = "<any Boolean>";
|
||||
this.ANY_NUMBER = "<any Number>";
|
||||
this.ANY_OBJECT = "<any Object>";
|
||||
this.ANY_FUNCTION = "<any Function>";
|
||||
this.MATCH_REGEXS = "<match regexs>";
|
||||
}
|
||||
static any() {
|
||||
}
|
||||
static anyString() {
|
||||
}
|
||||
static anyBoolean() {
|
||||
}
|
||||
static anyNumber() {
|
||||
}
|
||||
static anyObj() {
|
||||
}
|
||||
static anyFunction() {
|
||||
}
|
||||
static matchRegexs(regex) {
|
||||
if (ArgumentMatchers.isRegExp(regex)) {
|
||||
return regex;
|
||||
}
|
||||
throw Error("not a regex");
|
||||
}
|
||||
static isRegExp(value) {
|
||||
return Object.prototype.toString.call(value) === "[object RegExp]";
|
||||
}
|
||||
matcheReturnKey(...args) {
|
||||
let arg = args[0];
|
||||
let regex = args[1];
|
||||
let stubSetKey = args[2];
|
||||
if (stubSetKey && stubSetKey == this.ANY) {
|
||||
return this.ANY;
|
||||
}
|
||||
if (typeof arg === "string" && !regex) {
|
||||
return this.ANY_STRING;
|
||||
}
|
||||
if (typeof arg === "boolean" && !regex) {
|
||||
return this.ANY_BOOLEAN;
|
||||
}
|
||||
if (typeof arg === "number" && !regex) {
|
||||
return this.ANY_NUMBER;
|
||||
}
|
||||
if (typeof arg === "object" && !regex) {
|
||||
return this.ANY_OBJECT;
|
||||
}
|
||||
if (typeof arg === "function" && !regex) {
|
||||
return this.ANY_FUNCTION;
|
||||
}
|
||||
if (typeof arg === "string" && regex) {
|
||||
return regex.test(arg);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
matcheStubKey(key) {
|
||||
if (key === ArgumentMatchers.any) {
|
||||
return this.ANY;
|
||||
}
|
||||
if (key === ArgumentMatchers.anyString) {
|
||||
return this.ANY_STRING;
|
||||
}
|
||||
if (key === ArgumentMatchers.anyBoolean) {
|
||||
return this.ANY_BOOLEAN;
|
||||
}
|
||||
if (key === ArgumentMatchers.anyNumber) {
|
||||
return this.ANY_NUMBER;
|
||||
}
|
||||
if (key === ArgumentMatchers.anyObj) {
|
||||
return this.ANY_OBJECT;
|
||||
}
|
||||
if (key === ArgumentMatchers.anyFunction) {
|
||||
return this.ANY_FUNCTION;
|
||||
}
|
||||
if (ArgumentMatchers.isRegExp(key)) {
|
||||
return key;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
+118
@@ -0,0 +1,118 @@
|
||||
/*
|
||||
* Copyright (c) 2022 Huawei Device Co., Ltd.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export class ArgumentMatchers {
|
||||
ANY = "<any>";
|
||||
ANY_STRING = "<any String>";
|
||||
ANY_BOOLEAN = "<any Boolean>";
|
||||
ANY_NUMBER = "<any Number>";
|
||||
ANY_OBJECT = "<any Object>";
|
||||
ANY_FUNCTION = "<any Function>";
|
||||
MATCH_REGEXS = "<match regexs>";
|
||||
|
||||
static any() {
|
||||
}
|
||||
|
||||
static anyString() {
|
||||
}
|
||||
|
||||
static anyBoolean() {
|
||||
}
|
||||
|
||||
static anyNumber() {
|
||||
}
|
||||
|
||||
static anyObj() {
|
||||
}
|
||||
|
||||
static anyFunction() {
|
||||
}
|
||||
|
||||
static matchRegexs(regex: any) {
|
||||
if (ArgumentMatchers.isRegExp(regex)) {
|
||||
return regex;
|
||||
}
|
||||
throw Error("not a regex");
|
||||
}
|
||||
|
||||
static isRegExp(value: string) {
|
||||
return Object.prototype.toString.call(value) === "[object RegExp]";
|
||||
}
|
||||
|
||||
matcheReturnKey(...args: Array<any>) {
|
||||
let arg = args[0];
|
||||
let regex = args[1];
|
||||
let stubSetKey = args[2];
|
||||
|
||||
if (stubSetKey && stubSetKey == this.ANY) {
|
||||
return this.ANY;
|
||||
}
|
||||
|
||||
if (typeof arg === "string" && !regex) {
|
||||
return this.ANY_STRING;
|
||||
}
|
||||
|
||||
if (typeof arg === "boolean" && !regex) {
|
||||
return this.ANY_BOOLEAN;
|
||||
}
|
||||
|
||||
if (typeof arg === "number" && !regex) {
|
||||
return this.ANY_NUMBER;
|
||||
}
|
||||
|
||||
if (typeof arg === "object" && !regex) {
|
||||
return this.ANY_OBJECT;
|
||||
}
|
||||
|
||||
if (typeof arg === "function" && !regex) {
|
||||
return this.ANY_FUNCTION;
|
||||
}
|
||||
|
||||
if (typeof arg === "string" && regex) {
|
||||
return regex.test(arg);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
matcheStubKey(key: any) {
|
||||
|
||||
if (key === ArgumentMatchers.any) {
|
||||
return this.ANY;
|
||||
}
|
||||
|
||||
if (key === ArgumentMatchers.anyString) {
|
||||
return this.ANY_STRING;
|
||||
}
|
||||
if (key === ArgumentMatchers.anyBoolean) {
|
||||
return this.ANY_BOOLEAN;
|
||||
}
|
||||
if (key === ArgumentMatchers.anyNumber) {
|
||||
return this.ANY_NUMBER;
|
||||
}
|
||||
if (key === ArgumentMatchers.anyObj) {
|
||||
return this.ANY_OBJECT;
|
||||
}
|
||||
if (key === ArgumentMatchers.anyFunction) {
|
||||
return this.ANY_FUNCTION;
|
||||
}
|
||||
|
||||
if (ArgumentMatchers.isRegExp(key)) {
|
||||
return key;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright (c) 2022 Huawei Device Co., Ltd.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
class ExtendInterface {
|
||||
constructor(mocker) {
|
||||
this.mocker = mocker;
|
||||
}
|
||||
stub() {
|
||||
this.params = arguments;
|
||||
return this;
|
||||
}
|
||||
stubMockedCall(returnInfo) {
|
||||
this.mocker.stubApply(this, this.params, returnInfo);
|
||||
}
|
||||
afterReturn(value) {
|
||||
this.stubMockedCall(function () {
|
||||
return value;
|
||||
});
|
||||
}
|
||||
afterReturnNothing() {
|
||||
this.stubMockedCall(function () {
|
||||
return undefined;
|
||||
});
|
||||
}
|
||||
afterAction(action) {
|
||||
this.stubMockedCall(action);
|
||||
}
|
||||
afterThrow(msg) {
|
||||
this.stubMockedCall(function () {
|
||||
throw msg;
|
||||
});
|
||||
}
|
||||
clear(obj) {
|
||||
this.mocker.clear(obj);
|
||||
}
|
||||
}
|
||||
export default ExtendInterface;
|
||||
+63
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* Copyright (c) 2022 Huawei Device Co., Ltd.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { MockKit } from "./MockKit.js";
|
||||
|
||||
class ExtendInterface {
|
||||
|
||||
private mocker: MockKit
|
||||
private params: any
|
||||
|
||||
constructor(mocker: MockKit) {
|
||||
this.mocker = mocker;
|
||||
}
|
||||
|
||||
stub() {
|
||||
this.params = arguments;
|
||||
return this;
|
||||
}
|
||||
|
||||
stubMockedCall(returnInfo: any) {
|
||||
this.mocker.stubApply(this, this.params, returnInfo);
|
||||
}
|
||||
|
||||
afterReturn(value: any) {
|
||||
this.stubMockedCall(function () {
|
||||
return value;
|
||||
});
|
||||
}
|
||||
|
||||
afterReturnNothing() {
|
||||
this.stubMockedCall(function () {
|
||||
return undefined;
|
||||
});
|
||||
}
|
||||
|
||||
afterAction(action: Function) {
|
||||
this.stubMockedCall(action);
|
||||
}
|
||||
|
||||
afterThrow(msg: string) {
|
||||
this.stubMockedCall(function () {
|
||||
throw msg;
|
||||
});
|
||||
}
|
||||
|
||||
clear(obj?: any) {
|
||||
this.mocker.clear(obj);
|
||||
}
|
||||
}
|
||||
|
||||
export default ExtendInterface;
|
||||
@@ -0,0 +1,253 @@
|
||||
/*
|
||||
* Copyright (c) 2022-2023 Huawei Device Co., Ltd.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import ExtendInterface from "./ExtendInterface.js";
|
||||
import VerificationMode from "./VerificationMode.js";
|
||||
import { ArgumentMatchers } from "./ArgumentMatchers.js";
|
||||
class MockKit {
|
||||
constructor() {
|
||||
this.mFunctions = [];
|
||||
this.stubs = new Map();
|
||||
this.recordCalls = new Map();
|
||||
this.currentSetKey = new Map();
|
||||
this.mockObj = null;
|
||||
this.recordMockedMethod = new Map();
|
||||
this.mFunctions = [];
|
||||
this.stubs = new Map();
|
||||
this.recordCalls = new Map();
|
||||
this.currentSetKey = new Map();
|
||||
this.mockObj = null;
|
||||
this.recordMockedMethod = new Map();
|
||||
}
|
||||
init() {
|
||||
this.reset();
|
||||
}
|
||||
reset() {
|
||||
this.mFunctions = [];
|
||||
this.stubs = new Map();
|
||||
this.recordCalls = new Map();
|
||||
this.currentSetKey = new Map();
|
||||
this.mockObj = null;
|
||||
this.recordMockedMethod = new Map();
|
||||
}
|
||||
clearAll() {
|
||||
this.reset();
|
||||
}
|
||||
clear(obj) {
|
||||
if (!obj) throw Error("Please enter an object to be cleaned");
|
||||
if (typeof (obj) !== 'object' && typeof (obj) !== 'function') throw new Error('Not a object or static class');
|
||||
this.recordMockedMethod.forEach(function (value, key, map) {
|
||||
if (key) {
|
||||
obj[key] = value;
|
||||
}
|
||||
});
|
||||
}
|
||||
ignoreMock(obj, method) {
|
||||
if (typeof (obj) !== 'object' && typeof (obj) !== 'function') throw new Error('Not a object or static class');
|
||||
if (typeof (method) !== 'function') throw new Error('Not a function');
|
||||
let og = this.recordMockedMethod.get(method.propName);
|
||||
if (og) {
|
||||
obj[method.propName] = og;
|
||||
this.recordMockedMethod.set(method.propName, undefined);
|
||||
}
|
||||
}
|
||||
extend(dest, source) {
|
||||
dest["stub"] = source["stub"];
|
||||
dest["afterReturn"] = source["afterReturn"];
|
||||
dest["afterReturnNothing"] = source["afterReturnNothing"];
|
||||
dest["afterAction"] = source["afterAction"];
|
||||
dest["afterThrow"] = source["afterThrow"];
|
||||
dest["stubMockedCall"] = source["stubMockedCall"];
|
||||
dest["clear"] = source["clear"];
|
||||
return dest;
|
||||
}
|
||||
stubApply(f, params, returnInfo) {
|
||||
let values = this.stubs.get(f);
|
||||
if (!values) {
|
||||
values = new Map();
|
||||
}
|
||||
let key = params[0];
|
||||
if (typeof key === "undefined") {
|
||||
key = "anonymous-mock-" + f.propName;
|
||||
}
|
||||
let matcher = new ArgumentMatchers();
|
||||
if (matcher.matcheStubKey(key)) {
|
||||
key = matcher.matcheStubKey(key);
|
||||
if (key) {
|
||||
this.currentSetKey.set(f, key);
|
||||
}
|
||||
}
|
||||
values.set(key, returnInfo);
|
||||
this.stubs.set(f, values);
|
||||
}
|
||||
getReturnInfo(f, params) {
|
||||
let values = this.stubs.get(f);
|
||||
if (!values) {
|
||||
return undefined;
|
||||
}
|
||||
let retrunKet = params[0];
|
||||
if (typeof retrunKet === "undefined") {
|
||||
retrunKet = "anonymous-mock-" + f.propName;
|
||||
}
|
||||
let stubSetKey = this.currentSetKey.get(f);
|
||||
|
||||
if (stubSetKey && (typeof (retrunKet) !== "undefined")) {
|
||||
retrunKet = stubSetKey;
|
||||
}
|
||||
let matcher = new ArgumentMatchers();
|
||||
if (matcher.matcheReturnKey(params[0], undefined, stubSetKey) && matcher.matcheReturnKey(params[0], undefined, stubSetKey) !== stubSetKey) {
|
||||
retrunKet = params[0];
|
||||
}
|
||||
values.forEach(function (value, key, map) {
|
||||
if (ArgumentMatchers.isRegExp(key) && matcher.matcheReturnKey(params[0], key)) {
|
||||
retrunKet = key;
|
||||
}
|
||||
});
|
||||
return values.get(retrunKet);
|
||||
}
|
||||
findName(obj, value) {
|
||||
let properties = this.findProperties(obj);
|
||||
let name = '';
|
||||
properties.filter((item) => (item !== 'caller' && item !== 'arguments')).forEach(function (va1, idx, array) {
|
||||
if (obj[va1] === value) {
|
||||
name = va1;
|
||||
}
|
||||
});
|
||||
return name;
|
||||
}
|
||||
isFunctionFromPrototype(f, container, propName) {
|
||||
if (container.constructor !== Object && container.constructor.prototype !== container) {
|
||||
return container.constructor.prototype[propName] === f;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
findProperties(obj, ...arg) {
|
||||
function getProperty(new_obj) {
|
||||
if (new_obj.__proto__ === null) {
|
||||
return [];
|
||||
}
|
||||
let properties = Object.getOwnPropertyNames(new_obj);
|
||||
return [...properties, ...getProperty(new_obj.__proto__)];
|
||||
}
|
||||
return getProperty(obj);
|
||||
}
|
||||
recordMethodCall(originalMethod, args) {
|
||||
originalMethod['getName'] = function () {
|
||||
return this.name || this.toString().match(/function\s*([^(]*)\(/)[1];
|
||||
};
|
||||
let name = originalMethod.getName();
|
||||
let arglistString = name + '(' + Array.from(args).toString() + ')';
|
||||
let records = this.recordCalls.get(arglistString);
|
||||
if (!records) {
|
||||
records = 0;
|
||||
}
|
||||
records++;
|
||||
this.recordCalls.set(arglistString, records);
|
||||
}
|
||||
mockFunc(originalObject, originalMethod) {
|
||||
let tmp = this;
|
||||
this.originalMethod = originalMethod;
|
||||
const _this = this;
|
||||
let f = function () {
|
||||
let args = arguments;
|
||||
let action = tmp.getReturnInfo(f, args);
|
||||
if (originalMethod) {
|
||||
tmp.recordMethodCall(originalMethod, args);
|
||||
}
|
||||
if (action) {
|
||||
return action.apply(_this, args);
|
||||
}
|
||||
};
|
||||
f.container = null || originalObject;
|
||||
f.original = originalMethod || null;
|
||||
if (originalObject && originalMethod) {
|
||||
if (typeof (originalMethod) != 'function')
|
||||
throw new Error('Not a function');
|
||||
var name = this.findName(originalObject, originalMethod);
|
||||
originalObject[name] = f;
|
||||
this.recordMockedMethod.set(name, originalMethod);
|
||||
f.propName = name;
|
||||
f.originalFromPrototype = this.isFunctionFromPrototype(f.original, originalObject, f.propName);
|
||||
}
|
||||
f.mocker = this;
|
||||
this.mFunctions.push(f);
|
||||
this.extend(f, new ExtendInterface(this));
|
||||
return f;
|
||||
}
|
||||
verify(methodName, argsArray) {
|
||||
if (!methodName) {
|
||||
throw Error("not a function name");
|
||||
}
|
||||
let a = this.recordCalls.get(methodName + '(' + argsArray.toString() + ')');
|
||||
return new VerificationMode(a ? a : 0);
|
||||
}
|
||||
mockObject(object) {
|
||||
if (!object || typeof object === "string") {
|
||||
throw Error(`this ${object} cannot be mocked`);
|
||||
}
|
||||
const _this = this;
|
||||
let mockedObject = {};
|
||||
let keys = Reflect.ownKeys(object);
|
||||
keys.filter(key => (typeof Reflect.get(object, key)) === 'function')
|
||||
.forEach((key) => {
|
||||
mockedObject[key] = object[key];
|
||||
mockedObject[key] = _this.mockFunc(mockedObject, mockedObject[key]);
|
||||
});
|
||||
return mockedObject;
|
||||
}
|
||||
}
|
||||
function ifMockedFunction(f) {
|
||||
if (Object.prototype.toString.call(f) != "[object Function]" &&
|
||||
Object.prototype.toString.call(f) != "[object AsyncFunction]") {
|
||||
throw Error("not a function");
|
||||
}
|
||||
if (!f.stub) {
|
||||
throw Error("not a mock function");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
function when(f) {
|
||||
if (ifMockedFunction(f)) {
|
||||
return f.stub.bind(f);
|
||||
}
|
||||
}
|
||||
function MockSetup(target, propertyName, descriptor) {
|
||||
const aboutToAppearOrigin = target.aboutToAppear;
|
||||
const setup = descriptor.value;
|
||||
target.aboutToAppear = function (...args) {
|
||||
if (target.__Param) { // copy attributes and params of the original context
|
||||
try {
|
||||
const map = target.__Param;
|
||||
for (const [key, val] of map) {
|
||||
this[key] = val; // 'this' refers to context of current function
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
throw new Error(`Mock setup param error: ${e}`);
|
||||
}
|
||||
}
|
||||
if (setup) { // apply the mock content
|
||||
try {
|
||||
setup.apply(this);
|
||||
}
|
||||
catch (e) {
|
||||
throw new Error(`Mock setup apply error: ${e}`);
|
||||
}
|
||||
}
|
||||
if (aboutToAppearOrigin) { // append to aboutToAppear function of the original context
|
||||
aboutToAppearOrigin.apply(this, args);
|
||||
}
|
||||
};
|
||||
}
|
||||
export { MockSetup, MockKit, when };
|
||||
@@ -0,0 +1,294 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Huawei Device Co., Ltd.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import ExtendInterface from "./ExtendInterface.js";
|
||||
import VerificationMode from "./VerificationMode.js";
|
||||
import { ArgumentMatchers } from "./ArgumentMatchers.js";
|
||||
|
||||
interface IFunction extends Function {
|
||||
container: any;
|
||||
original: any;
|
||||
propName: string;
|
||||
originalFromPrototype: boolean
|
||||
mocker: MockKit
|
||||
}
|
||||
|
||||
class MockKit {
|
||||
|
||||
private mFunctions:Array<any> = [];
|
||||
private stubs = new Map();
|
||||
private recordCalls = new Map();
|
||||
private currentSetKey = new Map();
|
||||
private mockObj = null;
|
||||
private recordMockedMethod = new Map();
|
||||
private originalMethod: any;
|
||||
|
||||
constructor() {
|
||||
this.mFunctions = [];
|
||||
this.stubs = new Map();
|
||||
this.recordCalls = new Map();
|
||||
this.currentSetKey = new Map();
|
||||
this.mockObj = null;
|
||||
this.recordMockedMethod = new Map();
|
||||
}
|
||||
|
||||
init() {
|
||||
this.reset();
|
||||
}
|
||||
|
||||
reset() {
|
||||
this.mFunctions = [];
|
||||
this.stubs = new Map()
|
||||
this.recordCalls = new Map();
|
||||
this.currentSetKey = new Map();
|
||||
this.mockObj = null;
|
||||
this.recordMockedMethod = new Map();
|
||||
}
|
||||
|
||||
clearAll() {
|
||||
this.reset();
|
||||
}
|
||||
|
||||
clear(obj: any) {
|
||||
if (!obj) throw Error("Please enter an object to be cleaned");
|
||||
if (typeof (obj) != 'object') throw new Error('Not a object');
|
||||
this.recordMockedMethod.forEach(function (value, key, map) {
|
||||
if (key) {
|
||||
obj[key] = value;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
ignoreMock(obj:any, method: any) {
|
||||
if (typeof (obj) != 'object') throw new Error('Not a object');
|
||||
if (typeof (method) != 'function') throw new Error('Not a function');
|
||||
let og = this.recordMockedMethod.get(method.propName);
|
||||
if (og) {
|
||||
obj[method.propName] = og;
|
||||
this.recordMockedMethod.set(method.propName, undefined);
|
||||
}
|
||||
}
|
||||
|
||||
extend(dest: any, source:any) {
|
||||
dest["stub"] = source["stub"];
|
||||
dest["afterReturn"] = source["afterReturn"];
|
||||
dest["afterReturnNothing"] = source["afterReturnNothing"];
|
||||
dest["afterAction"] = source["afterAction"];
|
||||
dest["afterThrow"] = source["afterThrow"];
|
||||
dest["stubMockedCall"] = source["stubMockedCall"];
|
||||
dest["clear"] = source["clear"];
|
||||
return dest;
|
||||
}
|
||||
|
||||
stubApply(f: any, params:any, returnInfo:any) {
|
||||
let values = this.stubs.get(f);
|
||||
if (!values) {
|
||||
values = new Map();
|
||||
}
|
||||
let key = params[0];
|
||||
if (typeof key == "undefined") {
|
||||
key = "anonymous-mock-" + f.propName;
|
||||
}
|
||||
let matcher = new ArgumentMatchers();
|
||||
if (matcher.matcheStubKey(key)) {
|
||||
key = matcher.matcheStubKey(key);
|
||||
if (key) {
|
||||
this.currentSetKey.set(f, key);
|
||||
}
|
||||
}
|
||||
values.set(key, returnInfo);
|
||||
this.stubs.set(f, values);
|
||||
}
|
||||
|
||||
getReturnInfo(f: any, params:any) {
|
||||
let values = this.stubs.get(f);
|
||||
if (!values) {
|
||||
return undefined;
|
||||
}
|
||||
let retrunKet = params[0];
|
||||
if (typeof retrunKet == "undefined") {
|
||||
retrunKet = "anonymous-mock-" + f.propName;
|
||||
}
|
||||
let stubSetKey = this.currentSetKey.get(f);
|
||||
|
||||
if (stubSetKey && (typeof (retrunKet) != "undefined")) {
|
||||
retrunKet = stubSetKey;
|
||||
}
|
||||
let matcher = new ArgumentMatchers();
|
||||
if (matcher.matcheReturnKey(params[0], undefined, stubSetKey) && matcher.matcheReturnKey(params[0], undefined, stubSetKey) != stubSetKey) {
|
||||
retrunKet = params[0];
|
||||
}
|
||||
|
||||
values.forEach(function (value: any, key: any, map: any) {
|
||||
if (ArgumentMatchers.isRegExp(key) && matcher.matcheReturnKey(params[0], key)) {
|
||||
retrunKet = key;
|
||||
}
|
||||
});
|
||||
|
||||
return values.get(retrunKet);
|
||||
}
|
||||
|
||||
findName(obj: any, value: any) {
|
||||
let properties = this.findProperties(obj);
|
||||
let name = '';
|
||||
properties.filter((item:any) => (item !== 'caller' && item !== 'arguments')).forEach(
|
||||
function (va1:any, idx:any, array:any) {
|
||||
if (obj[va1] === value) {
|
||||
name = va1;
|
||||
}
|
||||
}
|
||||
);
|
||||
return name;
|
||||
}
|
||||
|
||||
isFunctionFromPrototype(f: Function, container:Function, propName: string) {
|
||||
if (container.constructor != Object && container.constructor.prototype !== container) {
|
||||
return container.constructor.prototype[propName] === f;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
findProperties(obj: any, ...arg: Array<any>) {
|
||||
function getProperty(new_obj:any): Array<any> {
|
||||
if (new_obj.__proto__ === null) {
|
||||
return [];
|
||||
}
|
||||
let properties = Object.getOwnPropertyNames(new_obj);
|
||||
return [...properties, ...getProperty(new_obj.__proto__)];
|
||||
}
|
||||
return getProperty(obj);
|
||||
}
|
||||
|
||||
recordMethodCall(originalMethod: any, args: any) {
|
||||
originalMethod['getName'] = function () {
|
||||
return this.name || this.toString().match(/function\s*([^(]*)\(/)[1];
|
||||
}
|
||||
let name = originalMethod.getName();
|
||||
let arglistString = name + '(' + Array.from(args).toString() + ')';
|
||||
let records = this.recordCalls.get(arglistString);
|
||||
if (!records) {
|
||||
records = 0;
|
||||
}
|
||||
records++;
|
||||
this.recordCalls.set(arglistString, records);
|
||||
}
|
||||
|
||||
mockFunc(originalObject:any, originalMethod:any) {
|
||||
let tmp = this;
|
||||
this.originalMethod = originalMethod;
|
||||
const _this = this;
|
||||
let f:any = function () {
|
||||
let args = arguments;
|
||||
let action = tmp.getReturnInfo(f, args);
|
||||
if (originalMethod) {
|
||||
tmp.recordMethodCall(originalMethod, args);
|
||||
}
|
||||
if (action) {
|
||||
return <IFunction> action.apply(_this, args);
|
||||
}
|
||||
};
|
||||
|
||||
f.container = null || originalObject;
|
||||
f.original = originalMethod || null;
|
||||
|
||||
if (originalObject && originalMethod) {
|
||||
if (typeof (originalMethod) != 'function') throw new Error('Not a function');
|
||||
var name = this.findName(originalObject, originalMethod);
|
||||
originalObject[name] = f;
|
||||
this.recordMockedMethod.set(name, originalMethod);
|
||||
f.propName = name;
|
||||
f.originalFromPrototype = this.isFunctionFromPrototype(f.original, originalObject, f.propName);
|
||||
}
|
||||
f.mocker = this;
|
||||
this.mFunctions.push(f);
|
||||
this.extend(f, new ExtendInterface(this));
|
||||
return f;
|
||||
}
|
||||
|
||||
verify(methodName:any, argsArray:any) {
|
||||
if (!methodName) {
|
||||
throw Error("not a function name");
|
||||
}
|
||||
let a = this.recordCalls.get(methodName + '(' + argsArray.toString() + ')');
|
||||
return new VerificationMode(a ? a : 0);
|
||||
}
|
||||
|
||||
mockObject(object: any) {
|
||||
if (!object || typeof object === "string") {
|
||||
throw Error(`this ${object} cannot be mocked`);
|
||||
}
|
||||
const _this = this;
|
||||
let mockedObject:any = {};
|
||||
let keys = Reflect.ownKeys(object);
|
||||
keys.filter(key => (typeof Reflect.get(object, key)) === 'function')
|
||||
.forEach((key:any) => {
|
||||
mockedObject[key] = object[key];
|
||||
mockedObject[key] = _this.mockFunc(mockedObject, mockedObject[key]);
|
||||
});
|
||||
return mockedObject;
|
||||
}
|
||||
}
|
||||
|
||||
function ifMockedFunction(f: any) {
|
||||
if (Object.prototype.toString.call(f) != "[object Function]" &&
|
||||
Object.prototype.toString.call(f) != "[object AsyncFunction]") {
|
||||
throw Error("not a function");
|
||||
}
|
||||
if (!f.stub) {
|
||||
throw Error("not a mock function");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function when(f: any) {
|
||||
if (ifMockedFunction(f)) {
|
||||
return f.stub.bind(f);
|
||||
}
|
||||
}
|
||||
|
||||
function MockSetup(target: Object, propertyName: string | Symbol, descriptor: TypedPropertyDescriptor<() => void>): void {
|
||||
const aboutToAppearOrigin = target.aboutToAppear;
|
||||
const setup = descriptor.value;
|
||||
target.aboutToAppear = function (...args: any[]) {
|
||||
if (target.__Param) { // copy attributes and params of the original context
|
||||
try {
|
||||
const map = target.__Param as Map<string, unknown>;
|
||||
for (const [key, val] of map) {
|
||||
this[key] = val; // 'this' refers to context of current function
|
||||
}
|
||||
} catch (e) {
|
||||
throw new Error(`Mock setup param error: ${e}`);
|
||||
}
|
||||
}
|
||||
|
||||
if (setup) { // apply the mock content
|
||||
try {
|
||||
setup.apply(this);
|
||||
} catch (e) {
|
||||
throw new Error(`Mock setup apply error: ${e}`);
|
||||
}
|
||||
}
|
||||
|
||||
if (aboutToAppearOrigin) { // append to aboutToAppear function of the original context
|
||||
aboutToAppearOrigin.apply(this, args);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export {
|
||||
MockSetup,
|
||||
MockKit,
|
||||
when
|
||||
};
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright (c) 2022-2023 Huawei Device Co., Ltd.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
class VerificationMode {
|
||||
constructor(times) {
|
||||
this.doTimes = times;
|
||||
}
|
||||
times(count) {
|
||||
if (count !== this.doTimes) {
|
||||
throw Error(`expect ${count} actual ${this.doTimes}`);
|
||||
}
|
||||
}
|
||||
never() {
|
||||
if (this.doTimes !== 0) {
|
||||
throw Error(`expect 0 actual ${this.doTimes}`);
|
||||
}
|
||||
}
|
||||
once() {
|
||||
if (this.doTimes !== 1) {
|
||||
throw Error(`expect 1 actual ${this.doTimes}`);
|
||||
}
|
||||
}
|
||||
atLeast(count) {
|
||||
if (count > this.doTimes) {
|
||||
throw Error('failed ' + count + ' greater than the actual execution times of method');
|
||||
}
|
||||
}
|
||||
atMost(count) {
|
||||
if (count < this.doTimes) {
|
||||
throw Error('failed ' + count + ' less than the actual execution times of method');
|
||||
}
|
||||
}
|
||||
}
|
||||
export default VerificationMode;
|
||||
+56
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Copyright (c) 2022-2023 Huawei Device Co., Ltd.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
|
||||
class VerificationMode {
|
||||
|
||||
private doTimes: number
|
||||
|
||||
constructor(times: number) {
|
||||
this.doTimes = times;
|
||||
}
|
||||
|
||||
times(count: number) {
|
||||
if(count !== this.doTimes) {
|
||||
throw Error(`expect ${count} actual ${this.doTimes}`);
|
||||
}
|
||||
}
|
||||
|
||||
never() {
|
||||
if (this.doTimes !== 0) {
|
||||
throw Error(`expect 0 actual ${this.doTimes}`);
|
||||
}
|
||||
}
|
||||
|
||||
once() {
|
||||
if (this.doTimes !== 1) {
|
||||
throw Error(`expect 1 actual ${this.doTimes}`);
|
||||
}
|
||||
}
|
||||
|
||||
atLeast(count: number) {
|
||||
if (count > this.doTimes) {
|
||||
throw Error('failed ' + count + ' greater than the actual execution times of method');
|
||||
}
|
||||
}
|
||||
|
||||
atMost(count: number) {
|
||||
if (count < this.doTimes) {
|
||||
throw Error('failed ' + count + ' less than the actual execution times of method');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default VerificationMode;
|
||||
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"app": {
|
||||
"bundleName": "com.example.hamock",
|
||||
"debug": true,
|
||||
"versionCode": 1000000,
|
||||
"versionName": "1.0.0",
|
||||
"minAPIVersion": 9,
|
||||
"targetAPIVersion": 9,
|
||||
"apiReleaseType": "Release"
|
||||
},
|
||||
"module": {
|
||||
"name": "hamock",
|
||||
"type": "har",
|
||||
"deviceTypes": [
|
||||
"default",
|
||||
"tablet",
|
||||
"tv",
|
||||
"wearable",
|
||||
"car"
|
||||
]
|
||||
}
|
||||
}
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-04/schema#",
|
||||
"title": "JSON schema for mock-config.json5 file",
|
||||
"definitions": {
|
||||
"sourceRedirection": {
|
||||
"description": "A source redirection for mocked module.",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"source"
|
||||
],
|
||||
"properties": {
|
||||
"source": {
|
||||
"type": "string",
|
||||
"maxLength": 128,
|
||||
"minLength": 1
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"patternProperties": {
|
||||
".+": {
|
||||
"$ref": "#/definitions/sourceRedirection"
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user