diff --git a/AppScope/app.json5 b/AppScope/app.json5 new file mode 100644 index 0000000..a32449f --- /dev/null +++ b/AppScope/app.json5 @@ -0,0 +1,10 @@ +{ + "app": { + "bundleName": "com.example.myapplication", + "vendor": "example", + "versionCode": 1000000, + "versionName": "1.0.0", + "icon": "$media:layered_image", + "label": "$string:app_name" + } +} diff --git a/AppScope/resources/base/element/string.json b/AppScope/resources/base/element/string.json new file mode 100644 index 0000000..d71e5ea --- /dev/null +++ b/AppScope/resources/base/element/string.json @@ -0,0 +1,8 @@ +{ + "string": [ + { + "name": "app_name", + "value": "MyApplication" + } + ] +} diff --git a/AppScope/resources/base/media/background.png b/AppScope/resources/base/media/background.png new file mode 100644 index 0000000..923f2b3 Binary files /dev/null and b/AppScope/resources/base/media/background.png differ diff --git a/AppScope/resources/base/media/foreground.png b/AppScope/resources/base/media/foreground.png new file mode 100644 index 0000000..97014d3 Binary files /dev/null and b/AppScope/resources/base/media/foreground.png differ diff --git a/AppScope/resources/base/media/layered_image.json b/AppScope/resources/base/media/layered_image.json new file mode 100644 index 0000000..fb49920 --- /dev/null +++ b/AppScope/resources/base/media/layered_image.json @@ -0,0 +1,7 @@ +{ + "layered-image": + { + "background" : "$media:background", + "foreground" : "$media:foreground" + } +} \ No newline at end of file diff --git a/build-profile.json5 b/build-profile.json5 new file mode 100644 index 0000000..67df524 --- /dev/null +++ b/build-profile.json5 @@ -0,0 +1,41 @@ +{ + "app": { + "signingConfigs": [], + "products": [ + { + "name": "default", + "signingConfig": "default", + "compatibleSdkVersion": "5.0.4(16)", + "runtimeOS": "HarmonyOS", + "buildOption": { + "strictMode": { + "caseSensitiveCheck": true, + "useNormalizedOHMUrl": true + } + } + } + ], + "buildModeSet": [ + { + "name": "debug", + }, + { + "name": "release" + } + ] + }, + "modules": [ + { + "name": "entry", + "srcPath": "./entry", + "targets": [ + { + "name": "default", + "applyToProducts": [ + "default" + ] + } + ] + } + ] +} \ No newline at end of file diff --git a/code-linter.json5 b/code-linter.json5 new file mode 100644 index 0000000..073990f --- /dev/null +++ b/code-linter.json5 @@ -0,0 +1,32 @@ +{ + "files": [ + "**/*.ets" + ], + "ignore": [ + "**/src/ohosTest/**/*", + "**/src/test/**/*", + "**/src/mock/**/*", + "**/node_modules/**/*", + "**/oh_modules/**/*", + "**/build/**/*", + "**/.preview/**/*" + ], + "ruleSet": [ + "plugin:@performance/recommended", + "plugin:@typescript-eslint/recommended" + ], + "rules": { + "@security/no-unsafe-aes": "error", + "@security/no-unsafe-hash": "error", + "@security/no-unsafe-mac": "warn", + "@security/no-unsafe-dh": "error", + "@security/no-unsafe-dsa": "error", + "@security/no-unsafe-ecdsa": "error", + "@security/no-unsafe-rsa-encrypt": "error", + "@security/no-unsafe-rsa-sign": "error", + "@security/no-unsafe-rsa-key": "error", + "@security/no-unsafe-dsa-key": "error", + "@security/no-unsafe-dh-key": "error", + "@security/no-unsafe-3des": "error" + } +} \ No newline at end of file diff --git a/entry/build-profile.json5 b/entry/build-profile.json5 new file mode 100644 index 0000000..dfb34f7 --- /dev/null +++ b/entry/build-profile.json5 @@ -0,0 +1,28 @@ +{ + "apiType": "stageMode", + "buildOption": { + }, + "buildOptionSet": [ + { + "name": "release", + "arkOptions": { + "obfuscation": { + "ruleOptions": { + "enable": false, + "files": [ + "./obfuscation-rules.txt" + ] + } + } + } + }, + ], + "targets": [ + { + "name": "default" + }, + { + "name": "ohosTest", + } + ] +} \ No newline at end of file diff --git a/entry/hvigorfile.ts b/entry/hvigorfile.ts new file mode 100644 index 0000000..c6edcd9 --- /dev/null +++ b/entry/hvigorfile.ts @@ -0,0 +1,6 @@ +import { hapTasks } from '@ohos/hvigor-ohos-plugin'; + +export default { + system: hapTasks, /* Built-in plugin of Hvigor. It cannot be modified. */ + plugins:[] /* Custom plugin to extend the functionality of Hvigor. */ +} diff --git a/entry/obfuscation-rules.txt b/entry/obfuscation-rules.txt new file mode 100644 index 0000000..272efb6 --- /dev/null +++ b/entry/obfuscation-rules.txt @@ -0,0 +1,23 @@ +# Define project specific obfuscation rules here. +# You can include the obfuscation configuration files in the current module's build-profile.json5. +# +# For more details, see +# https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V5/source-obfuscation-V5 + +# Obfuscation options: +# -disable-obfuscation: disable all obfuscations +# -enable-property-obfuscation: obfuscate the property names +# -enable-toplevel-obfuscation: obfuscate the names in the global scope +# -compact: remove unnecessary blank spaces and all line feeds +# -remove-log: remove all console.* statements +# -print-namecache: print the name cache that contains the mapping from the old names to new names +# -apply-namecache: reuse the given cache file + +# Keep options: +# -keep-property-name: specifies property names that you want to keep +# -keep-global-name: specifies names that you want to keep in the global scope + +-enable-property-obfuscation +-enable-toplevel-obfuscation +-enable-filename-obfuscation +-enable-export-obfuscation \ No newline at end of file diff --git a/entry/oh-package.json5 b/entry/oh-package.json5 new file mode 100644 index 0000000..2959d78 --- /dev/null +++ b/entry/oh-package.json5 @@ -0,0 +1,10 @@ +{ + "name": "entry", + "version": "1.0.0", + "description": "Please describe the basic information.", + "main": "", + "author": "", + "license": "", + "dependencies": {} +} + diff --git a/entry/src/main/ets/entryability/EntryAbility.ets b/entry/src/main/ets/entryability/EntryAbility.ets new file mode 100644 index 0000000..b12b706 --- /dev/null +++ b/entry/src/main/ets/entryability/EntryAbility.ets @@ -0,0 +1,44 @@ +import { AbilityConstant, ConfigurationConstant, UIAbility, Want } from '@kit.AbilityKit'; +import { hilog } from '@kit.PerformanceAnalysisKit'; +import { window } from '@kit.ArkUI'; + +const DOMAIN = 0x0000; + +export default class EntryAbility extends UIAbility { + onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void { + this.context.getApplicationContext().setColorMode(ConfigurationConstant.ColorMode.COLOR_MODE_NOT_SET); + hilog.info(DOMAIN, 'testTag', '%{public}s', 'Ability onCreate'); + } + + onDestroy(): void { + hilog.info(DOMAIN, 'testTag', '%{public}s', 'Ability onDestroy'); + } + + onWindowStageCreate(windowStage: window.WindowStage): void { + // Main window is created, set main page for this ability + hilog.info(DOMAIN, 'testTag', '%{public}s', 'Ability onWindowStageCreate'); + + windowStage.loadContent('pages/Index', (err) => { + if (err.code) { + hilog.error(DOMAIN, 'testTag', 'Failed to load the content. Cause: %{public}s', JSON.stringify(err)); + return; + } + hilog.info(DOMAIN, 'testTag', 'Succeeded in loading the content.'); + }); + } + + onWindowStageDestroy(): void { + // Main window is destroyed, release UI related resources + hilog.info(DOMAIN, 'testTag', '%{public}s', 'Ability onWindowStageDestroy'); + } + + onForeground(): void { + // Ability has brought to foreground + hilog.info(DOMAIN, 'testTag', '%{public}s', 'Ability onForeground'); + } + + onBackground(): void { + // Ability has back to background + hilog.info(DOMAIN, 'testTag', '%{public}s', 'Ability onBackground'); + } +} \ No newline at end of file diff --git a/entry/src/main/ets/entrybackupability/EntryBackupAbility.ets b/entry/src/main/ets/entrybackupability/EntryBackupAbility.ets new file mode 100644 index 0000000..fae19f9 --- /dev/null +++ b/entry/src/main/ets/entrybackupability/EntryBackupAbility.ets @@ -0,0 +1,16 @@ +import { hilog } from '@kit.PerformanceAnalysisKit'; +import { BackupExtensionAbility, BundleVersion } from '@kit.CoreFileKit'; + +const DOMAIN = 0x0000; + +export default class EntryBackupAbility extends BackupExtensionAbility { + async onBackup() { + hilog.info(DOMAIN, 'testTag', 'onBackup ok'); + await Promise.resolve(); + } + + async onRestore(bundleVersion: BundleVersion) { + hilog.info(DOMAIN, 'testTag', 'onRestore ok %{public}s', JSON.stringify(bundleVersion)); + await Promise.resolve(); + } +} \ No newline at end of file diff --git a/entry/src/main/ets/pages/Index.ets b/entry/src/main/ets/pages/Index.ets new file mode 100644 index 0000000..458651d --- /dev/null +++ b/entry/src/main/ets/pages/Index.ets @@ -0,0 +1,82 @@ +import router from '@ohos.router'; +import Home from '../pages/home/Home' +import My from './my/My' +import Message from '../pages/message/Message' +import Profile from '../pages/profile/profile' +@Entry +@Component +struct Index { + @State currentIndex: number = 0; + + build() { + Stack({ alignContent: Alignment.Bottom }) { + Column() { + Tabs({ barPosition: BarPosition.End }) { + TabContent() { + Navigation() { + Home() + } + } + .tabBar(this.TabBuilder('首页', 0, $r('app.media.ys'), $r('app.media.s'))) + + TabContent() { + Navigation() { + My() + } + } + .tabBar(this.TabBuilder('生活圈', 1, $r('app.media.y'), $r('app.media.yy'))) + + TabContent() { + Navigation() { + Message() + } + } + .tabBar(this.TabBuilder('消息', 2, $r('app.media.x'), $r('app.media.yx'))) + + TabContent() { + Navigation() { + Profile() + } + } + .tabBar(this.TabBuilder('我的', 3, $r('app.media.w'), $r('app.media.wy'))) + } + .barHeight(56) + .backgroundColor('#FFFFFF') + .barMode(BarMode.Fixed) + .onChange((index: number) => { + this.currentIndex = index + }) + } + + Button() { + Image($r('app.media.j')) + .width(24) + .height(24) + } + .width(48) + .height(48) + .position({x: '50%', y: '92%'}) + .translate({x: -24}) + .backgroundColor('#7B48F8') + .borderRadius(24) + .onClick(() => { + // 处理加号按钮点击事件 + }) + } + } + + @Builder TabBuilder(title: string, index: number, normalImg: Resource, selectedImg: Resource) { + Column() { + Image(this.currentIndex === index ? selectedImg : normalImg) + .width(24) + .height(24) + Text(title) + .fontSize(12) + .fontColor(this.currentIndex === index ? '#7B48F8' : '#999999') + .margin({ top: 4 }) + } + .width('100%') + .height('100%') + .justifyContent(FlexAlign.Center) + } +} \ No newline at end of file diff --git a/entry/src/main/ets/pages/home/Home.ets b/entry/src/main/ets/pages/home/Home.ets new file mode 100644 index 0000000..1ebd13e --- /dev/null +++ b/entry/src/main/ets/pages/home/Home.ets @@ -0,0 +1,12 @@ + + +@Component +export default struct Home { + + + build() { + Column(){ + Text('首页') + } + } +} \ No newline at end of file diff --git a/entry/src/main/ets/pages/message/Message.ets b/entry/src/main/ets/pages/message/Message.ets new file mode 100644 index 0000000..f18991e --- /dev/null +++ b/entry/src/main/ets/pages/message/Message.ets @@ -0,0 +1,13 @@ +@Component +export default struct Message { + build() { + Column() { + Text('消息') + .fontSize(20) + .fontWeight(FontWeight.Bold) + } + .width('100%') + .height('100%') + .backgroundColor('#F1F3F5') + } +} \ No newline at end of file diff --git a/entry/src/main/ets/pages/my/My.ets b/entry/src/main/ets/pages/my/My.ets new file mode 100644 index 0000000..1800aa3 --- /dev/null +++ b/entry/src/main/ets/pages/my/My.ets @@ -0,0 +1,13 @@ +@Component +export default struct My { + build() { + Column() { + Text('生活圈') + .fontSize(20) + .fontWeight(FontWeight.Bold) + } + .width('100%') + .height('100%') + .backgroundColor('#F1F3F5') + } +} \ No newline at end of file diff --git a/entry/src/main/ets/pages/profile/profile.ets b/entry/src/main/ets/pages/profile/profile.ets new file mode 100644 index 0000000..843ebc2 --- /dev/null +++ b/entry/src/main/ets/pages/profile/profile.ets @@ -0,0 +1,13 @@ +@Component +export default struct Profile { + build() { + Column() { + Text('我的') + .fontSize(20) + .fontWeight(FontWeight.Bold) + } + .width('100%') + .height('100%') + .backgroundColor('#F1F3F5') + } +} \ No newline at end of file diff --git a/entry/src/main/module.json5 b/entry/src/main/module.json5 new file mode 100644 index 0000000..1cf10bc --- /dev/null +++ b/entry/src/main/module.json5 @@ -0,0 +1,52 @@ +{ + "module": { + "name": "entry", + "type": "entry", + "description": "$string:module_desc", + "mainElement": "EntryAbility", + "deviceTypes": [ + "phone", + "tablet", + "2in1" + ], + "deliveryWithInstall": true, + "installationFree": false, + "pages": "$profile:main_pages", + "abilities": [ + { + "name": "EntryAbility", + "srcEntry": "./ets/entryability/EntryAbility.ets", + "description": "$string:EntryAbility_desc", + "icon": "$media:layered_image", + "label": "$string:EntryAbility_label", + "startWindowIcon": "$media:startIcon", + "startWindowBackground": "$color:start_window_background", + "exported": true, + "skills": [ + { + "entities": [ + "entity.system.home" + ], + "actions": [ + "action.system.home" + ] + } + ] + } + ], + "extensionAbilities": [ + { + "name": "EntryBackupAbility", + "srcEntry": "./ets/entrybackupability/EntryBackupAbility.ets", + "type": "backup", + "exported": false, + "metadata": [ + { + "name": "ohos.extension.backup", + "resource": "$profile:backup_config" + } + ], + } + ] + } +} \ No newline at end of file diff --git a/entry/src/main/resources/base/element/color.json b/entry/src/main/resources/base/element/color.json new file mode 100644 index 0000000..d66f9a7 --- /dev/null +++ b/entry/src/main/resources/base/element/color.json @@ -0,0 +1,8 @@ +{ + "color": [ + { + "name": "start_window_background", + "value": "#FFFFFF" + } + ] +} \ No newline at end of file diff --git a/entry/src/main/resources/base/element/float.json b/entry/src/main/resources/base/element/float.json new file mode 100644 index 0000000..a8a5d40 --- /dev/null +++ b/entry/src/main/resources/base/element/float.json @@ -0,0 +1,8 @@ +{ + "float": [ + { + "name": "page_text_font_size", + "value": "50fp" + } + ] +} diff --git a/entry/src/main/resources/base/element/string.json b/entry/src/main/resources/base/element/string.json new file mode 100644 index 0000000..f945955 --- /dev/null +++ b/entry/src/main/resources/base/element/string.json @@ -0,0 +1,16 @@ +{ + "string": [ + { + "name": "module_desc", + "value": "module description" + }, + { + "name": "EntryAbility_desc", + "value": "description" + }, + { + "name": "EntryAbility_label", + "value": "label" + } + ] +} \ No newline at end of file diff --git a/entry/src/main/resources/base/media/background.png b/entry/src/main/resources/base/media/background.png new file mode 100644 index 0000000..923f2b3 Binary files /dev/null and b/entry/src/main/resources/base/media/background.png differ diff --git a/entry/src/main/resources/base/media/foreground.png b/entry/src/main/resources/base/media/foreground.png new file mode 100644 index 0000000..97014d3 Binary files /dev/null and b/entry/src/main/resources/base/media/foreground.png differ diff --git a/entry/src/main/resources/base/media/j.png b/entry/src/main/resources/base/media/j.png new file mode 100644 index 0000000..163c748 Binary files /dev/null and b/entry/src/main/resources/base/media/j.png differ diff --git a/entry/src/main/resources/base/media/layered_image.json b/entry/src/main/resources/base/media/layered_image.json new file mode 100644 index 0000000..fb49920 --- /dev/null +++ b/entry/src/main/resources/base/media/layered_image.json @@ -0,0 +1,7 @@ +{ + "layered-image": + { + "background" : "$media:background", + "foreground" : "$media:foreground" + } +} \ No newline at end of file diff --git a/entry/src/main/resources/base/media/s.png b/entry/src/main/resources/base/media/s.png new file mode 100644 index 0000000..ce4bd5c Binary files /dev/null and b/entry/src/main/resources/base/media/s.png differ diff --git a/entry/src/main/resources/base/media/startIcon.png b/entry/src/main/resources/base/media/startIcon.png new file mode 100644 index 0000000..205ad8b Binary files /dev/null and b/entry/src/main/resources/base/media/startIcon.png differ diff --git a/entry/src/main/resources/base/media/w.png b/entry/src/main/resources/base/media/w.png new file mode 100644 index 0000000..e57527f Binary files /dev/null and b/entry/src/main/resources/base/media/w.png differ diff --git a/entry/src/main/resources/base/media/wy.png b/entry/src/main/resources/base/media/wy.png new file mode 100644 index 0000000..099ab0c Binary files /dev/null and b/entry/src/main/resources/base/media/wy.png differ diff --git a/entry/src/main/resources/base/media/x.png b/entry/src/main/resources/base/media/x.png new file mode 100644 index 0000000..c2437d7 Binary files /dev/null and b/entry/src/main/resources/base/media/x.png differ diff --git a/entry/src/main/resources/base/media/y.png b/entry/src/main/resources/base/media/y.png new file mode 100644 index 0000000..0cb38ee Binary files /dev/null and b/entry/src/main/resources/base/media/y.png differ diff --git a/entry/src/main/resources/base/media/ys.png b/entry/src/main/resources/base/media/ys.png new file mode 100644 index 0000000..ea74184 Binary files /dev/null and b/entry/src/main/resources/base/media/ys.png differ diff --git a/entry/src/main/resources/base/media/yx.png b/entry/src/main/resources/base/media/yx.png new file mode 100644 index 0000000..c11f946 Binary files /dev/null and b/entry/src/main/resources/base/media/yx.png differ diff --git a/entry/src/main/resources/base/media/yy.png b/entry/src/main/resources/base/media/yy.png new file mode 100644 index 0000000..9bc75b0 Binary files /dev/null and b/entry/src/main/resources/base/media/yy.png differ diff --git a/entry/src/main/resources/base/profile/backup_config.json b/entry/src/main/resources/base/profile/backup_config.json new file mode 100644 index 0000000..d742c2f --- /dev/null +++ b/entry/src/main/resources/base/profile/backup_config.json @@ -0,0 +1,3 @@ +{ + "allowToBackupRestore": true +} \ No newline at end of file diff --git a/entry/src/main/resources/base/profile/main_pages.json b/entry/src/main/resources/base/profile/main_pages.json new file mode 100644 index 0000000..1898d94 --- /dev/null +++ b/entry/src/main/resources/base/profile/main_pages.json @@ -0,0 +1,5 @@ +{ + "src": [ + "pages/Index" + ] +} diff --git a/entry/src/main/resources/dark/element/color.json b/entry/src/main/resources/dark/element/color.json new file mode 100644 index 0000000..438d5bc --- /dev/null +++ b/entry/src/main/resources/dark/element/color.json @@ -0,0 +1,8 @@ +{ + "color": [ + { + "name": "start_window_background", + "value": "#000000" + } + ] +} \ No newline at end of file diff --git a/entry/src/mock/mock-config.json5 b/entry/src/mock/mock-config.json5 new file mode 100644 index 0000000..7a73a41 --- /dev/null +++ b/entry/src/mock/mock-config.json5 @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/entry/src/ohosTest/ets/test/Ability.test.ets b/entry/src/ohosTest/ets/test/Ability.test.ets new file mode 100644 index 0000000..85c78f6 --- /dev/null +++ b/entry/src/ohosTest/ets/test/Ability.test.ets @@ -0,0 +1,35 @@ +import { hilog } from '@kit.PerformanceAnalysisKit'; +import { describe, beforeAll, beforeEach, afterEach, afterAll, it, expect } from '@ohos/hypium'; + +export default function abilityTest() { + describe('ActsAbilityTest', () => { + // Defines a test suite. Two parameters are supported: test suite name and test suite function. + beforeAll(() => { + // Presets an action, which is performed only once before all test cases of the test suite start. + // This API supports only one parameter: preset action function. + }) + beforeEach(() => { + // Presets an action, which is performed before each unit test case starts. + // The number of execution times is the same as the number of test cases defined by **it**. + // This API supports only one parameter: preset action function. + }) + afterEach(() => { + // Presets a clear action, which is performed after each unit test case ends. + // The number of execution times is the same as the number of test cases defined by **it**. + // This API supports only one parameter: clear action function. + }) + afterAll(() => { + // Presets a clear action, which is performed after all test cases of the test suite end. + // This API supports only one parameter: clear action function. + }) + it('assertContain', 0, () => { + // Defines a test case. This API supports three parameters: test case name, filter parameter, and test case function. + hilog.info(0x0000, 'testTag', '%{public}s', 'it begin'); + let a = 'abc'; + let b = 'b'; + // Defines a variety of assertion methods, which are used to declare expected boolean conditions. + expect(a).assertContain(b); + expect(a).assertEqual(a); + }) + }) +} \ No newline at end of file diff --git a/entry/src/ohosTest/ets/test/List.test.ets b/entry/src/ohosTest/ets/test/List.test.ets new file mode 100644 index 0000000..794c7dc --- /dev/null +++ b/entry/src/ohosTest/ets/test/List.test.ets @@ -0,0 +1,5 @@ +import abilityTest from './Ability.test'; + +export default function testsuite() { + abilityTest(); +} \ No newline at end of file diff --git a/entry/src/ohosTest/module.json5 b/entry/src/ohosTest/module.json5 new file mode 100644 index 0000000..ae666e1 --- /dev/null +++ b/entry/src/ohosTest/module.json5 @@ -0,0 +1,13 @@ +{ + "module": { + "name": "entry_test", + "type": "feature", + "deviceTypes": [ + "phone", + "tablet", + "2in1" + ], + "deliveryWithInstall": true, + "installationFree": false + } +} diff --git a/entry/src/test/List.test.ets b/entry/src/test/List.test.ets new file mode 100644 index 0000000..bb5b5c3 --- /dev/null +++ b/entry/src/test/List.test.ets @@ -0,0 +1,5 @@ +import localUnitTest from './LocalUnit.test'; + +export default function testsuite() { + localUnitTest(); +} \ No newline at end of file diff --git a/hvigor/hvigor-config.json5 b/hvigor/hvigor-config.json5 new file mode 100644 index 0000000..de3bba9 --- /dev/null +++ b/hvigor/hvigor-config.json5 @@ -0,0 +1,22 @@ +{ + "modelVersion": "5.0.4", + "dependencies": { + }, + "execution": { + // "analyze": "normal", /* Define the build analyze mode. Value: [ "normal" | "advanced" | false ]. Default: "normal" */ + // "daemon": true, /* Enable daemon compilation. Value: [ true | false ]. Default: true */ + // "incremental": true, /* Enable incremental compilation. Value: [ true | false ]. Default: true */ + // "parallel": true, /* Enable parallel compilation. Value: [ true | false ]. Default: true */ + // "typeCheck": false, /* Enable typeCheck. Value: [ true | false ]. Default: false */ + }, + "logging": { + // "level": "info" /* Define the log level. Value: [ "debug" | "info" | "warn" | "error" ]. Default: "info" */ + }, + "debugging": { + // "stacktrace": false /* Disable stacktrace compilation. Value: [ true | false ]. Default: false */ + }, + "nodeOptions": { + // "maxOldSpaceSize": 8192 /* Enable nodeOptions maxOldSpaceSize compilation. Unit M. Used for the daemon process. Default: 8192*/ + // "exposeGC": true /* Enable to trigger garbage collection explicitly. Default: true*/ + } +} diff --git a/hvigorfile.ts b/hvigorfile.ts new file mode 100644 index 0000000..f3cb9f1 --- /dev/null +++ b/hvigorfile.ts @@ -0,0 +1,6 @@ +import { appTasks } from '@ohos/hvigor-ohos-plugin'; + +export default { + system: appTasks, /* Built-in plugin of Hvigor. It cannot be modified. */ + plugins:[] /* Custom plugin to extend the functionality of Hvigor. */ +} diff --git a/local.properties b/local.properties new file mode 100644 index 0000000..e45ae57 --- /dev/null +++ b/local.properties @@ -0,0 +1,9 @@ +# This file is automatically generated by DevEco Studio. +# Do not modify this file -- YOUR CHANGES WILL BE ERASED! +# +# This file should *NOT* be checked into Version Control Systems, +# as it contains information specific to your local configuration. +# +# For customization when using a Version Control System, please read the header note. + + diff --git a/oh-package-lock.json5 b/oh-package-lock.json5 new file mode 100644 index 0000000..e66b2da --- /dev/null +++ b/oh-package-lock.json5 @@ -0,0 +1,27 @@ +{ + "meta": { + "stableOrder": true + }, + "lockfileVersion": 3, + "ATTENTION": "THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.", + "specifiers": { + "@ohos/hamock@1.0.0": "@ohos/hamock@1.0.0", + "@ohos/hypium@1.0.21": "@ohos/hypium@1.0.21" + }, + "packages": { + "@ohos/hamock@1.0.0": { + "name": "@ohos/hamock", + "version": "1.0.0", + "integrity": "sha512-K6lDPYc6VkKe6ZBNQa9aoG+ZZMiwqfcR/7yAVFSUGIuOAhPvCJAo9+t1fZnpe0dBRBPxj2bxPPbKh69VuyAtDg==", + "resolved": "https://ohpm.openharmony.cn/ohpm/@ohos/hamock/-/hamock-1.0.0.har", + "registryType": "ohpm" + }, + "@ohos/hypium@1.0.21": { + "name": "@ohos/hypium", + "version": "1.0.21", + "integrity": "sha512-iyKGMXxE+9PpCkqEwu0VykN/7hNpb+QOeIuHwkmZnxOpI+dFZt6yhPB7k89EgV1MiSK/ieV/hMjr5Z2mWwRfMQ==", + "resolved": "https://ohpm.openharmony.cn/ohpm/@ohos/hypium/-/hypium-1.0.21.har", + "registryType": "ohpm" + } + } +} \ No newline at end of file diff --git a/oh-package.json5 b/oh-package.json5 new file mode 100644 index 0000000..6eb6639 --- /dev/null +++ b/oh-package.json5 @@ -0,0 +1,10 @@ +{ + "modelVersion": "5.0.4", + "description": "Please describe the basic information.", + "dependencies": { + }, + "devDependencies": { + "@ohos/hypium": "1.0.21", + "@ohos/hamock": "1.0.0" + } +} diff --git a/oh_modules/@ohos/hamock/CHANGELOG.md b/oh_modules/@ohos/hamock/CHANGELOG.md new file mode 100644 index 0000000..f62e2be --- /dev/null +++ b/oh_modules/@ohos/hamock/CHANGELOG.md @@ -0,0 +1,4 @@ +## 1.0.0 +- 修复once断言问题 +## 1.0.0-rc +- 提供DevEco Studio预览器场景使能的MockSetup装饰器 \ No newline at end of file diff --git a/oh_modules/@ohos/hamock/LICENSE b/oh_modules/@ohos/hamock/LICENSE new file mode 100644 index 0000000..4a45986 --- /dev/null +++ b/oh_modules/@ohos/hamock/LICENSE @@ -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 \ No newline at end of file diff --git a/oh_modules/@ohos/hamock/README.md b/oh_modules/@ohos/hamock/README.md new file mode 100644 index 0000000..ea667a1 --- /dev/null +++ b/oh_modules/@ohos/hamock/README.md @@ -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) ,请自由地享受和参与开源。 \ No newline at end of file diff --git a/oh_modules/@ohos/hamock/build-profile.json5 b/oh_modules/@ohos/hamock/build-profile.json5 new file mode 100644 index 0000000..cf61dec --- /dev/null +++ b/oh_modules/@ohos/hamock/build-profile.json5 @@ -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" + } + ] +} diff --git a/oh_modules/@ohos/hamock/hvigorfile.js b/oh_modules/@ohos/hamock/hvigorfile.js new file mode 100644 index 0000000..76915cf --- /dev/null +++ b/oh_modules/@ohos/hamock/hvigorfile.js @@ -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'; diff --git a/oh_modules/@ohos/hamock/hvigorfile.ts b/oh_modules/@ohos/hamock/hvigorfile.ts new file mode 100644 index 0000000..ba30640 --- /dev/null +++ b/oh_modules/@ohos/hamock/hvigorfile.ts @@ -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'; \ No newline at end of file diff --git a/oh_modules/@ohos/hamock/index.d.ts b/oh_modules/@ohos/hamock/index.d.ts new file mode 100644 index 0000000..d6179ad --- /dev/null +++ b/oh_modules/@ohos/hamock/index.d.ts @@ -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): 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; \ No newline at end of file diff --git a/oh_modules/@ohos/hamock/index.ets b/oh_modules/@ohos/hamock/index.ets new file mode 100644 index 0000000..3e4ab4c --- /dev/null +++ b/oh_modules/@ohos/hamock/index.ets @@ -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'; \ No newline at end of file diff --git a/oh_modules/@ohos/hamock/index.js b/oh_modules/@ohos/hamock/index.js new file mode 100644 index 0000000..b3832e8 --- /dev/null +++ b/oh_modules/@ohos/hamock/index.js @@ -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'; diff --git a/oh_modules/@ohos/hamock/index.ts b/oh_modules/@ohos/hamock/index.ts new file mode 100644 index 0000000..df0e313 --- /dev/null +++ b/oh_modules/@ohos/hamock/index.ts @@ -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'; \ No newline at end of file diff --git a/oh_modules/@ohos/hamock/oh-package.json5 b/oh_modules/@ohos/hamock/oh-package.json5 new file mode 100644 index 0000000..49f3683 --- /dev/null +++ b/oh_modules/@ohos/hamock/oh-package.json5 @@ -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' +} diff --git a/oh_modules/@ohos/hamock/src/main/mock/ArgumentMatchers.js b/oh_modules/@ohos/hamock/src/main/mock/ArgumentMatchers.js new file mode 100644 index 0000000..610c540 --- /dev/null +++ b/oh_modules/@ohos/hamock/src/main/mock/ArgumentMatchers.js @@ -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 = ""; + this.ANY_STRING = ""; + this.ANY_BOOLEAN = ""; + this.ANY_NUMBER = ""; + this.ANY_OBJECT = ""; + this.ANY_FUNCTION = ""; + this.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; + } +} diff --git a/oh_modules/@ohos/hamock/src/main/mock/ArgumentMatchers.ts b/oh_modules/@ohos/hamock/src/main/mock/ArgumentMatchers.ts new file mode 100644 index 0000000..0e0e64c --- /dev/null +++ b/oh_modules/@ohos/hamock/src/main/mock/ArgumentMatchers.ts @@ -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_STRING = ""; + ANY_BOOLEAN = ""; + ANY_NUMBER = ""; + ANY_OBJECT = ""; + ANY_FUNCTION = ""; + 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) { + 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; + } +} \ No newline at end of file diff --git a/oh_modules/@ohos/hamock/src/main/mock/ExtendInterface.js b/oh_modules/@ohos/hamock/src/main/mock/ExtendInterface.js new file mode 100644 index 0000000..325be96 --- /dev/null +++ b/oh_modules/@ohos/hamock/src/main/mock/ExtendInterface.js @@ -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; diff --git a/oh_modules/@ohos/hamock/src/main/mock/ExtendInterface.ts b/oh_modules/@ohos/hamock/src/main/mock/ExtendInterface.ts new file mode 100644 index 0000000..8e27e2e --- /dev/null +++ b/oh_modules/@ohos/hamock/src/main/mock/ExtendInterface.ts @@ -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; \ No newline at end of file diff --git a/oh_modules/@ohos/hamock/src/main/mock/MockKit.js b/oh_modules/@ohos/hamock/src/main/mock/MockKit.js new file mode 100644 index 0000000..01a4bf3 --- /dev/null +++ b/oh_modules/@ohos/hamock/src/main/mock/MockKit.js @@ -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 }; diff --git a/oh_modules/@ohos/hamock/src/main/mock/MockKit.ts b/oh_modules/@ohos/hamock/src/main/mock/MockKit.ts new file mode 100644 index 0000000..a9f965a --- /dev/null +++ b/oh_modules/@ohos/hamock/src/main/mock/MockKit.ts @@ -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 = []; + 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) { + function getProperty(new_obj:any): Array { + 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 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; + 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 +}; \ No newline at end of file diff --git a/oh_modules/@ohos/hamock/src/main/mock/VerificationMode.js b/oh_modules/@ohos/hamock/src/main/mock/VerificationMode.js new file mode 100644 index 0000000..02fbf3b --- /dev/null +++ b/oh_modules/@ohos/hamock/src/main/mock/VerificationMode.js @@ -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; diff --git a/oh_modules/@ohos/hamock/src/main/mock/VerificationMode.ts b/oh_modules/@ohos/hamock/src/main/mock/VerificationMode.ts new file mode 100644 index 0000000..0748125 --- /dev/null +++ b/oh_modules/@ohos/hamock/src/main/mock/VerificationMode.ts @@ -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; \ No newline at end of file diff --git a/oh_modules/@ohos/hamock/src/main/module.json b/oh_modules/@ohos/hamock/src/main/module.json new file mode 100644 index 0000000..384ae72 --- /dev/null +++ b/oh_modules/@ohos/hamock/src/main/module.json @@ -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" + ] + } +} diff --git a/oh_modules/@ohos/hamock/src/res/schemas/mock-config-json5-schema.json b/oh_modules/@ohos/hamock/src/res/schemas/mock-config-json5-schema.json new file mode 100644 index 0000000..b137b0a --- /dev/null +++ b/oh_modules/@ohos/hamock/src/res/schemas/mock-config-json5-schema.json @@ -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" + } + } +} \ No newline at end of file diff --git a/oh_modules/@ohos/hypium/BuildProfile.ets b/oh_modules/@ohos/hypium/BuildProfile.ets new file mode 100644 index 0000000..07a9435 --- /dev/null +++ b/oh_modules/@ohos/hypium/BuildProfile.ets @@ -0,0 +1,17 @@ +/** + * Use these variables when you tailor your ArkTS code. They must be of the const type. + */ +export const HAR_VERSION = '1.0.21'; +export const BUILD_MODE_NAME = 'debug'; +export const DEBUG = true; +export const TARGET_NAME = 'default'; + +/** + * BuildProfile Class is used only for compatibility purposes. + */ +export default class BuildProfile { + static readonly HAR_VERSION = HAR_VERSION; + static readonly BUILD_MODE_NAME = BUILD_MODE_NAME; + static readonly DEBUG = DEBUG; + static readonly TARGET_NAME = TARGET_NAME; +} \ No newline at end of file diff --git a/oh_modules/@ohos/hypium/CHANGELOG.md b/oh_modules/@ohos/hypium/CHANGELOG.md new file mode 100644 index 0000000..6b8c61a --- /dev/null +++ b/oh_modules/@ohos/hypium/CHANGELOG.md @@ -0,0 +1,27 @@ +## 1.0.21 +- mock支持多参数 +- describe中异步函数抛出日志信息 +- 修复多测试套时,执行单个测试套会打印其他测试套的日志信息 +## 1.0.14 +- 堆栈信息打印到cmd +## 1.0.15 +- 支持获取测试代码的失败堆栈信息 +- mock代码迁移至harmock包 +- 适配arkts语法 +- 修复覆盖率数据容易截断的bug +## 1.0.16 +- 修改覆盖率文件生成功能 +- 修改静态方法无法ignoreMock函数 +- ## 1.0.17 +- 修改not断言失败提示日志 +- 自定义错误message信息 +- 添加xdescribe, xit API功能 +- ## 1.0.18 +- 添加全局变量存储API get set +- 自定义断言功能 +## 1.0.18-rc.0 +添加框架worker执行能力 +## 1.0.19 +规范日志格式 +# 1.0.20 +代码告警整改 \ No newline at end of file diff --git a/oh_modules/@ohos/hypium/LICENSE b/oh_modules/@ohos/hypium/LICENSE new file mode 100644 index 0000000..4947287 --- /dev/null +++ b/oh_modules/@ohos/hypium/LICENSE @@ -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 \ No newline at end of file diff --git a/oh_modules/@ohos/hypium/README.md b/oh_modules/@ohos/hypium/README.md new file mode 100644 index 0000000..93997dc --- /dev/null +++ b/oh_modules/@ohos/hypium/README.md @@ -0,0 +1,224 @@ +
Hypium
+
A unit test framework for OpenHarmonyOS application
+ +## Hypium是什么? +*** +- Hypium是OpenHarmony上的测试框架,提供测试用例编写、执行、结果显示能力,用于OpenHarmony系统应用接口以及应用界面测试。 +- Hypium结构化模型:hypium工程主要由List.test.js与TestCase.test.js组成。 +``` +rootProject // Hypium工程根目录 +├── moduleA +│   ├── src +│      ├── main // 被测试应用目录 +│      ├── ohosTest // 测试用例目录 +│         ├── js/ets +│            └── test +│               └── List.test.js // 测试用例加载脚本,ets目录下为.ets后缀 +│               └── TestCase.test.js // 测试用例脚本,ets目录下为.ets后缀 +└── moduleB + ... +│               └── List.test.js // 测试用例加载脚本,ets目录下为.ets后缀 +│               └── TestCase.test.js // 测试用例脚本,ets目录下为.ets后缀 +``` + +## 安装使用 + +```javascript +ohpm install @ohos/hypium +``` + +*** +- 在DevEco Studio内使用Hypium +- 工程级package.json内配置: +```json +"dependencies": { + "@ohos/hypium": "1.0.21" +} +``` +注: +hypium服务于OpenHarmonyOS应用对外接口测试、系统对外接口测试(SDK中接口),完成HAP自动化测试。详细指导: +[Deveco Studio](https://developer.harmonyos.com/cn/develop/deveco-studio) + +#### 通用语法 + +- 测试用例采用业内通用语法,describe代表一个测试套, it代表一条用例。 + +| No. | API | 功能说明 | +| --- | ---------- | ---------------------------------------------------------------------------------------------------------------------- | +| 1 | describe | 定义一个测试套,支持两个参数:测试套名称和测试套函数 | +| 2 | beforeAll | 在测试套内定义一个预置条件,在所有测试用例开始前执行且仅执行一次,支持一个参数:预置动作函数 | +| 3 | beforeEach | 在测试套内定义一个单元预置条件,在每条测试用例开始前执行,执行次数与it定义的测试用例数一致,支持一个参数:预置动作函数 | +| 4 | afterEach | 在测试套内定义一个单元清理条件,在每条测试用例结束后执行,执行次数与it定义的测试用例数一致,支持一个参数:清理动作函数 | +| 5 | afterAll | 在测试套内定义一个清理条件,在所有测试用例结束后执行且仅执行一次,支持一个参数:清理动作函数 | +| 6 | it | 定义一条测试用例,支持三个参数:用例名称,过滤参数和用例函数 | +| 7 | expect | 支持bool类型判断等多种断言方法 | + +#### 断言库 + +- 示例代码: + +```javascript + expect(${actualvalue}).assertX(${expectvalue}) +``` + +- 断言功能列表: + +| No. | API | 功能说明 | +| :--- | :------------------------------- | ---------------------------------------------------------------------------------------------- | +| 1 | assertClose | 检验actualvalue和expectvalue(0)的接近程度是否是expectValue(1) | +| 2 | assertContain | 检验actualvalue中是否包含expectvalue | +| 3 | assertDeepEquals | @since1.0.4 检验actualvalue和expectvalue(0)是否是同一个对象 | +| 4 | assertEqual | 检验actualvalue是否等于expectvalue[0] | +| 5 | assertFail | 抛出一个错误 | +| 6 | assertFalse | 检验actualvalue是否是false | +| 7 | assertTrue | 检验actualvalue是否是true | +| 8 | assertInstanceOf | 检验actualvalue是否是expectvalue类型 | +| 9 | assertLarger | 检验actualvalue是否大于expectvalue | +| 10 | assertLess | 检验actualvalue是否小于expectvalue | +| 11 | assertNaN | @since1.0.4 检验actualvalue是否是NaN | +| 12 | assertNegUnlimited | @since1.0.4 检验actualvalue是否等于Number.NEGATIVE_INFINITY | +| 13 | assertNull | 检验actualvalue是否是null | +| 14 | assertPosUnlimited | @since1.0.4 检验actualvalue是否等于Number.POSITIVE_INFINITY | +| 15 | assertPromiseIsPending | @since1.0.4 检验actualvalue是否处于Pending状态【actualvalue为promse对象】 | +| 16 | assertPromiseIsRejected | @since1.0.4 检验actualvalue是否处于Rejected状态【同15】 | +| 17 | assertPromiseIsRejectedWith | @since1.0.4 检验actualvalue是否处于Rejected状态,并且比较执行的结果值【同15】 | +| 18 | assertPromiseIsRejectedWithError | @since1.0.4 检验actualvalue是否处于Rejected状态并有异常,同时比较异常的类型和message值【同15】 | +| 19 | assertPromiseIsResolved | @since1.0.4 检验actualvalue是否处于Resolved状态【同15】 | +| 20 | assertPromiseIsResolvedWith | @since1.0.4 检验actualvalue是否处于Resolved状态,并且比较执行的结果值【同15】 | +| 21 | assertThrowError | 检验actualvalue抛出Error内容是否是expectValue | +| 22 | assertUndefined | 检验actualvalue是否是undefined | +| 23 | not | @since1.0.4 断言结果取反 | + + +示例代码: + +```javascript + import { describe, it, expect } from '@ohos/hypium'; + + export default async function assertCloseTest() { + describe('assertClose', function () { + it('assertClose_success', 0, function () { + let a = 100; + let b = 0.1; + expect(a).assertClose(99, b); + }) + }) + } +``` + +#### 公共系统能力 + +| No. | API | 功能描述 | +| ---- | ------------------------------------------------------- | ------------------------------------------------------------ | +| 1 | existKeyword(keyword: string, timeout: number): boolean | @since1.0.3 hilog日志中查找指定字段是否存在,keyword是待查找关键字,timeout为设置的查找时间 | +| 2 | actionStart(tag: string): void | @since1.0.3 cmd窗口输出开始tag | +| 3 | actionEnd(tag: string): void | @since1.0.3 cmd窗口输出结束tag | + +示例代码: + +```javascript +import { describe, it, expect, SysTestKit} from '@ohos/hypium'; + +export default function existKeywordTest() { + describe('existKeywordTest', function () { + it('existKeyword',DEFAULT, async function () { + console.info("HelloTest"); + let isExist = await SysTestKit.existKeyword('HelloTest'); + console.info('isExist ------>' + isExist); + }) + }) +} +``` +```javascript +import { describe, it, expect, SysTestKit} from '@ohos/hypium'; + +export default function actionTest() { + describe('actionTest', function () { + it('existKeyword',DEFAULT, async function () { + let tag = '[MyTest]'; + SysTestKit.actionStart(tag); + //do something + SysTestKit.actionEnd(tag); + }) + }) +} +``` + +#### 专项能力 + +- 测试用例属性筛选能力:hypium支持根据用例属性筛选执行指定测试用例,使用方式是先在测试用例上标记用例属性后,再在测试应用的启动shell命令后新增" -s ${Key} ${Value}"。 + +| Key | 含义说明 | Value取值范围 | +| -------- | ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------ | +| level | 用例级别 | "0","1","2","3","4", 例如:-s level 1 | +| size | 用例粒度 | "small","medium","large", 例如:-s size small | +| testType | 用例测试类型 | "function","performance","power","reliability","security","global","compatibility","user","standard","safety","resilience", 例如:-s testType function | + +示例代码 + +```javascript +import { describe, it, expect, TestType, Size, Level } from '@ohos/hypium'; + +export default function attributeTest() { + describe('attributeTest', function () { + it("testAttributeIt", TestType.FUNCTION | Size.SMALLTEST | Level.LEVEL0, function () { + console.info('Hello Test'); + }) + }) +} +``` + +示例命令 +```shell +XX -s level 1 -s size small -s testType function +``` +该命令的作用是:筛选测试应用中同时满足a)用例级别是1 b)用例粒度是small c)用例测试类型是function 三个条件的用例执行。 + +- 测试套/测试用例名称筛选能力(测试套与用例名称用“#”号连接,多个用“,”英文逗号分隔) + +| Key | 含义说明 | Value取值范围 | +| -------- | ----------------------- | -------------------------------------------------------------------------------------------- | +| class | 指定要执行的测试套&用例 | ${describeName}#${itName},${describeName} , 例如:-s class attributeTest#testAttributeIt | +| notClass | 指定不执行的测试套&用例 | ${describeName}#${itName},${describeName} , 例如:-s notClass attributeTest#testAttributeIt | + +示例命令 +```shell +XX -s class attributeTest#testAttributeIt,abilityTest#testAbilityIt +``` +该命令的作用是:筛选测试应用中attributeTest测试套下的testAttributeIt测试用例,abilityTest测试套下的testAbilityIt测试用例,只执行这两条用例。 + +- 其他能力 + +| 能力项 | Key | 含义说明 | Value取值范围 | +| ------------ | ------- | ---------------------------- | ---------------------------------------------- | +| 随机执行能力 | random | 测试套&测试用例随机执行 | true, 不传参默认为false, 例如:-s random true | +| 空跑能力 | dryRun | 显示要执行的测试用例信息全集 | true , 不传参默认为false,例如:-s dryRun true | +| 异步超时能力 | timeout | 异步用例执行的超时时间 | 正整数 , 单位ms,例如:-s timeout 5000 | + +##### 约束限制 +随机执行能力和空跑能力从npm包1.0.3版本开始支持 + +#### Mock能力 + +##### 约束限制 + +单元测试框架Mock能力从npm包[1.0.1版本](https://repo.harmonyos.com/#/cn/application/atomService/@ohos%2Fhypium/v/1.0.1)开始支持 + +## 约束 + +*** + 本模块首批接口从OpenHarmony SDK API version 8开始支持。 + +## Hypium开放能力隐私声明 + +- 我们如何收集和使用您的个人信息 + 您在使用集成了Hypium开放能力的测试应用时,Hypium不会处理您的个人信息。 +- SDK处理的个人信息 + 不涉及。 +- SDK集成第三方服务声明 + 不涉及。 +- SDK数据安全保护 + 不涉及。 +- SDK版本更新声明 + 为了向您提供最新的服务,我们会不时更新Hypium版本。我们强烈建议开发者集成使用最新版本的Hypium。 + diff --git a/oh_modules/@ohos/hypium/ResourceTable.txt b/oh_modules/@ohos/hypium/ResourceTable.txt new file mode 100644 index 0000000..d159750 --- /dev/null +++ b/oh_modules/@ohos/hypium/ResourceTable.txt @@ -0,0 +1 @@ +string page_show 0x02000000 \ No newline at end of file diff --git a/oh_modules/@ohos/hypium/build-profile.json5 b/oh_modules/@ohos/hypium/build-profile.json5 new file mode 100644 index 0000000..16208e7 --- /dev/null +++ b/oh_modules/@ohos/hypium/build-profile.json5 @@ -0,0 +1,28 @@ +{ + "apiType": "stageMode", + "buildOption": { + }, + "buildOptionSet": [ + { + "name": "release", + "arkOptions": { + "obfuscation": { + "ruleOptions": { + "enable": true, + "files": [ + "./obfuscation-rules.txt" + ] + }, + "consumerFiles": [ + "./consumer-rules.txt" + ] + } + }, + }, + ], + "targets": [ + { + "name": "default" + } + ] +} diff --git a/oh_modules/@ohos/hypium/consumer-rules.txt b/oh_modules/@ohos/hypium/consumer-rules.txt new file mode 100644 index 0000000..e69de29 diff --git a/oh_modules/@ohos/hypium/hvigorfile.ts b/oh_modules/@ohos/hypium/hvigorfile.ts new file mode 100644 index 0000000..4218707 --- /dev/null +++ b/oh_modules/@ohos/hypium/hvigorfile.ts @@ -0,0 +1,6 @@ +import { harTasks } from '@ohos/hvigor-ohos-plugin'; + +export default { + system: harTasks, /* Built-in plugin of Hvigor. It cannot be modified. */ + plugins:[] /* Custom plugin to extend the functionality of Hvigor. */ +} diff --git a/oh_modules/@ohos/hypium/index.d.ts b/oh_modules/@ohos/hypium/index.d.ts new file mode 100644 index 0000000..58e1066 --- /dev/null +++ b/oh_modules/@ohos/hypium/index.d.ts @@ -0,0 +1,150 @@ +/* + * Copyright (c) 2021-2024 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 const DEFAULT = 0B0000 + +export const when: when; + +export enum TestType { + FUNCTION = 0B1, + PERFORMANCE = 0B1 << 1, + POWER = 0B1 << 2, + RELIABILITY = 0B1 << 3, + SECURITY = 0B1 << 4, + GLOBAL = 0B1 << 5, + COMPATIBILITY = 0B1 << 6, + USER = 0B1 << 7, + STANDARD = 0B1 << 8, + SAFETY = 0B1 << 9, + RESILIENCE = 0B1 << 10 +} + +export enum Size { + SMALLTEST = 0B1 << 16, + MEDIUMTEST = 0B1 << 17, + LARGETEST = 0B1 << 18 +} + +export enum Level { + LEVEL0 = 0B1 << 24, + LEVEL1 = 0B1 << 25, + LEVEL2 = 0B1 << 26, + LEVEL3 = 0B1 << 27, + LEVEL4 = 0B1 << 28 +} +export { xdescribe, xit, describe, it } from './index'; + + + +export function beforeItSpecified(testCaseNames: Array | string, callback: Function): void + +export function afterItSpecified(testCaseNames: Array | string, callback: Function): void + +export function beforeEach(callback: Function): void + +export function afterEach(callback: Function): void + +export function beforeAll(callback: Function): void + +export function afterAll(callback: Function): void + + +export interface Assert { + assertClose(expectValue: number, precision: number): void + assertContain(expectValue: any): void + assertEqual(expectValue: any): void + assertFail(): void + assertFalse(): void + assertTrue(): void + assertInstanceOf(expectValue: string): void + assertLarger(expectValue: number): void + assertLess(expectValue: number): void + assertNull(): void + assertThrowError(expectValue: string | Function): void + assertUndefined(): void + assertLargerOrEqual(expectValue: number): void + assertLessOrEqual(expectValue: number): void + assertNaN(): void + assertNegUnlimited(): void + assertPosUnlimited(): void + not(): Assert; + assertDeepEquals(expectValue: any): void + assertPromiseIsPending(): Promise + assertPromiseIsRejected(): Promise + assertPromiseIsRejectedWith(expectValue?: any): Promise + assertPromiseIsRejectedWithError(...expectValue): Promise + assertPromiseIsResolved(): Promise + assertPromiseIsResolvedWith(expectValue?: any): Promise + message(msg: string): Assert +} + +export function expect(actualValue?: any): Assert + +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 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): VerificationMode + ignoreMock(obj: Object, func: Function): void + clear(obj: Object): void + clearAll(): void +} + +export class SysTestKit { + static getDescribeName(): string; + static getItName(): string; + static getItAttribute(): TestType | Size | Level + static actionStart(tag: string): void + static actionEnd(tag: string): void + static existKeyword(keyword: string, timeout?: number): boolean +} + +export class Hypium { + static setData(data: { [key: string]: any }): void + static setTimeConfig(systemTime: any) + static hypiumTest(abilityDelegator: any, abilityDelegatorArguments: any, testsuite: Function): void + static set(key: string, value: any): void + static get(key: string): any + static registerAssert(customAssertion: Function): void + static unregisterAssert(customAssertion: string | Function): void + static hypiumWorkerTest(abilityDelegator: Object, abilityDelegatorArguments: Object, testsuite: Function, workerPort: Object): void; + static hypiumInitWorkers(abilityDelegator: Object, scriptURL: string, workerNum: number, params: Object): void; +} \ No newline at end of file diff --git a/oh_modules/@ohos/hypium/index.ets b/oh_modules/@ohos/hypium/index.ets new file mode 100644 index 0000000..996fc7d --- /dev/null +++ b/oh_modules/@ohos/hypium/index.ets @@ -0,0 +1,137 @@ +/* + * Copyright (c) 2021-2024 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 Core from './src/main/core'; +import {TestType, Size, Level, DEFAULT} from './src/main/Constant'; +import DataDriver from './src/main/module/config/DataDriver'; +import ExpectExtend from './src/main/module/assert/ExpectExtend'; +import OhReport from './src/main/module/report/OhReport'; +export { xdescribe, xit, describe, it } from './index.ts'; + +export declare class Hypium { + static setData(data: Object): void + static setTimeConfig(systemTime: Object): void + static hypiumTest(abilityDelegator: Object, abilityDelegatorArguments: Object, testsuite: Function): void + static set(key: string, value: Object): void + static get(key: string): Object + static registerAssert(customAssertion: Function): void + static unregisterAssert(customAssertion: string | Function): void + static hypiumWorkerTest(abilityDelegator: Object, abilityDelegatorArguments: Object, + testsuite: Function, workerPort: Object): void; + static hypiumInitWorkers(abilityDelegator: Object, scriptURL: string, workerNum: number, params: Object): void; +} + +export { + Core, + DataDriver, + ExpectExtend, + OhReport, + TestType, + Size, + Level, + DEFAULT +}; + +type allExpectType = Object | undefined | null + +export declare function beforeItSpecified(testCaseNames: Array | string, callback: Function): void + +export declare function afterItSpecified(testCaseNames: Array | string, callback: Function): void + +export declare function beforeEach(callback: Function): void + +export declare function afterEach(callback: Function): void + +export declare function beforeAll(callback: Function): void + +export declare function afterAll(callback: Function): void + +export declare interface Assert { + assertClose(expectValue: number, precision: number): void + assertContain(expectValue: allExpectType): void + assertEqual(expectValue: allExpectType): void + assertFail(): void + assertFalse(): void + assertTrue(): void + assertInstanceOf(expectValue: string): void + assertLarger(expectValue: number): void + assertLess(expectValue: number): void + assertNull(): void + assertThrowError(expectValue: string | Function): void + assertUndefined(): void + assertLargerOrEqual(expectValue: number):void + assertLessOrEqual(expectValue: number):void + assertNaN():void + assertNegUnlimited(): void + assertPosUnlimited(): void + not(): Assert; + assertDeepEquals(expectValue: allExpectType):void + assertPromiseIsPending(): Promise + assertPromiseIsRejected(): Promise + assertPromiseIsRejectedWith(expectValue?: allExpectType): Promise + assertPromiseIsRejectedWithError(...expectValue: allExpectType[]): Promise + assertPromiseIsResolved(): Promise + assertPromiseIsResolvedWith(expectValue?: allExpectType): Promise + message(msg: string): Assert +} + +export declare function expect(actualValue?: allExpectType): Assert + +export declare class ArgumentMatchers { + public static any: allExpectType; + public static anyString: string; + public static anyBoolean: Boolean; + public static anyNumber: Number; + public static anyObj: Object; + public static anyFunction: Function; + public static matchRegexs(regex: RegExp): void +} + +declare interface whenResult { + afterReturn: (value: allExpectType) => allExpectType + afterReturnNothing: () => undefined + afterAction: (action: allExpectType) => allExpectType + afterThrow: (e_msg: string) => string +} + +export declare function when(f:Function): (...args: (allExpectType | void)[]) => whenResult + +export declare interface VerificationMode { + times(count: Number): void + never(): void + once(): void + atLeast(count: Number): void + atMost(count: Number): void +} + +export declare class MockKit { + constructor() + mockFunc(obj: Object, func: Function): Function + mockObject(obj: Object): Object + verify(methodName: String, argsArray: Array): VerificationMode + ignoreMock(obj: Object, func: Function): void + clear(obj: Object): void + clearAll(): void +} + +export declare class SysTestKit { + static getDescribeName(): string; + static getItName(): string; + static getItAttribute(): TestType | Size | Level + static actionStart(tag: string): void + static actionEnd(tag: string): void + static existKeyword(keyword: string, timeout?: number): boolean +} + diff --git a/oh_modules/@ohos/hypium/index.js b/oh_modules/@ohos/hypium/index.js new file mode 100644 index 0000000..954f765 --- /dev/null +++ b/oh_modules/@ohos/hypium/index.js @@ -0,0 +1,261 @@ +/* + * Copyright (c) 2021-2024 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 Core from './src/main/core'; +import { DEFAULT, TestType, Size, Level, TAG, PrintTag } from './src/main/Constant'; +import DataDriver from './src/main/module/config/DataDriver'; +import ExpectExtend from './src/main/module/assert/ExpectExtend'; +import OhReport from './src/main/module/report/OhReport'; +import SysTestKit from './src/main/module/kit/SysTestKit'; +import { describe, beforeAll, beforeEach, afterEach, afterAll, it, expect, beforeItSpecified, afterItSpecified, xdescribe, xit } from './src/main/interface'; +import { MockKit, when } from './src/main/module/mock/MockKit'; +import ArgumentMatchers from './src/main/module/mock/ArgumentMatchers'; +import worker from '@ohos.worker'; + +class Hypium { + static context = new Map(); + static setData(data) { + const core = Core.getInstance(); + const dataDriver = new DataDriver({ data }); + core.addService('dataDriver', dataDriver); + } + + static setTimeConfig(systemTime) { + SysTestKit.systemTime = systemTime; + } + + static set(key, value) { + Hypium.context.set(key, value); + } + + static get(key) { + return Hypium.context.get(key); + } + + static hypiumTest(abilityDelegator, abilityDelegatorArguments, testsuite) { + const core = Core.getInstance(); + const expectExtend = new ExpectExtend({ + 'id': 'extend' + }); + core.addService('expect', expectExtend); + const ohReport = new OhReport({ + 'delegator': abilityDelegator, + 'abilityDelegatorArguments': abilityDelegatorArguments + }); + SysTestKit.delegator = abilityDelegator; + core.addService('report', ohReport); + core.init(); + core.subscribeEvent('spec', ohReport); + core.subscribeEvent('suite', ohReport); + core.subscribeEvent('task', ohReport); + const configService = core.getDefaultService('config'); + if (abilityDelegatorArguments !== null) { + let testParameters = configService.translateParams(abilityDelegatorArguments.parameters); + console.info(`${TAG}parameters:${JSON.stringify(testParameters)}`); + configService.setConfig(testParameters); + } + testsuite(); + core.execute(abilityDelegator); + } + static async hypiumInitWorkers(abilityDelegator, scriptURL, workerNum = 8, params) { + console.info(`${TAG}, hypiumInitWorkers call,${scriptURL}`); + let workerPromiseArray = []; + + // 开始统计时间 + let startTime = await SysTestKit.getRealTime(); + for (let i = 0; i < workerNum; i++) { + // 创建worker线程 + const workerPromise = Hypium.createWorkerPromise(scriptURL, i, params); + workerPromiseArray.push(workerPromise); + } + const ret = {total: 0, failure: 0, error: 0, pass: 0, ignore: 0, duration: 0}; + Promise.all(workerPromiseArray).then(async (items) => { + console.info(`${TAG}, all result from workers, ${JSON.stringify(items)}`); + let allItemList = new Array(); + // 统计执行结果 + Hypium.handleWorkerTestResult(ret, allItemList, items); + console.info(`${TAG}, all it result, ${JSON.stringify(allItemList)}`); + // 统计用例执行结果 + const retResult = {total: 0, failure: 0, error: 0, pass: 0, ignore: 0, duration: 0}; + // 标记用例执行结果 + Hypium.configWorkerItTestResult(retResult, allItemList); + // 打印用例结果 + Hypium.printWorkerTestResult(abilityDelegator, allItemList); + // 用例执行完成统计时间 + let endTime = await SysTestKit.getRealTime(); + const taskConsuming = endTime - startTime; + const message = + `\n${PrintTag.OHOS_REPORT_ALL_RESULT}: stream=Test run: runTimes: ${ret.total},total: ${retResult.total}, Failure: ${retResult.failure}, Error: ${retResult.error}, Pass: ${retResult.pass}, Ignore: ${retResult.ignore}` + + `\n${PrintTag.OHOS_REPORT_ALL_CODE}: ${retResult.failure > 0 || retResult.error > 0 ? -1 : 0}` + + `\n${PrintTag.OHOS_REPORT_ALL_STATUS}: taskconsuming=${taskConsuming > 0 ? taskConsuming : ret.duration}`; + abilityDelegator.printSync(message); + console.info(`${TAG}, [end] you worker test`); + abilityDelegator.finishTest('you worker test finished!!!', 0, () => {}); + }).catch((e) => { + console.info(`${TAG}, [end] error you worker test, ${JSON.stringify(e)}`); + abilityDelegator.finishTest('you worker test error finished!!!', 0, () => {}); + }).finally(() => { + console.info(`${TAG}, all promise finally end`); + }); + } + // 创建worker线程 + static createWorkerPromise(scriptURL, i, params) { + console.info(`${TAG}, createWorkerPromiser, ${scriptURL}, ${i}`); + const workerPromise = new Promise((resolve, reject) => { + const workerInstance = new worker.ThreadWorker(scriptURL, {name: `worker_${i}`}); + console.info(`${TAG}, send data to worker`); + // 发送数据到worker线程中 + workerInstance.postMessage(params); + workerInstance.onmessage = function (e) { + let currentThreadName = e.data?.currentThreadName; + console.info(`${TAG}, receview data from ${currentThreadName}, ${JSON.stringify(e.data)}`); + // + resolve(e.data?.summary); + console.info(`${TAG}, ${currentThreadName} finish`); + workerInstance.terminate(); + }; + workerInstance.onerror = function (e) { + console.info(`${TAG}, worker error, ${JSON.stringify(e)}`); + reject(e); + workerInstance.terminate(); + }; + workerInstance.onmessageerror = function (e) { + console.info(`${TAG}, worker message error, ${JSON.stringify(e)}`); + reject(e); + workerInstance.terminate(); + }; + }); + return workerPromise; + } + static handleWorkerTestResult(ret, allItemList, items) { + console.info(`${TAG}, handleWorkerTestResult, ${JSON.stringify(items)}`); + for (const {total, failure, error, pass, ignore, duration, itItemList} of items) { + ret.total += total; + ret.failure += failure; + ret.error += error; + ret.pass += pass; + ret.ignore += ignore; + ret.duration += duration; + Hypium.handleItResult(allItemList, itItemList); + } + } + static handleItResult(allItemList, itItemList) { + // 遍历所有的用例结果统计最终结果 + for (const {currentThreadName, description, result} of itItemList) { + let item = allItemList.find((it) => it.description === description); + if (item) { + let itResult = item.result; + // 当在worker中出现一次failure就标记为failure, 出现一次error就标记为error, 所有线程都pass才标记为pass + if (itResult === 0) { + item.result = result; + item.currentThreadName = currentThreadName; + } + } else { + let it = { + description: description, + currentThreadName: currentThreadName, + result: result + }; + allItemList.push(it); + } + } + } + static configWorkerItTestResult(retResult, allItemList) { + console.info(`${TAG}, configWorkerItTestResult, ${JSON.stringify(allItemList)}`); + for (const {currentThreadName, description, result} of allItemList) { + console.info(`${TAG}, description, ${description}, result,${result}`); + retResult.total ++; + if (result === 0) { + retResult.pass ++; + } else if (result === -1) { + retResult.error ++; + } else if (result === -2) { + retResult.failure ++; + } else { + retResult.ignore ++; + } + } + } + static printWorkerTestResult(abilityDelegator, allItemList) { + console.info(`${TAG}, printWorkerTestResult, ${JSON.stringify(allItemList)}`); + let index = 1; + for (const {currentThreadName, description, result} of allItemList) { + console.info(`${TAG}, description print, ${description}, result,${result}`); + let itArray = description.split('#'); + let des; + let itName; + if (itArray.length > 1) { + des = itArray[0]; + itName = itArray[1]; + } else if (itArray.length > 1) { + des = itArray[0]; + itName = itArray[0]; + } else { + des = 'undefined'; + itName = 'undefined'; + } + + let msg = `\n${PrintTag.OHOS_REPORT_WORKER_STATUS}: class=${des}`; + msg += `\n${PrintTag.OHOS_REPORT_WORKER_STATUS}: test=${itName}`; + msg += `\n${PrintTag.OHOS_REPORT_WORKER_STATUS}: current=${index}`; + msg += `\n${PrintTag.OHOS_REPORT_WORKER_STATUS}: CODE=${result}`; + abilityDelegator.printSync(msg); + index ++; + } + } + static hypiumWorkerTest(abilityDelegator, abilityDelegatorArguments, testsuite, workerPort) { + console.info(`${TAG}, hypiumWorkerTest call`); + SysTestKit.workerPort = workerPort; + let currentWorkerName = workerPort.name; + console.info(`${TAG}, hypiumWorkerTest_currentWorkerName: ${currentWorkerName}`); + Hypium.hypiumTest(abilityDelegator, abilityDelegatorArguments, testsuite); + + } + + static registerAssert(customAssertion) { + const core = Core.getInstance(); + const expectService = core.getDefaultService('expect'); + let matchers = {}; + matchers[customAssertion.name] = customAssertion; + expectService.addMatchers(matchers); + expectService.customMatchers.push(customAssertion.name); + console.info(`${TAG}success to register the ${customAssertion.name}`); + } + + static unregisterAssert(customAssertion) { + const core = Core.getInstance(); + const expectService = core.getDefaultService('expect'); + let customAssertionName = typeof customAssertion === 'function' ? customAssertion.name : customAssertion; + expectService.removeMatchers(customAssertionName); + console.info(`${TAG}success to unregister the ${customAssertionName}`); + } + +} + +export { + Hypium, + Core, + DEFAULT, + TestType, + Size, + Level, + DataDriver, + ExpectExtend, + OhReport, + SysTestKit, + describe, beforeAll, beforeEach, afterEach, afterAll, it, expect, beforeItSpecified, afterItSpecified, xdescribe, xit, + MockKit, when, + ArgumentMatchers +}; \ No newline at end of file diff --git a/oh_modules/@ohos/hypium/index.ts b/oh_modules/@ohos/hypium/index.ts new file mode 100644 index 0000000..6e059e3 --- /dev/null +++ b/oh_modules/@ohos/hypium/index.ts @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2024 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 { TestType, Size, Level } from "./src/main/Constant"; + +export declare function xdescribe(testSuiteName: string, func: Function): void; + +export declare namespace xdescribe { + function reason(reason: string): any; +}; + +export declare function describe(testSuiteName: string, func: Function): void; + +export declare function xit(testCaseName: string, attribute: TestType | Size | Level, func: Function): void; + +export declare namespace xit { + function reason(reason: string): any; +}; + +export declare function it(testCaseName: string, attribute: TestType | Size | Level, func: Function): void; diff --git a/oh_modules/@ohos/hypium/obfuscation-rules.txt b/oh_modules/@ohos/hypium/obfuscation-rules.txt new file mode 100644 index 0000000..985b2ae --- /dev/null +++ b/oh_modules/@ohos/hypium/obfuscation-rules.txt @@ -0,0 +1,18 @@ +# Define project specific obfuscation rules here. +# You can include the obfuscation configuration files in the current module's build-profile.json5. +# +# For more details, see +# https://gitee.com/openharmony/arkcompiler_ets_frontend/blob/master/arkguard/README.md + +# Obfuscation options: +# -disable-obfuscation: disable all obfuscations +# -enable-property-obfuscation: obfuscate the property names +# -enable-toplevel-obfuscation: obfuscate the names in the global scope +# -compact: remove unnecessary blank spaces and all line feeds +# -remove-log: remove all console.* statements +# -print-namecache: print the name cache that contains the mapping from the old names to new names +# -apply-namecache: reuse the given cache file + +# Keep options: +# -keep-property-name: specifies property names that you want to keep +# -keep-global-name: specifies names that you want to keep in the global scope \ No newline at end of file diff --git a/oh_modules/@ohos/hypium/oh-package.json5 b/oh_modules/@ohos/hypium/oh-package.json5 new file mode 100644 index 0000000..3ef03ac --- /dev/null +++ b/oh_modules/@ohos/hypium/oh-package.json5 @@ -0,0 +1 @@ +{"name":"@ohos/hypium","version":"1.0.21","description":"A unit test framework for OpenHarmony application","main":"index.js","keywords":["测试框架","except","mock"],"author":"huawei","license":"Apache-2.0","repository":"https://gitee.com/openharmony/testfwk_arkxtest","homepage":"https://gitee.com/openharmony/testfwk_arkxtest","dependencies":{},"metadata":{"sourceRoots":["./src/main"],"debug":true},"compatibleSdkVersion":11,"compatibleSdkType":"OpenHarmony","obfuscated":false} diff --git a/oh_modules/@ohos/hypium/src/main/Constant.js b/oh_modules/@ohos/hypium/src/main/Constant.js new file mode 100644 index 0000000..2f6e8a1 --- /dev/null +++ b/oh_modules/@ohos/hypium/src/main/Constant.js @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2021-2024 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. + */ + +/** + * define the testcase type : TestType, Size , Level + */ +export const TAG = '[Hypium]'; + +export const DEFAULT = 0B0000; + +export class PrintTag { + static OHOS_REPORT_WORKER_STATUS = 'OHOS_REPORT_WORKER_STATUS'; + static OHOS_REPORT_ALL_RESULT = 'OHOS_REPORT_ALL_RESULT'; + static OHOS_REPORT_ALL_CODE = 'OHOS_REPORT_ALL_CODE'; + static OHOS_REPORT_ALL_STATUS = 'OHOS_REPORT_ALL_STATUS'; + static OHOS_REPORT_RESULT = 'OHOS_REPORT_RESULT'; + static OHOS_REPORT_CODE = 'OHOS_REPORT_CODE'; + static OHOS_REPORT_STATUS = 'OHOS_REPORT_STATUS'; + static OHOS_REPORT_SUM = 'OHOS_REPORT_SUM'; + static OHOS_REPORT_STATUS_CODE = 'OHOS_REPORT_STATUS_CODE'; +}; + +export class TestType { + static FUNCTION = 0B1; + static PERFORMANCE = 0B1 << 1; + static POWER = 0B1 << 2; + static RELIABILITY = 0B1 << 3; + static SECURITY = 0B1 << 4; + static GLOBAL = 0B1 << 5; + static COMPATIBILITY = 0B1 << 6; + static USER = 0B1 << 7; + static STANDARD = 0B1 << 8; + static SAFETY = 0B1 << 9; + static RESILIENCE = 0B1 << 10; +}; + +export class Size { + static SMALLTEST = 0B1 << 16; + static MEDIUMTEST = 0B1 << 17; + static LARGETEST = 0B1 << 18; +}; + +export class Level { + static LEVEL0 = 0B1 << 24; + static LEVEL1 = 0B1 << 25; + static LEVEL2 = 0B1 << 26; + static LEVEL3 = 0B1 << 27; + static LEVEL4 = 0B1 << 28; +}; + +export const TESTTYPE = { + 'function': 1, + 'performance': 1 << 1, + 'power': 1 << 2, + 'reliability': 1 << 3, + 'security': 1 << 4, + 'global': 1 << 5, + 'compatibility': 1 << 6, + 'user': 1 << 7, + 'standard': 1 << 8, + 'safety': 1 << 9, + 'resilience': 1 << 10, +}; + +export const LEVEL = { + '0': 1 << 24, + '1': 1 << 25, + '2': 1 << 26, + '3': 1 << 27, + '4': 1 << 28, +}; + +export const SIZE = { + 'small': 1 << 16, + 'medium': 1 << 17, + 'large': 1 << 18, +}; + +export const KEYSET = [ + '-s class', '-s notClass', '-s suite', '-s itName', + '-s level', '-s testType', '-s size', '-s timeout', + '-s dryRun', '-s random', '-s breakOnError', '-s stress', + '-s coverage', '-s skipMessage', '-s runSkipped', + 'class', 'notClass', 'suite', 'itName', + 'level', 'testType', 'size', 'timeout', 'dryRun', 'random', + 'breakOnError', 'stress', 'coverage', 'skipMessage', 'runSkipped' +]; diff --git a/oh_modules/@ohos/hypium/src/main/core.js b/oh_modules/@ohos/hypium/src/main/core.js new file mode 100644 index 0000000..1981a5a --- /dev/null +++ b/oh_modules/@ohos/hypium/src/main/core.js @@ -0,0 +1,159 @@ +/* + * Copyright (c) 2021-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 {SuiteService, SpecService, ExpectService, ReportService} from './service'; +import {ConfigService} from './module/config/configService'; +import {SpecEvent, TaskEvent, SuiteEvent} from './event'; + +/** + * core service for execute testcase. + */ +class Core { + static getInstance() { + if (!this.instance) { + this.instance = new Core(); + } + return this.instance; + } + + constructor() { + this.instance = null; + this.services = { + suite: {}, + spec: {}, + config: {}, + expect: {}, + log: {}, + report: {} + + }; + this.events = { + suite: {}, + spec: {}, + task: {} + }; + } + + addService(name, service) { + let serviceObj = {}; + if (!this.services[name]) { + this.services[name] = serviceObj; + } else { + serviceObj = this.services[name]; + } + serviceObj[service.id] = service; + } + + getDefaultService(name) { + return this.services[name].default; + } + + getServices(name) { + return this.services[name]; + } + + registerEvent(serviceName, event) { + let eventObj = {}; + if (!this.events[serviceName]) { + this.events[serviceName] = eventObj; + } else { + eventObj = this.events[serviceName]; + } + eventObj[event.id] = event; + } + + unRegisterEvent(serviceName, eventID) { + const eventObj = this.events[serviceName]; + if (eventObj) { + delete eventObj[eventID]; + } + } + + subscribeEvent(serviceName, serviceObj) { + const eventObj = this.events[serviceName]; + if (eventObj) { + for (const attr in eventObj) { + eventObj[attr]['subscribeEvent'](serviceObj); + } + } + } + + async fireEvents(serviceName, eventName) { + const eventObj = this.events[serviceName]; + if (!eventObj) { + return; + } + for (const attr in eventObj) { + await eventObj[attr][eventName](); + } + } + + addToGlobal(apis) { + if (typeof globalThis !== 'undefined') { + for (let api in apis) { + globalThis[api] = apis[api]; + } + } + for (const api in apis) { + this[api] = apis[api]; + } + } + + init() { + this.addService('suite', new SuiteService({id: 'default'})); + this.addService('spec', new SpecService({id: 'default'})); + this.addService('expect', new ExpectService({id: 'default'})); + this.addService('report', new ReportService({id: 'default'})); + this.addService('config', new ConfigService({id: 'default'})); + this.registerEvent('task', new TaskEvent({id: 'default', coreContext: this})); + this.registerEvent('suite', new SuiteEvent({id: 'default', coreContext: this})); + this.registerEvent('spec', new SpecEvent({id: 'default', coreContext: this})); + this.subscribeEvent('spec', this.getDefaultService('report')); + this.subscribeEvent('suite', this.getDefaultService('report')); + this.subscribeEvent('task', this.getDefaultService('report')); + const context = this; + for (const key in this.services) { + const serviceObj = this.services[key]; + for (const serviceID in serviceObj) { + const service = serviceObj[serviceID]; + service.init(context); + + if (typeof service.apis !== 'function') { + continue; + } + const apis = service.apis(); + if (apis) { + this.addToGlobal(apis); + } + } + } + } + + execute(abilityDelegator) { + const suiteService = this.getDefaultService('suite'); + const configService = this.getDefaultService('config'); + if (configService['dryRun'] === 'true') { + (async function () { + await suiteService.dryRun(abilityDelegator); + })(); + return; + } + setTimeout(() => { + suiteService.execute(); + }, 10); + } +} + +export default Core; diff --git a/oh_modules/@ohos/hypium/src/main/event.js b/oh_modules/@ohos/hypium/src/main/event.js new file mode 100644 index 0000000..d465896 --- /dev/null +++ b/oh_modules/@ohos/hypium/src/main/event.js @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2021-2024 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 SpecEvent { + constructor(attr) { + this.id = attr.id; + this.coreContext = attr.context; + this.eventMonitors = []; + } + + subscribeEvent(service) { + this.eventMonitors.push(service); + } + + async specStart() { + for (const monitor of this.eventMonitors) { + await monitor['specStart'](); + } + } + + async specDone() { + for (const monitor of this.eventMonitors) { + await monitor['specDone'](); + } + } +} + +class SuiteEvent { + constructor(attr) { + this.id = attr.id; + this.suiteContext = attr.coreContext; + this.eventMonitors = []; + } + + subscribeEvent(service) { + this.eventMonitors.push(service); + } + + async suiteStart() { + for (const monitor of this.eventMonitors) { + await monitor['suiteStart'](); + } + } + + async suiteDone() { + for (const monitor of this.eventMonitors) { + await monitor['suiteDone'](); + } + } +} + +class TaskEvent { + constructor(attr) { + this.id = attr.id; + this.coreContext = attr.coreContext; + this.eventMonitors = []; + } + + subscribeEvent(service) { + this.eventMonitors.push(service); + } + + async taskStart() { + for (const monitor of this.eventMonitors) { + await monitor['taskStart'](); + } + } + + async taskDone() { + for (const monitor of this.eventMonitors) { + await monitor['taskDone'](); + } + } + + incorrectFormat() { + for (const monitor of this.eventMonitors) { + monitor['incorrectFormat'](); + } + } + + incorrectTestSuiteFormat() { + for (const monitor of this.eventMonitors) { + monitor.incorrectTestSuiteFormat(); + } + } +} + +export { SpecEvent, TaskEvent, SuiteEvent }; diff --git a/oh_modules/@ohos/hypium/src/main/interface.js b/oh_modules/@ohos/hypium/src/main/interface.js new file mode 100644 index 0000000..e2530d2 --- /dev/null +++ b/oh_modules/@ohos/hypium/src/main/interface.js @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2021-2024 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 Core from './core'; + +const core = Core.getInstance(); + +const describe = function (desc, func) { + return Reflect.has(core, 'describe') ? core.describe(desc, func) : (desc, func) => { }; +}; +const it = function (desc, filter, func) { + return Reflect.has(core, 'it') ? core.it(desc, filter, func) : (desc, filter, func) => { }; +}; +const beforeItSpecified = function (itDescs, func) { + return Reflect.has(core, 'beforeItSpecified') ? core.beforeItSpecified(itDescs, func) : (itDescs, func) => { }; +}; + +const afterItSpecified = function (itDescs, func) { + return Reflect.has(core, 'afterItSpecified') ? core.afterItSpecified(itDescs, func) : (itDescs, func) => { }; +}; +const beforeEach = function (func) { + return Reflect.has(core, 'beforeEach') ? core.beforeEach(func) : (func) => { }; +}; +const afterEach = function (func) { + return Reflect.has(core, 'afterEach') ? core.afterEach(func) : (func) => { }; +}; +const beforeAll = function (func) { + return Reflect.has(core, 'beforeAll') ? core.beforeAll(func) : (func) => { }; +}; +const afterAll = function (func) { + return Reflect.has(core, 'afterAll') ? core.afterAll(func) : (func) => { }; +}; +const expect = function (actualValue) { + return Reflect.has(core, 'expect') ? core.expect(actualValue) : (actualValue) => { }; +}; + +const xdescribe = function (desc, func) { + return Reflect.has(core, 'xdescribe') ? core.xdescribe(desc, func, null) : (desc, func, reason) => { }; +}; +xdescribe.reason = (reason) => { + return (desc, func) => { + return Reflect.has(core, 'xdescribe') ? core.xdescribe(desc, func, reason) : (desc, func, reason) => { }; + }; +}; +const xit = function (desc, filter, func) { + return Reflect.has(core, 'xit') ? core.xit(desc, filter, func, null) : (desc, filter, func, reason) => { }; +}; +xit.reason = (reason) => { + return (desc, filter, func) => { + return Reflect.has(core, 'xit') ? core.xit(desc, filter, func, reason) : (desc, filter, func, reason) => { }; + }; +}; + +export { + describe, it, beforeAll, beforeEach, afterEach, afterAll, expect, beforeItSpecified, afterItSpecified, xdescribe, xit +}; diff --git a/oh_modules/@ohos/hypium/src/main/module/assert/assertClose.js b/oh_modules/@ohos/hypium/src/main/module/assert/assertClose.js new file mode 100644 index 0000000..b4470fa --- /dev/null +++ b/oh_modules/@ohos/hypium/src/main/module/assert/assertClose.js @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2021-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. + */ + +function assertClose(actualValue, expected) { + if (actualValue === null && expected[0] === null) { + throw new Error('actualValue and expected can not be both null!!!'); + } + let result; + let diff = Math.abs(expected[0] - actualValue); + let actualAbs = Math.abs(actualValue); + if ((actualAbs - 0) === 0) { + if ((diff - 0) === 0) { + result = true; + } else { + result = false; + } + } else if (diff / actualAbs < expected[1]) { + result = true; + } else { + result = false; + } + return { + pass: result, + message: '|' + actualValue + ' - ' + expected[0] + '|/' + actualValue + ' is not less than ' + expected[1] + }; +} + +export default assertClose; diff --git a/oh_modules/@ohos/hypium/src/main/module/assert/assertContain.js b/oh_modules/@ohos/hypium/src/main/module/assert/assertContain.js new file mode 100644 index 0000000..ee896ab --- /dev/null +++ b/oh_modules/@ohos/hypium/src/main/module/assert/assertContain.js @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2021-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. + */ + +function assertContain(actualValue, expect) { + let result = false; + if (Object.prototype.toString.call(actualValue).indexOf('Array')) { + for (let i in actualValue) { + if (actualValue[i] == expect[0]) { + result = true; + } + } + } + let type = Object.prototype.toString.call(actualValue); + if (type === '[object String]') { + result = actualValue.indexOf(expect[0]) >= 0; + } + return { + pass: result, + message: 'expect false, ' + actualValue + ' do not have ' + expect[0] + }; +} + +export default assertContain; diff --git a/oh_modules/@ohos/hypium/src/main/module/assert/assertFail.js b/oh_modules/@ohos/hypium/src/main/module/assert/assertFail.js new file mode 100644 index 0000000..e06dcd4 --- /dev/null +++ b/oh_modules/@ohos/hypium/src/main/module/assert/assertFail.js @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2021-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. + */ + +function assertFail() { + return { + pass: false, + message: 'fail ' + }; +} + +export default assertFail; diff --git a/oh_modules/@ohos/hypium/src/main/module/assert/assertFalse.js b/oh_modules/@ohos/hypium/src/main/module/assert/assertFalse.js new file mode 100644 index 0000000..299781f --- /dev/null +++ b/oh_modules/@ohos/hypium/src/main/module/assert/assertFalse.js @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2021-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. + */ + +function assertFalse(actualValue) { + return { + pass: (actualValue) === false, + message: 'expect false, actualValue is ' + actualValue + }; +} + +export default assertFalse; diff --git a/oh_modules/@ohos/hypium/src/main/module/assert/assertInstanceOf.js b/oh_modules/@ohos/hypium/src/main/module/assert/assertInstanceOf.js new file mode 100644 index 0000000..bc5e52c --- /dev/null +++ b/oh_modules/@ohos/hypium/src/main/module/assert/assertInstanceOf.js @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2021-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. + */ + +function assertInstanceOf(actualValue, expected) { + if (Object.prototype.toString.call(actualValue) == '[object ' + expected[0] + ']') { + return { + pass: true + }; + } else { + return { + pass: false, + message: actualValue + ' is ' + Object.prototype.toString.call(actualValue) + 'not ' + expected[0] + }; + } +} + +export default assertInstanceOf; diff --git a/oh_modules/@ohos/hypium/src/main/module/assert/assertLarger.js b/oh_modules/@ohos/hypium/src/main/module/assert/assertLarger.js new file mode 100644 index 0000000..a0fa15e --- /dev/null +++ b/oh_modules/@ohos/hypium/src/main/module/assert/assertLarger.js @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2021-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. + */ + +function assertLarger(actualValue, expected) { + return { + pass: (actualValue) > expected[0], + message: (actualValue) + ' is not larger than ' + expected[0] + }; +} + +export default assertLarger; diff --git a/oh_modules/@ohos/hypium/src/main/module/assert/assertLargerOrEqual.js b/oh_modules/@ohos/hypium/src/main/module/assert/assertLargerOrEqual.js new file mode 100644 index 0000000..3416bd1 --- /dev/null +++ b/oh_modules/@ohos/hypium/src/main/module/assert/assertLargerOrEqual.js @@ -0,0 +1,23 @@ +/* + * 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. + */ + +function assertLargerOrEqual(actualValue, expected) { + return { + pass: (actualValue) >= expected[0], + message: (actualValue) + ' is not larger than ' + expected[0] + }; +} + +export default assertLargerOrEqual; diff --git a/oh_modules/@ohos/hypium/src/main/module/assert/assertLess.js b/oh_modules/@ohos/hypium/src/main/module/assert/assertLess.js new file mode 100644 index 0000000..23a1d8c --- /dev/null +++ b/oh_modules/@ohos/hypium/src/main/module/assert/assertLess.js @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2021-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. + */ + +function assertLess(actualValue, expected) { + return { + pass: (actualValue) < expected[0], + message: (actualValue) + ' is not less than ' + expected[0] + }; +} + +export default assertLess; diff --git a/oh_modules/@ohos/hypium/src/main/module/assert/assertLessOrEqual.js b/oh_modules/@ohos/hypium/src/main/module/assert/assertLessOrEqual.js new file mode 100644 index 0000000..4a2d05e --- /dev/null +++ b/oh_modules/@ohos/hypium/src/main/module/assert/assertLessOrEqual.js @@ -0,0 +1,23 @@ +/* + * 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. + */ + +function assertLessOrEqual(actualValue, expected) { + return { + pass: (actualValue) <= expected[0], + message: (actualValue) + ' is not less than ' + expected[0] + }; +} + +export default assertLessOrEqual; diff --git a/oh_modules/@ohos/hypium/src/main/module/assert/assertNaN.js b/oh_modules/@ohos/hypium/src/main/module/assert/assertNaN.js new file mode 100644 index 0000000..fa06fe7 --- /dev/null +++ b/oh_modules/@ohos/hypium/src/main/module/assert/assertNaN.js @@ -0,0 +1,23 @@ +/* + * 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. + */ + +function assertNaN(actualValue) { + return { + pass: actualValue !== actualValue, + message: 'expect NaN, actualValue is ' + actualValue + }; +} + +export default assertNaN; diff --git a/oh_modules/@ohos/hypium/src/main/module/assert/assertNegUnlimited.js b/oh_modules/@ohos/hypium/src/main/module/assert/assertNegUnlimited.js new file mode 100644 index 0000000..3573ee0 --- /dev/null +++ b/oh_modules/@ohos/hypium/src/main/module/assert/assertNegUnlimited.js @@ -0,0 +1,23 @@ +/* +* 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. +*/ + +function assertNegUnlimited(actualValue) { + return { + pass: actualValue === Number.NEGATIVE_INFINITY, + message: 'Expected actualValue not to be -Infinity. actualValue is,' + actualValue + }; +} + +export default assertNegUnlimited; diff --git a/oh_modules/@ohos/hypium/src/main/module/assert/assertNull.js b/oh_modules/@ohos/hypium/src/main/module/assert/assertNull.js new file mode 100644 index 0000000..32523dd --- /dev/null +++ b/oh_modules/@ohos/hypium/src/main/module/assert/assertNull.js @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2021-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. + */ + +function assertNull(actualValue) { + return { + pass: (actualValue) === null, + message: 'expect null, actualValue is ' + (actualValue) + }; +} + +export default assertNull; diff --git a/oh_modules/@ohos/hypium/src/main/module/assert/assertPosUnlimited.js b/oh_modules/@ohos/hypium/src/main/module/assert/assertPosUnlimited.js new file mode 100644 index 0000000..43a5a0f --- /dev/null +++ b/oh_modules/@ohos/hypium/src/main/module/assert/assertPosUnlimited.js @@ -0,0 +1,23 @@ +/* +* 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. +*/ + +function assertPosUnlimited(actualValue) { + return { + pass: actualValue === Number.POSITIVE_INFINITY, + message: 'Expected actualValue is POSITIVE_INFINITY. actualValue is,' + actualValue + }; +} + +export default assertPosUnlimited;