diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..26d33521af10bcc7fd8cea344038eaaeb78d0ef5 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,3 @@ +# Default ignored files +/shelf/ +/workspace.xml diff --git a/.idea/applications_app_samples_1113.iml b/.idea/applications_app_samples_1113.iml new file mode 100644 index 0000000000000000000000000000000000000000..61021940449bdc69db005bbd04a05380f5629b2c --- /dev/null +++ b/.idea/applications_app_samples_1113.iml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000000000000000000000000000000000000..d037de81fc6b1ee57025cf2e56223d3221ca5ed6 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000000000000000000000000000000000000..35eb1ddfbbc029bcab630581847471d7f238ec53 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/code/.idea/code.iml b/code/.idea/code.iml new file mode 100644 index 0000000000000000000000000000000000000000..61021940449bdc69db005bbd04a05380f5629b2c --- /dev/null +++ b/code/.idea/code.iml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/code/.idea/modules.xml b/code/.idea/modules.xml new file mode 100644 index 0000000000000000000000000000000000000000..23968dc678111b184a2091404b0a05ec67a7b905 --- /dev/null +++ b/code/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/code/.idea/vcs.xml b/code/.idea/vcs.xml new file mode 100644 index 0000000000000000000000000000000000000000..6c0b8635858dc7ad44b93df54b762707ce49eefc --- /dev/null +++ b/code/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/code/.idea/workspace.xml b/code/.idea/workspace.xml new file mode 100644 index 0000000000000000000000000000000000000000..8e5313f467af971b2e64cd4bf08248b01a8c16b6 --- /dev/null +++ b/code/.idea/workspace.xml @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/code/UI/CalendarViewSwitch/.clang-format b/code/UI/CalendarViewSwitch/.clang-format new file mode 100644 index 0000000000000000000000000000000000000000..ec09519c8cec974898f804cc7e42ba16b8768c9d --- /dev/null +++ b/code/UI/CalendarViewSwitch/.clang-format @@ -0,0 +1,64 @@ +Language: Cpp +# BasedOnStyle: LLVM +ColumnLimit: 120 +SortIncludes: false +TabWidth: 4 +IndentWidth: 4 +UseTab: Never +AccessModifierOffset: -4 +ContinuationIndentWidth: 4 +IndentCaseBlocks: false +IndentCaseLabels: false +IndentGotoLabels: true +IndentWrappedFunctionNames: false +SortUsingDeclarations: false +NamespaceIndentation: None +SpaceAfterCStyleCast: false +SpaceAfterLogicalNot: false +SpaceAfterTemplateKeyword: true +SpaceBeforeAssignmentOperators: true +SpaceBeforeCaseColon: false +SpaceBeforeCpp11BracedList: false +SpaceBeforeCtorInitializerColon: true +SpaceBeforeInheritanceColon: true +SpaceBeforeRangeBasedForLoopColon: true +SpaceBeforeSquareBrackets: false +SpaceInEmptyBlock: false +SpaceInEmptyParentheses: false +SpacesInAngles: false +SpacesInCStyleCastParentheses: false +SpacesInConditionalStatement: false +SpacesInParentheses: false +SpacesInSquareBrackets: false +AlignTrailingComments: true +AlignAfterOpenBracket: true +AllowShortCaseLabelsOnASingleLine: false +AllowShortEnumsOnASingleLine: true +AllowShortFunctionsOnASingleLine: All +AllowShortIfStatementsOnASingleLine: Never +AllowShortLambdasOnASingleLine: All +AllowShortLoopsOnASingleLine: false +AlwaysBreakTemplateDeclarations: MultiLine +BinPackArguments: true +BinPackParameters: true +BreakBeforeTernaryOperators: true +BreakConstructorInitializers: BeforeColon +BreakInheritanceList: BeforeColon +BreakStringLiterals: true +InsertBraces: false +IndentExternBlock: NoIndent +BreakBeforeBraces: Custom +BraceWrapping: + AfterCaseLabel: false + AfterClass: false + AfterControlStatement: Never + AfterEnum: false + AfterFunction: false + AfterNamespace: false + AfterStruct: false + AfterUnion: false + AfterExternBlock: false + BeforeCatch: false + BeforeElse: false +ReflowComments: true +MaxEmptyLinesToKeep: 2 \ No newline at end of file diff --git a/code/UI/CalendarViewSwitch/.hvigor/cache/meta.json b/code/UI/CalendarViewSwitch/.hvigor/cache/meta.json new file mode 100644 index 0000000000000000000000000000000000000000..bafad42b1135e5d353c582e44da94cd966397f2e --- /dev/null +++ b/code/UI/CalendarViewSwitch/.hvigor/cache/meta.json @@ -0,0 +1 @@ +{"compileSdkVersion":12,"hvigorVersion":"5.8.5","toolChainsVersion":"5.0.0.71"} diff --git a/code/UI/CalendarViewSwitch/.hvigor/dependencyMap/calendarswitch/oh-package.json5 b/code/UI/CalendarViewSwitch/.hvigor/dependencyMap/calendarswitch/oh-package.json5 new file mode 100644 index 0000000000000000000000000000000000000000..20fd4fcfb33a3b65cf1937d677d8cdc3a9a3a7d4 --- /dev/null +++ b/code/UI/CalendarViewSwitch/.hvigor/dependencyMap/calendarswitch/oh-package.json5 @@ -0,0 +1 @@ +{"name":"calendarswitch","version":"1.0.0","description":"Please describe the basic information.","main":"Index.ets","author":"","license":"Apache-2.0","dependencies":{}} \ No newline at end of file diff --git a/code/UI/CalendarViewSwitch/.hvigor/dependencyMap/dependencyMap.json5 b/code/UI/CalendarViewSwitch/.hvigor/dependencyMap/dependencyMap.json5 new file mode 100644 index 0000000000000000000000000000000000000000..f18aa83dd8517e4ba2f9332f47aba89e674d228e --- /dev/null +++ b/code/UI/CalendarViewSwitch/.hvigor/dependencyMap/dependencyMap.json5 @@ -0,0 +1 @@ +{"basePath":"D:\\workCode\\fork_sample_new\\applications_app_samples_1113\\code\\UI\\CalendarViewSwitch\\.hvigor\\dependencyMap\\dependencyMap.json5","rootDependency":"./oh-package.json5","dependencyMap":{"entry":"./entry/oh-package.json5","calendarswitch":"./calendarswitch/oh-package.json5"},"modules":[{"name":"entry","srcPath":"..\\..\\..\\entry"},{"name":"calendarswitch","srcPath":"..\\..\\..\\casesfeature\\calendarswitch"}]} \ No newline at end of file diff --git a/code/UI/CalendarViewSwitch/.hvigor/dependencyMap/entry/oh-package.json5 b/code/UI/CalendarViewSwitch/.hvigor/dependencyMap/entry/oh-package.json5 new file mode 100644 index 0000000000000000000000000000000000000000..d560333cf8a9445e4ddfd181a9a12eec1e07a0b5 --- /dev/null +++ b/code/UI/CalendarViewSwitch/.hvigor/dependencyMap/entry/oh-package.json5 @@ -0,0 +1 @@ +{"name":"entry","version":"1.0.0","description":"Please describe the basic information.","main":"","author":"","license":"","dependencies":{"calendarswitch":"file:../casesfeature/calendarswitch"}} \ No newline at end of file diff --git a/code/UI/CalendarViewSwitch/.hvigor/dependencyMap/oh-package.json5 b/code/UI/CalendarViewSwitch/.hvigor/dependencyMap/oh-package.json5 new file mode 100644 index 0000000000000000000000000000000000000000..0771193154a032440129db0f508365b10d04d731 --- /dev/null +++ b/code/UI/CalendarViewSwitch/.hvigor/dependencyMap/oh-package.json5 @@ -0,0 +1 @@ +{"modelVersion":"5.0.0","description":"Please describe the basic information.","dependencies":{},"devDependencies":{"@ohos/hypium":"1.0.19"}} \ No newline at end of file diff --git a/code/UI/CalendarViewSwitch/.hvigor/outputs/build-logs/build.log b/code/UI/CalendarViewSwitch/.hvigor/outputs/build-logs/build.log new file mode 100644 index 0000000000000000000000000000000000000000..4bf711bfd22a2730d5dd13eadeddbe5f3bb3dc28 --- /dev/null +++ b/code/UI/CalendarViewSwitch/.hvigor/outputs/build-logs/build.log @@ -0,0 +1,218 @@ +[2024-12-06T15:51:02.756] [DEBUG] debug-file - env: nodejsVersion=v18.20.1 +[2024-12-06T15:51:03.275] [DEBUG] debug-file - Hvigor init with startParameters:{ + hvigorfileTypeCheck: false, + parallelExecution: true, + incrementalExecution: true, + printStackTrace: false, + daemon: false, + analyze: 0, + logLevel: Level { level: 20000, levelStr: 'INFO', colour: 'green' } +} +[2024-12-06T15:51:03.300] [DEBUG] debug-file - hvigorfile, resolving D:\workCode\fork_sample_new\applications_app_samples_1113\code\UI\CalendarViewSwitch\hvigorfile.ts +[2024-12-06T15:51:05.273] [DEBUG] debug-file - hvigorfile, require result: { default: { system: [Function: appTasks], plugins: [] } } +[2024-12-06T15:51:05.481] [DEBUG] debug-file - Start initialize project's product build option map with build mode debug. +[2024-12-06T15:51:05.487] [DEBUG] debug-file - Product 'default' using build option: { + "debuggable": true, + "strictMode": { + "caseSensitiveCheck": true, + "useNormalizedOHMUrl": true + } +} in this build. +[2024-12-06T15:51:05.516] [DEBUG] debug-file - Start recording SDK configuration permission data. +[2024-12-06T15:51:05.594] [DEBUG] debug-file - Project task initialization takes 42 ms +[2024-12-06T15:51:05.610] [DEBUG] debug-file - hvigorfile, resolving D:\workCode\fork_sample_new\applications_app_samples_1113\code\UI\CalendarViewSwitch\entry\hvigorfile.ts +[2024-12-06T15:51:05.657] [DEBUG] debug-file - Module entry Collected Dependency: D:\workCode\fork_sample_new\applications_app_samples_1113\code\UI\CalendarViewSwitch\casesfeature\calendarswitch +[2024-12-06T15:51:05.677] [DEBUG] debug-file - Module entry task initialization takes 6 ms +[2024-12-06T15:51:05.694] [DEBUG] debug-file - hvigorfile, resolving D:\workCode\fork_sample_new\applications_app_samples_1113\code\UI\CalendarViewSwitch\casesfeature\calendarswitch\hvigorfile.ts +[2024-12-06T15:51:05.774] [DEBUG] debug-file - Start initialize module-target build option map, moduleName=calendarswitch, buildMode=debug +[2024-12-06T15:51:05.779] [DEBUG] debug-file - Module calendarswitch task initialization takes 2 ms +[2024-12-06T15:51:05.784] [DEBUG] debug-file - project has submodules:entry,calendarswitch +[2024-12-06T15:51:05.821] [DEBUG] debug-file - Configuration task cost before running: 2 s 578 ms +[2024-12-06T15:51:05.826] [DEBUG] debug-file - Executing task :entry:clean +[2024-12-06T15:51:05.830] [DEBUG] debug-file - entry : clean cost memory 0.25566864013671875 +[2024-12-06T15:51:05.836] [DEBUG] debug-file - Executing task :calendarswitch:clean +[2024-12-06T15:51:05.842] [DEBUG] debug-file - calendarswitch : clean cost memory 0.25311279296875 +[2024-12-06T15:51:05.863] [DEBUG] debug-file - Module 'entry' target 'default' using build option: { + "debuggable": true, + "copyFrom": "default", + "strictMode": { + "caseSensitiveCheck": true, + "useNormalizedOHMUrl": true + }, + "name": "debug" +} in this build. +[2024-12-06T15:51:02.758] [DEBUG] debug-file - env: hvigor-config.json5 content = { + modelVersion: '5.0.0', + dependencies: {}, + execution: {}, + logging: {}, + debugging: {}, + nodeOptions: {} +} +[2024-12-06T15:51:03.277] [DEBUG] debug-file - Since current hvigor version 5.8.5 differs from last hvigor version + undefined, delete file-cache.json and task-cache.json. +[2024-12-06T15:51:05.897] [DEBUG] debug-file - Create resident worker with id: 0. +[2024-12-06T15:51:05.273] [DEBUG] debug-file - hvigorfile, binding system plugins [Function: appTasks] +[2024-12-06T15:51:05.481] [DEBUG] debug-file - Picking option from product 'default' with build mode 'debug'. +[2024-12-06T15:51:05.497] [DEBUG] debug-file - not found resModel json file in : D:\workCode\fork_sample_new\applications_app_samples_1113\code\UI\CalendarViewSwitch\casesfeature\calendarswitch\src\ohosTest\module.json5 +[2024-12-06T15:51:05.541] [DEBUG] debug-file - Sdk init in 41 ms +[2024-12-06T15:51:05.594] [DEBUG] debug-file - hvigorfile, binding custom plugins [] +[2024-12-06T15:51:05.618] [DEBUG] debug-file - hvigorfile, require result: { default: { system: [Function: hapTasks], plugins: [] } } +[2024-12-06T15:51:05.657] [DEBUG] debug-file - Module entry's total dependency: 1 +[2024-12-06T15:51:05.677] [DEBUG] debug-file - hvigorfile, binding custom plugins [] +[2024-12-06T15:51:05.700] [DEBUG] debug-file - hvigorfile, require result: { default: { system: [Function: harTasks], plugins: [] } } +[2024-12-06T15:51:05.774] [DEBUG] debug-file - Target 'default' config: {} +[2024-12-06T15:51:05.780] [DEBUG] debug-file - hvigorfile, binding custom plugins [] +[2024-12-06T15:51:05.784] [DEBUG] debug-file - module:calendarswitch no need to execute packageHap +[2024-12-06T15:51:05.826] [DEBUG] debug-file - clean: Worker pool is inactive. +[2024-12-06T15:51:05.831] [DEBUG] debug-file - runTaskFromQueue task cost before running: 2 s 588 ms +[2024-12-06T15:51:05.836] [DEBUG] debug-file - clean: Worker pool is inactive. +[2024-12-06T15:51:05.842] [DEBUG] debug-file - runTaskFromQueue task cost before running: 2 s 599 ms +[2024-12-06T15:51:05.869] [DEBUG] debug-file - Module 'entry' target 'ohosTest' using build option: { + "debuggable": true, + "strictMode": { + "caseSensitiveCheck": true, + "useNormalizedOHMUrl": true + }, + "name": "default" +} in this build. +[2024-12-06T15:51:02.760] [DEBUG] debug-file - env: daemon=false +[2024-12-06T15:51:03.278] [DEBUG] debug-file - Cache service initialization finished in 2 ms +[2024-12-06T15:51:05.900] [DEBUG] debug-file - Create resident worker with id: 1. +[2024-12-06T15:51:05.481] [DEBUG] debug-file - Product 'default' build option: {} +[2024-12-06T15:51:05.500] [DEBUG] debug-file - No signingConfig found, initRemoteHspCache failed. +[2024-12-06T15:51:05.594] [DEBUG] debug-file - hvigorfile, no custom plugins were found in D:\workCode\fork_sample_new\applications_app_samples_1113\code\UI\CalendarViewSwitch\hvigorfile.ts +[2024-12-06T15:51:05.618] [DEBUG] debug-file - hvigorfile, binding system plugins [Function: hapTasks] +[2024-12-06T15:51:05.661] [DEBUG] debug-file - Start initialize module-target build option map, moduleName=entry, buildMode=debug +[2024-12-06T15:51:05.677] [DEBUG] debug-file - hvigorfile, no custom plugins were found in D:\workCode\fork_sample_new\applications_app_samples_1113\code\UI\CalendarViewSwitch\entry\hvigorfile.ts +[2024-12-06T15:51:05.701] [DEBUG] debug-file - hvigorfile, binding system plugins [Function: harTasks] +[2024-12-06T15:51:05.774] [DEBUG] debug-file - Target 'ohosTest' config: {} +[2024-12-06T15:51:05.780] [DEBUG] debug-file - hvigorfile, no custom plugins were found in D:\workCode\fork_sample_new\applications_app_samples_1113\code\UI\CalendarViewSwitch\casesfeature\calendarswitch\hvigorfile.ts +[2024-12-06T15:51:05.786] [DEBUG] debug-file - start to load updatedOhPackageInfo to the disk +[2024-12-06T15:51:05.832] [INFO] debug-file - Finished :entry:clean... after 5 ms +[2024-12-06T15:51:05.843] [INFO] debug-file - Finished :calendarswitch:clean... after 7 ms +[2024-12-06T15:51:05.876] [DEBUG] debug-file - Module 'calendarswitch' target 'default' using build option: { + "debuggable": true, + "copyFrom": "default", + "strictMode": { + "caseSensitiveCheck": true, + "useNormalizedOHMUrl": true + }, + "name": "debug" +} in this build. +[2024-12-06T15:51:02.760] [DEBUG] debug-file - no-daemon, use the parent process.execArgv --max-old-space-size=8192,--expose-gc +[2024-12-06T15:51:05.902] [DEBUG] debug-file - Current worker pool is stopped or closed. +[2024-12-06T15:51:05.481] [DEBUG] debug-file - End initialize project's product build option map with build mode 'debug'. +[2024-12-06T15:51:05.594] [DEBUG] debug-file - hvigorfile, resolve finished D:\workCode\fork_sample_new\applications_app_samples_1113\code\UI\CalendarViewSwitch\hvigorfile.ts +[2024-12-06T15:51:05.662] [DEBUG] debug-file - Target 'default' config: {} +[2024-12-06T15:51:05.677] [DEBUG] debug-file - hvigorfile, resolve finished D:\workCode\fork_sample_new\applications_app_samples_1113\code\UI\CalendarViewSwitch\entry\hvigorfile.ts +[2024-12-06T15:51:05.775] [DEBUG] debug-file - Module 'calendarswitch' target 'default' build option: { + "debuggable": true, + "copyFrom": "default", + "strictMode": { + "caseSensitiveCheck": true, + "useNormalizedOHMUrl": true + }, + "name": "debug" +} +[2024-12-06T15:51:05.780] [DEBUG] debug-file - hvigorfile, resolve finished D:\workCode\fork_sample_new\applications_app_samples_1113\code\UI\CalendarViewSwitch\casesfeature\calendarswitch\hvigorfile.ts +[2024-12-06T15:51:05.793] [DEBUG] debug-file - load to the disk finished +[2024-12-06T15:51:05.843] [DEBUG] debug-file - Executing task ::clean +[2024-12-06T15:51:05.882] [DEBUG] debug-file - hvigor start cli arguments:{ + prop: [ 'product=default' ], + sync: true, + parallel: true, + incremental: true, + daemon: false, + _: [], + analyze: 'normal' +} +[2024-12-06T15:51:05.903] [DEBUG] debug-file - Clear worker 0. +[2024-12-06T15:51:05.662] [DEBUG] debug-file - Target 'ohosTest' config: {} +[2024-12-06T15:51:05.775] [DEBUG] debug-file - Module 'calendarswitch' target 'ohosTest' build option: { + "debuggable": true, + "strictMode": { + "caseSensitiveCheck": true, + "useNormalizedOHMUrl": true + }, + "name": "default" +} +[2024-12-06T15:51:05.797] [DEBUG] debug-file - Module CalendarViewSwitch Collected Dependency: +[2024-12-06T15:51:05.843] [DEBUG] debug-file - clean: Worker pool is inactive. +[2024-12-06T15:51:05.895] [DEBUG] debug-file - Since there is no instance or instance is terminated, create a new worker pool. +[2024-12-06T15:51:05.903] [DEBUG] debug-file - Worker 0 has been cleared. +[2024-12-06T15:51:05.663] [DEBUG] debug-file - Module 'entry' target 'default' build option: { + "debuggable": true, + "copyFrom": "default", + "strictMode": { + "caseSensitiveCheck": true, + "useNormalizedOHMUrl": true + }, + "name": "debug" +} +[2024-12-06T15:51:05.775] [DEBUG] debug-file - End initialize module-target build option map, moduleName=calendarswitch +[2024-12-06T15:51:05.798] [DEBUG] debug-file - Module CalendarViewSwitch's total dependency: 0 +[2024-12-06T15:51:05.844] [DEBUG] debug-file - CalendarViewSwitch : clean cost memory 0.03330230712890625 +[2024-12-06T15:51:05.895] [DEBUG] debug-file - Worker pool is initialized with config: { + minPoolNum: 2, + maxPoolNum: undefined, + maxCoreSize: undefined, + cacheCapacity: undefined, + cacheTtl: undefined +} +[2024-12-06T15:51:05.903] [DEBUG] debug-file - Current idle worker size: 1. +[2024-12-06T15:51:05.663] [DEBUG] debug-file - Module 'entry' target 'ohosTest' build option: { + "debuggable": true, + "strictMode": { + "caseSensitiveCheck": true, + "useNormalizedOHMUrl": true + }, + "name": "default" +} +[2024-12-06T15:51:05.775] [DEBUG] debug-file - Module 'calendarswitch' target 'default' using build option: { + "debuggable": true, + "copyFrom": "default", + "strictMode": { + "caseSensitiveCheck": true, + "useNormalizedOHMUrl": true + }, + "name": "debug" +} in this build. +[2024-12-06T15:51:05.803] [DEBUG] debug-file - Module entry Collected Dependency: D:\workCode\fork_sample_new\applications_app_samples_1113\code\UI\CalendarViewSwitch\casesfeature\calendarswitch +[2024-12-06T15:51:05.844] [DEBUG] debug-file - runTaskFromQueue task cost before running: 2 s 601 ms +[2024-12-06T15:51:05.903] [DEBUG] debug-file - Current busy worker size: 0. +[2024-12-06T15:51:05.663] [DEBUG] debug-file - End initialize module-target build option map, moduleName=entry +[2024-12-06T15:51:05.803] [DEBUG] debug-file - Module entry's total dependency: 1 +[2024-12-06T15:51:05.844] [INFO] debug-file - Finished ::clean... after 2 ms +[2024-12-06T15:51:05.903] [DEBUG] debug-file - Clear worker 1. +[2024-12-06T15:51:05.663] [DEBUG] debug-file - Module 'entry' target 'default' using build option: { + "debuggable": true, + "copyFrom": "default", + "strictMode": { + "caseSensitiveCheck": true, + "useNormalizedOHMUrl": true + }, + "name": "debug" +} in this build. +[2024-12-06T15:51:05.812] [DEBUG] debug-file - Module calendarswitch Collected Dependency: +[2024-12-06T15:51:05.844] [DEBUG] debug-file - Executing task :entry:init +[2024-12-06T15:51:05.903] [DEBUG] debug-file - Worker 1 has been cleared. +[2024-12-06T15:51:05.812] [DEBUG] debug-file - Module calendarswitch's total dependency: 0 +[2024-12-06T15:51:05.845] [DEBUG] debug-file - entry : init cost memory 0.01015472412109375 +[2024-12-06T15:51:05.903] [DEBUG] debug-file - Current idle worker size: 0. +[2024-12-06T15:51:05.816] [DEBUG] debug-file - Configuration phase cost:2 s 529 ms +[2024-12-06T15:51:05.845] [DEBUG] debug-file - runTaskFromQueue task cost before running: 2 s 602 ms +[2024-12-06T15:51:05.903] [DEBUG] debug-file - Current busy worker size: 0. +[2024-12-06T15:51:05.845] [INFO] debug-file - Finished :entry:init... after 1 ms +[2024-12-06T15:51:05.904] [DEBUG] debug-file - hvigor build process will be closed. +[2024-12-06T15:51:05.845] [DEBUG] debug-file - Executing task :calendarswitch:init +[2024-12-06T15:51:05.911] [DEBUG] debug-file - worker[0] exits with exit code 0. +[2024-12-06T15:51:05.845] [DEBUG] debug-file - calendarswitch : init cost memory 0.008758544921875 +[2024-12-06T15:51:05.911] [DEBUG] debug-file - worker[1] exits with exit code 0. +[2024-12-06T15:51:05.845] [DEBUG] debug-file - runTaskFromQueue task cost before running: 2 s 602 ms +[2024-12-06T15:51:05.912] [DEBUG] debug-file - Current worker pool is terminated. +[2024-12-06T15:51:05.846] [INFO] debug-file - Finished :calendarswitch:init... after 1 ms +[2024-12-06T15:51:05.846] [DEBUG] debug-file - Executing task ::init +[2024-12-06T15:51:05.846] [DEBUG] debug-file - CalendarViewSwitch : init cost memory 0.00907135009765625 +[2024-12-06T15:51:05.846] [DEBUG] debug-file - runTaskFromQueue task cost before running: 2 s 603 ms +[2024-12-06T15:51:05.846] [INFO] debug-file - Finished ::init... after 1 ms diff --git a/code/UI/CalendarViewSwitch/.hvigor/outputs/sync/fileCache.json b/code/UI/CalendarViewSwitch/.hvigor/outputs/sync/fileCache.json new file mode 100644 index 0000000000000000000000000000000000000000..c19f3e6805ca49eb2489bb419cbecd84b41e476e --- /dev/null +++ b/code/UI/CalendarViewSwitch/.hvigor/outputs/sync/fileCache.json @@ -0,0 +1 @@ +{"CACHE_SYNC_FILE_HASH":{"D:\\workCode\\fork_sample_new\\applications_app_samples_1113\\code\\UI\\CalendarViewSwitch\\oh-package.json5":"dc7cf5ffd44bf376f99ba4a04c54815f27e282f64065008827bc6056632496a6","D:\\workCode\\fork_sample_new\\applications_app_samples_1113\\code\\UI\\CalendarViewSwitch\\hvigorfile.ts":"65b23ad0a57c7c337e78b49dc05aacd922bbc5cd389e1319086bd78f310dceec","D:\\workCode\\fork_sample_new\\applications_app_samples_1113\\code\\UI\\CalendarViewSwitch\\.hvigor\\outputs\\sync\\output.json":"91de05da402a9ccf0b50595f06a2e59d60438dc1916195b36040731fbd24f356","D:\\workCode\\fork_sample_new\\applications_app_samples_1113\\code\\UI\\CalendarViewSwitch\\build-profile.json5":"0356ef0d513af197c76ad6dea6541c12a3cd927c3dbaf0bb4d8522791a6f0882","D:\\workCode\\fork_sample_new\\applications_app_samples_1113\\code\\UI\\CalendarViewSwitch\\entry\\build-profile.json5":"f787911f60f7a6a0127f7ba4afb1ab41b3393089ae688c1d8fb609c4a96f420e","D:\\workCode\\fork_sample_new\\applications_app_samples_1113\\code\\UI\\CalendarViewSwitch\\entry\\oh-package.json5":"beaa3cbc604d327ee274fc3bc528fe0c9af27dcc010aa9310acdaa94f6a9cc6d","D:\\workCode\\fork_sample_new\\applications_app_samples_1113\\code\\UI\\CalendarViewSwitch\\casesfeature\\calendarswitch\\build-profile.json5":"194607b22ab18197069a56313421404c8a521c367f8a85a500666bc0c683ea4c","D:\\workCode\\fork_sample_new\\applications_app_samples_1113\\code\\UI\\CalendarViewSwitch\\hvigor\\hvigor-config.json5":"f33e11017379ff5bd9694b1aa3821c8c896886cc7a27938b067b9a683c3f9d20","D:\\workCode\\fork_sample_new\\applications_app_samples_1113\\code\\UI\\CalendarViewSwitch\\entry\\hvigorfile.ts":"be07317cab65913c70b9d814784b6cd61fca38b1beb520e6d61d80ed79487dab","SDK_LOCATION":"D:/work_software2/IDE/devecostudio_5.0.3.900/DevEco Studio/sdk","D:\\workCode\\fork_sample_new\\applications_app_samples_1113\\code\\UI\\CalendarViewSwitch\\casesfeature\\calendarswitch\\hvigorfile.ts":"3aa9e75758e7ea387b001285e4b05fa1e5c67f1bb1e54284d13ee4b353ed6a12","D:\\workCode\\fork_sample_new\\applications_app_samples_1113\\code\\UI\\CalendarViewSwitch\\casesfeature\\calendarswitch\\oh-package.json5":"206762e8f23d61064a45900f9a0c155ff59f46f42e671d0fbc78f503bb8dab07"},"OHPM_INSTALL_FILE_HASH":{"D:\\workCode\\fork_sample_new\\applications_app_samples_1113\\code\\UI\\CalendarViewSwitch\\oh-package.json5":"dc7cf5ffd44bf376f99ba4a04c54815f27e282f64065008827bc6056632496a6","D:\\workCode\\fork_sample_new\\applications_app_samples_1113\\code\\UI\\CalendarViewSwitch\\entry\\oh-package.json5":"beaa3cbc604d327ee274fc3bc528fe0c9af27dcc010aa9310acdaa94f6a9cc6d","D:\\workCode\\fork_sample_new\\applications_app_samples_1113\\code\\UI\\CalendarViewSwitch\\entry\\oh_modules":true,"D:\\workCode\\fork_sample_new\\applications_app_samples_1113\\code\\UI\\CalendarViewSwitch\\oh_modules":true,"D:\\workCode\\fork_sample_new\\applications_app_samples_1113\\code\\UI\\CalendarViewSwitch\\casesfeature\\calendarswitch\\oh_modules":false,"D:\\workCode\\fork_sample_new\\applications_app_samples_1113\\code\\UI\\CalendarViewSwitch\\casesfeature\\calendarswitch\\oh-package.json5":"206762e8f23d61064a45900f9a0c155ff59f46f42e671d0fbc78f503bb8dab07"}} \ No newline at end of file diff --git a/code/UI/CalendarViewSwitch/.hvigor/outputs/sync/output.json b/code/UI/CalendarViewSwitch/.hvigor/outputs/sync/output.json new file mode 100644 index 0000000000000000000000000000000000000000..8f9d79b0d0711526ccc317df9887520b6b4f73db --- /dev/null +++ b/code/UI/CalendarViewSwitch/.hvigor/outputs/sync/output.json @@ -0,0 +1,258 @@ +{ + "ohos-module-entry": { + "SELECT_TARGET": "default", + "MODULE_BUILD_DIR": "D:\\workCode\\fork_sample_new\\applications_app_samples_1113\\code\\UI\\CalendarViewSwitch\\entry\\build", + "DEPENDENCY_INFO": { + "calendarswitch": "D:\\workCode\\fork_sample_new\\applications_app_samples_1113\\code\\UI\\CalendarViewSwitch\\casesfeature\\calendarswitch" + }, + "TARGETS": { + "default": { + "SOURCE_ROOT": "D:\\workCode\\fork_sample_new\\applications_app_samples_1113\\code\\UI\\CalendarViewSwitch\\entry\\src\\main", + "RESOURCES_PATH": [ + "D:\\workCode\\fork_sample_new\\applications_app_samples_1113\\code\\UI\\CalendarViewSwitch\\entry\\src\\main\\resources" + ], + "BUILD_PATH": { + "OUTPUT_PATH": "D:\\workCode\\fork_sample_new\\applications_app_samples_1113\\code\\UI\\CalendarViewSwitch\\entry\\build\\default\\outputs\\default", + "INTERMEDIA_PATH": "D:\\workCode\\fork_sample_new\\applications_app_samples_1113\\code\\UI\\CalendarViewSwitch\\entry\\build\\default\\intermediates", + "JS_ASSETS_PATH": "D:\\workCode\\fork_sample_new\\applications_app_samples_1113\\code\\UI\\CalendarViewSwitch\\entry\\build\\default\\intermediates\\loader_out\\default", + "JS_LITE_ASSETS_PATH": "D:\\workCode\\fork_sample_new\\applications_app_samples_1113\\code\\UI\\CalendarViewSwitch\\entry\\build\\default\\intermediates\\loader_out_lite\\default", + "RES_PATH": "D:\\workCode\\fork_sample_new\\applications_app_samples_1113\\code\\UI\\CalendarViewSwitch\\entry\\build\\default\\intermediates\\res\\default", + "RES_PROFILE_PATH": "D:\\workCode\\fork_sample_new\\applications_app_samples_1113\\code\\UI\\CalendarViewSwitch\\entry\\build\\default\\intermediates\\res\\default\\resources\\base\\profile", + "ETS_SUPER_VISUAL_PATH": "D:\\workCode\\fork_sample_new\\applications_app_samples_1113\\code\\UI\\CalendarViewSwitch\\entry\\build\\default\\cache\\default\\default@CompileArkTS\\esmodule", + "JS_SUPER_VISUAL_PATH": "D:\\workCode\\fork_sample_new\\applications_app_samples_1113\\code\\UI\\CalendarViewSwitch\\entry\\build\\default\\cache\\default\\default@CompileJS\\jsbundle", + "WORKER_LOADER": "D:\\workCode\\fork_sample_new\\applications_app_samples_1113\\code\\UI\\CalendarViewSwitch\\entry\\build\\default\\intermediates\\loader\\default\\loader.json", + "MANIFEST_JSON": "D:\\workCode\\fork_sample_new\\applications_app_samples_1113\\code\\UI\\CalendarViewSwitch\\entry\\build\\default\\intermediates\\manifest\\default", + "OUTPUT_METADATA_JSON": "D:\\workCode\\fork_sample_new\\applications_app_samples_1113\\code\\UI\\CalendarViewSwitch\\entry\\build\\default\\intermediates\\hap_metadata\\default\\output_metadata.json" + }, + "BUILD_OPTION": { + "debuggable": true + } + }, + "ohosTest": { + "SOURCE_ROOT": "D:\\workCode\\fork_sample_new\\applications_app_samples_1113\\code\\UI\\CalendarViewSwitch\\entry\\src\\ohosTest", + "RESOURCES_PATH": [ + "D:\\workCode\\fork_sample_new\\applications_app_samples_1113\\code\\UI\\CalendarViewSwitch\\entry\\src\\ohosTest\\resources" + ], + "BUILD_PATH": { + "OUTPUT_PATH": "D:\\workCode\\fork_sample_new\\applications_app_samples_1113\\code\\UI\\CalendarViewSwitch\\entry\\build\\default\\outputs\\ohosTest", + "INTERMEDIA_PATH": "D:\\workCode\\fork_sample_new\\applications_app_samples_1113\\code\\UI\\CalendarViewSwitch\\entry\\build\\default\\intermediates", + "JS_ASSETS_PATH": "D:\\workCode\\fork_sample_new\\applications_app_samples_1113\\code\\UI\\CalendarViewSwitch\\entry\\build\\default\\intermediates\\loader_out\\ohosTest", + "JS_LITE_ASSETS_PATH": "D:\\workCode\\fork_sample_new\\applications_app_samples_1113\\code\\UI\\CalendarViewSwitch\\entry\\build\\default\\intermediates\\loader_out_lite\\ohosTest", + "RES_PATH": "D:\\workCode\\fork_sample_new\\applications_app_samples_1113\\code\\UI\\CalendarViewSwitch\\entry\\build\\default\\intermediates\\res\\ohosTest", + "RES_PROFILE_PATH": "D:\\workCode\\fork_sample_new\\applications_app_samples_1113\\code\\UI\\CalendarViewSwitch\\entry\\build\\default\\intermediates\\res\\ohosTest\\resources\\base\\profile", + "ETS_SUPER_VISUAL_PATH": "D:\\workCode\\fork_sample_new\\applications_app_samples_1113\\code\\UI\\CalendarViewSwitch\\entry\\build\\default\\cache\\ohosTest\\ohosTest@OhosTestCompileArkTS\\esmodule", + "JS_SUPER_VISUAL_PATH": "D:\\workCode\\fork_sample_new\\applications_app_samples_1113\\code\\UI\\CalendarViewSwitch\\entry\\build\\default\\cache\\ohosTest\\ohosTest@OhosTestCompileJS\\jsbundle", + "WORKER_LOADER": "D:\\workCode\\fork_sample_new\\applications_app_samples_1113\\code\\UI\\CalendarViewSwitch\\entry\\build\\default\\intermediates\\loader\\ohosTest\\loader.json", + "MANIFEST_JSON": "D:\\workCode\\fork_sample_new\\applications_app_samples_1113\\code\\UI\\CalendarViewSwitch\\entry\\build\\default\\intermediates\\manifest\\ohosTest", + "OUTPUT_METADATA_JSON": "D:\\workCode\\fork_sample_new\\applications_app_samples_1113\\code\\UI\\CalendarViewSwitch\\entry\\build\\default\\intermediates\\hap_metadata\\ohosTest\\output_metadata.json" + }, + "BUILD_OPTION": { + "debuggable": true + } + } + }, + "BUILD_OPTION": { + "default-default": { + "debuggable": true, + "copyFrom": "default", + "strictMode": { + "caseSensitiveCheck": true, + "useNormalizedOHMUrl": true + }, + "name": "debug" + } + }, + "BUILD_PROFILE_OPT": { + "apiType": "stageMode", + "buildOption": {}, + "buildOptionSet": [ + { + "name": "release", + "arkOptions": { + "obfuscation": { + "ruleOptions": { + "enable": false, + "files": [ + "./obfuscation-rules.txt" + ] + } + } + } + }, + { + "name": "default" + }, + { + "name": "debug" + } + ], + "targets": [ + { + "name": "default" + }, + { + "name": "ohosTest" + } + ] + } + }, + "ohos-module-calendarswitch": { + "SELECT_TARGET": "default", + "MODULE_BUILD_DIR": "D:\\workCode\\fork_sample_new\\applications_app_samples_1113\\code\\UI\\CalendarViewSwitch\\casesfeature\\calendarswitch\\build", + "DEPENDENCY_INFO": {}, + "TARGETS": { + "default": { + "SOURCE_ROOT": "D:\\workCode\\fork_sample_new\\applications_app_samples_1113\\code\\UI\\CalendarViewSwitch\\casesfeature\\calendarswitch\\src\\main", + "RESOURCES_PATH": [ + "D:\\workCode\\fork_sample_new\\applications_app_samples_1113\\code\\UI\\CalendarViewSwitch\\casesfeature\\calendarswitch\\src\\main\\resources" + ], + "BUILD_PATH": { + "OUTPUT_PATH": "D:\\workCode\\fork_sample_new\\applications_app_samples_1113\\code\\UI\\CalendarViewSwitch\\casesfeature\\calendarswitch\\build\\default\\outputs\\default", + "INTERMEDIA_PATH": "D:\\workCode\\fork_sample_new\\applications_app_samples_1113\\code\\UI\\CalendarViewSwitch\\casesfeature\\calendarswitch\\build\\default\\intermediates", + "JS_ASSETS_PATH": "D:\\workCode\\fork_sample_new\\applications_app_samples_1113\\code\\UI\\CalendarViewSwitch\\casesfeature\\calendarswitch\\build\\default\\intermediates\\loader_out\\default", + "JS_LITE_ASSETS_PATH": "D:\\workCode\\fork_sample_new\\applications_app_samples_1113\\code\\UI\\CalendarViewSwitch\\casesfeature\\calendarswitch\\build\\default\\intermediates\\loader_out_lite\\default", + "RES_PATH": "D:\\workCode\\fork_sample_new\\applications_app_samples_1113\\code\\UI\\CalendarViewSwitch\\casesfeature\\calendarswitch\\build\\default\\intermediates\\res\\default", + "RES_PROFILE_PATH": "D:\\workCode\\fork_sample_new\\applications_app_samples_1113\\code\\UI\\CalendarViewSwitch\\casesfeature\\calendarswitch\\build\\default\\intermediates\\res\\default\\resources\\base\\profile", + "ETS_SUPER_VISUAL_PATH": "D:\\workCode\\fork_sample_new\\applications_app_samples_1113\\code\\UI\\CalendarViewSwitch\\casesfeature\\calendarswitch\\build\\default\\cache\\default\\default@HarCompileArkTS\\esmodule", + "JS_SUPER_VISUAL_PATH": "D:\\workCode\\fork_sample_new\\applications_app_samples_1113\\code\\UI\\CalendarViewSwitch\\casesfeature\\calendarswitch\\build\\default\\cache\\default\\default@HarCompileJS\\jsbundle", + "WORKER_LOADER": "D:\\workCode\\fork_sample_new\\applications_app_samples_1113\\code\\UI\\CalendarViewSwitch\\casesfeature\\calendarswitch\\build\\default\\intermediates\\loader\\default\\loader.json", + "MANIFEST_JSON": "D:\\workCode\\fork_sample_new\\applications_app_samples_1113\\code\\UI\\CalendarViewSwitch\\casesfeature\\calendarswitch\\build\\default\\intermediates\\manifest\\default", + "OUTPUT_METADATA_JSON": "D:\\workCode\\fork_sample_new\\applications_app_samples_1113\\code\\UI\\CalendarViewSwitch\\casesfeature\\calendarswitch\\build\\default\\intermediates\\hap_metadata\\default\\output_metadata.json" + }, + "BUILD_OPTION": { + "debuggable": true + } + } + }, + "BUILD_OPTION": { + "default-default": { + "debuggable": true, + "copyFrom": "default", + "strictMode": { + "caseSensitiveCheck": true, + "useNormalizedOHMUrl": true + }, + "name": "debug" + } + }, + "BUILD_PROFILE_OPT": { + "apiType": "stageMode", + "buildOption": {}, + "buildOptionSet": [ + { + "name": "release", + "arkOptions": { + "obfuscation": { + "ruleOptions": { + "enable": false, + "files": [ + "./obfuscation-rules.txt" + ] + }, + "consumerFiles": [ + "./consumer-rules.txt" + ] + } + } + }, + { + "name": "default" + }, + { + "name": "debug" + } + ], + "targets": [ + { + "name": "default" + }, + { + "name": "ohosTest" + } + ] + } + }, + "ohos-project": { + "SELECT_PRODUCT_NAME": "default", + "MODULE_BUILD_DIR": "D:\\workCode\\fork_sample_new\\applications_app_samples_1113\\code\\UI\\CalendarViewSwitch\\build", + "BUNDLE_NAME": "com.samples.calendarviewswitch", + "BUILD_PATH": { + "OUTPUT_PATH": "D:\\workCode\\fork_sample_new\\applications_app_samples_1113\\code\\UI\\CalendarViewSwitch\\build\\outputs\\default" + }, + "MODULES": [ + { + "name": "entry", + "srcPath": "D:\\workCode\\fork_sample_new\\applications_app_samples_1113\\code\\UI\\CalendarViewSwitch\\entry", + "targets": [ + { + "name": "default", + "applyToProducts": [ + "default" + ] + } + ], + "belongProjectPath": "D:\\workCode\\fork_sample_new\\applications_app_samples_1113\\code\\UI\\CalendarViewSwitch" + }, + { + "name": "calendarswitch", + "srcPath": "D:\\workCode\\fork_sample_new\\applications_app_samples_1113\\code\\UI\\CalendarViewSwitch\\casesfeature\\calendarswitch", + "belongProjectPath": "D:\\workCode\\fork_sample_new\\applications_app_samples_1113\\code\\UI\\CalendarViewSwitch" + } + ], + "PROFILE_OPT": { + "app": { + "signingConfigs": [], + "products": [ + { + "name": "default", + "signingConfig": "default", + "compileSdkVersion": 12, + "compatibleSdkVersion": 12, + "runtimeOS": "OpenHarmony", + "buildOption": { + "strictMode": { + "caseSensitiveCheck": true, + "useNormalizedOHMUrl": true + } + } + } + ], + "buildModeSet": [ + { + "name": "debug" + }, + { + "name": "release" + } + ] + }, + "modules": [ + { + "name": "entry", + "srcPath": "./entry", + "targets": [ + { + "name": "default", + "applyToProducts": [ + "default" + ] + } + ] + }, + { + "name": "calendarswitch", + "srcPath": "./casesfeature/calendarswitch" + } + ] + }, + "CONFIG_PROPERTIES": { + "enableSignTask": true, + "skipNativeIncremental": false, + "hvigor.keepDependency": true + }, + "OVERALL_PROJECT_PATHS": [ + "D:\\workCode\\fork_sample_new\\applications_app_samples_1113\\code\\UI\\CalendarViewSwitch" + ] + }, + "version": 1 +} \ No newline at end of file diff --git a/code/UI/CalendarViewSwitch/.idea/.deveco/module/calendarswitch.cache.json b/code/UI/CalendarViewSwitch/.idea/.deveco/module/calendarswitch.cache.json new file mode 100644 index 0000000000000000000000000000000000000000..536a8c5e9c2d4e4baf89c655cc1a3e1669dc80db --- /dev/null +++ b/code/UI/CalendarViewSwitch/.idea/.deveco/module/calendarswitch.cache.json @@ -0,0 +1,21 @@ +{ + "BuildOptions":{ + "SELECT_BUILD_TARGET":"default", + "BUILD_PATH":{ + "OUTPUT_METADATA_JSON":"D:\\workCode\\fork_sample_new\\applications_app_samples_1113\\code\\UI\\CalendarViewSwitch\\casesfeature\\calendarswitch\\build\\default\\intermediates\\hap_metadata\\default\\output_metadata.json", + "OUTPUT_PATH":"D:\\workCode\\fork_sample_new\\applications_app_samples_1113\\code\\UI\\CalendarViewSwitch\\casesfeature\\calendarswitch\\build\\default\\outputs\\default", + "RES_PATH":"D:\\workCode\\fork_sample_new\\applications_app_samples_1113\\code\\UI\\CalendarViewSwitch\\casesfeature\\calendarswitch\\build\\default\\intermediates\\res\\default", + "ETS_SUPER_VISUAL_PATH":"D:\\workCode\\fork_sample_new\\applications_app_samples_1113\\code\\UI\\CalendarViewSwitch\\casesfeature\\calendarswitch\\build\\default\\cache\\default\\default@HarCompileArkTS\\esmodule", + "JS_ASSETS_PATH":"D:\\workCode\\fork_sample_new\\applications_app_samples_1113\\code\\UI\\CalendarViewSwitch\\casesfeature\\calendarswitch\\build\\default\\intermediates\\loader_out\\default", + "INTERMEDIA_PATH":"D:\\workCode\\fork_sample_new\\applications_app_samples_1113\\code\\UI\\CalendarViewSwitch\\casesfeature\\calendarswitch\\build\\default\\intermediates", + "RES_PROFILE_PATH":"D:\\workCode\\fork_sample_new\\applications_app_samples_1113\\code\\UI\\CalendarViewSwitch\\casesfeature\\calendarswitch\\build\\default\\intermediates\\res\\default\\resources\\base\\profile", + "WORKER_LOADER":"D:\\workCode\\fork_sample_new\\applications_app_samples_1113\\code\\UI\\CalendarViewSwitch\\casesfeature\\calendarswitch\\build\\default\\intermediates\\loader\\default\\loader.json", + "MANIFEST_JSON":"D:\\workCode\\fork_sample_new\\applications_app_samples_1113\\code\\UI\\CalendarViewSwitch\\casesfeature\\calendarswitch\\build\\default\\intermediates\\manifest\\default", + "JS_LITE_ASSETS_PATH":"D:\\workCode\\fork_sample_new\\applications_app_samples_1113\\code\\UI\\CalendarViewSwitch\\casesfeature\\calendarswitch\\build\\default\\intermediates\\loader_out_lite\\default", + "JS_SUPER_VISUAL_PATH":"D:\\workCode\\fork_sample_new\\applications_app_samples_1113\\code\\UI\\CalendarViewSwitch\\casesfeature\\calendarswitch\\build\\default\\cache\\default\\default@HarCompileJS\\jsbundle" + } + }, + "CommonInfo":{ + "current.select.target":"default" + } +} \ No newline at end of file diff --git a/code/UI/CalendarViewSwitch/.idea/.deveco/module/entry.cache.json b/code/UI/CalendarViewSwitch/.idea/.deveco/module/entry.cache.json new file mode 100644 index 0000000000000000000000000000000000000000..63e84c67dfb8bef4667c5fcb5b47210825719f0a --- /dev/null +++ b/code/UI/CalendarViewSwitch/.idea/.deveco/module/entry.cache.json @@ -0,0 +1,21 @@ +{ + "BuildOptions":{ + "SELECT_BUILD_TARGET":"default", + "BUILD_PATH":{ + "OUTPUT_METADATA_JSON":"D:\\workCode\\fork_sample_new\\applications_app_samples_1113\\code\\UI\\CalendarViewSwitch\\entry\\build\\default\\intermediates\\hap_metadata\\default\\output_metadata.json", + "OUTPUT_PATH":"D:\\workCode\\fork_sample_new\\applications_app_samples_1113\\code\\UI\\CalendarViewSwitch\\entry\\build\\default\\outputs\\default", + "RES_PATH":"D:\\workCode\\fork_sample_new\\applications_app_samples_1113\\code\\UI\\CalendarViewSwitch\\entry\\build\\default\\intermediates\\res\\default", + "ETS_SUPER_VISUAL_PATH":"D:\\workCode\\fork_sample_new\\applications_app_samples_1113\\code\\UI\\CalendarViewSwitch\\entry\\build\\default\\cache\\default\\default@CompileArkTS\\esmodule", + "JS_ASSETS_PATH":"D:\\workCode\\fork_sample_new\\applications_app_samples_1113\\code\\UI\\CalendarViewSwitch\\entry\\build\\default\\intermediates\\loader_out\\default", + "INTERMEDIA_PATH":"D:\\workCode\\fork_sample_new\\applications_app_samples_1113\\code\\UI\\CalendarViewSwitch\\entry\\build\\default\\intermediates", + "RES_PROFILE_PATH":"D:\\workCode\\fork_sample_new\\applications_app_samples_1113\\code\\UI\\CalendarViewSwitch\\entry\\build\\default\\intermediates\\res\\default\\resources\\base\\profile", + "WORKER_LOADER":"D:\\workCode\\fork_sample_new\\applications_app_samples_1113\\code\\UI\\CalendarViewSwitch\\entry\\build\\default\\intermediates\\loader\\default\\loader.json", + "MANIFEST_JSON":"D:\\workCode\\fork_sample_new\\applications_app_samples_1113\\code\\UI\\CalendarViewSwitch\\entry\\build\\default\\intermediates\\manifest\\default", + "JS_LITE_ASSETS_PATH":"D:\\workCode\\fork_sample_new\\applications_app_samples_1113\\code\\UI\\CalendarViewSwitch\\entry\\build\\default\\intermediates\\loader_out_lite\\default", + "JS_SUPER_VISUAL_PATH":"D:\\workCode\\fork_sample_new\\applications_app_samples_1113\\code\\UI\\CalendarViewSwitch\\entry\\build\\default\\cache\\default\\default@CompileJS\\jsbundle" + } + }, + "CommonInfo":{ + "current.select.target":"default" + } +} \ No newline at end of file diff --git a/code/UI/CalendarViewSwitch/.idea/.deveco/project.cache.json b/code/UI/CalendarViewSwitch/.idea/.deveco/project.cache.json new file mode 100644 index 0000000000000000000000000000000000000000..fb715f1799102c9360d0c54223639d0ed1b6fee8 Binary files /dev/null and b/code/UI/CalendarViewSwitch/.idea/.deveco/project.cache.json differ diff --git a/code/UI/CalendarViewSwitch/entry/oh-package-lock.json5 b/code/UI/CalendarViewSwitch/entry/oh-package-lock.json5 new file mode 100644 index 0000000000000000000000000000000000000000..00619c4d8b62c1beb210706f65b11a539cbf9427 --- /dev/null +++ b/code/UI/CalendarViewSwitch/entry/oh-package-lock.json5 @@ -0,0 +1,18 @@ +{ + "meta": { + "stableOrder": true + }, + "lockfileVersion": 3, + "ATTENTION": "THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.", + "specifiers": { + "calendarswitch@../casesfeature/calendarswitch": "calendarswitch@../casesfeature/calendarswitch" + }, + "packages": { + "calendarswitch@../casesfeature/calendarswitch": { + "name": "calendarswitch", + "version": "1.0.0", + "resolved": "../casesfeature/calendarswitch", + "registryType": "local" + } + } +} \ No newline at end of file diff --git a/code/UI/CalendarViewSwitch/entry/oh_modules/calendarswitch/.gitignore b/code/UI/CalendarViewSwitch/entry/oh_modules/calendarswitch/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..e2713a2779c5a3e0eb879efe6115455592caeea5 --- /dev/null +++ b/code/UI/CalendarViewSwitch/entry/oh_modules/calendarswitch/.gitignore @@ -0,0 +1,6 @@ +/node_modules +/oh_modules +/.preview +/build +/.cxx +/.test \ No newline at end of file diff --git a/code/UI/CalendarViewSwitch/entry/oh_modules/calendarswitch/Index.ets b/code/UI/CalendarViewSwitch/entry/oh_modules/calendarswitch/Index.ets new file mode 100644 index 0000000000000000000000000000000000000000..5632a336a83ce6c56a1f36d095bc49ee566dafba --- /dev/null +++ b/code/UI/CalendarViewSwitch/entry/oh_modules/calendarswitch/Index.ets @@ -0,0 +1,17 @@ +/* + * 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. + */ + +export { CustomCalendarSamplePage } from './src/main/ets/pages/CustomCalendarSample'; + diff --git a/code/UI/CalendarViewSwitch/entry/oh_modules/calendarswitch/build-profile.json5 b/code/UI/CalendarViewSwitch/entry/oh_modules/calendarswitch/build-profile.json5 new file mode 100644 index 0000000000000000000000000000000000000000..8a1a9482b2444ff2286af4c54198e591672e900a --- /dev/null +++ b/code/UI/CalendarViewSwitch/entry/oh_modules/calendarswitch/build-profile.json5 @@ -0,0 +1,45 @@ +/* + * 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. + */ +{ + "apiType": "stageMode", + "buildOption": { + }, + "buildOptionSet": [ + { + "name": "release", + "arkOptions": { + "obfuscation": { + "ruleOptions": { + "enable": false, + "files": [ + "./obfuscation-rules.txt" + ] + }, + "consumerFiles": [ + "./consumer-rules.txt" + ] + } + }, + }, + ], + "targets": [ + { + "name": "default" + }, + { + "name": "ohosTest" + } + ] +} diff --git a/code/UI/CalendarViewSwitch/entry/oh_modules/calendarswitch/calendar_switch.gif b/code/UI/CalendarViewSwitch/entry/oh_modules/calendarswitch/calendar_switch.gif new file mode 100644 index 0000000000000000000000000000000000000000..38a7bc9a17ba380b0315949103c7ee9ae8c4de50 Binary files /dev/null and b/code/UI/CalendarViewSwitch/entry/oh_modules/calendarswitch/calendar_switch.gif differ diff --git a/code/UI/CalendarViewSwitch/entry/oh_modules/calendarswitch/hvigorfile.ts b/code/UI/CalendarViewSwitch/entry/oh_modules/calendarswitch/hvigorfile.ts new file mode 100644 index 0000000000000000000000000000000000000000..9c1b2acf8526a94c883faec148a2d836c67c20d0 --- /dev/null +++ b/code/UI/CalendarViewSwitch/entry/oh_modules/calendarswitch/hvigorfile.ts @@ -0,0 +1,20 @@ +/* + * 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 { 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. */ +} \ No newline at end of file diff --git a/code/UI/CalendarViewSwitch/entry/oh_modules/calendarswitch/obfuscation-rules.txt b/code/UI/CalendarViewSwitch/entry/oh_modules/calendarswitch/obfuscation-rules.txt new file mode 100644 index 0000000000000000000000000000000000000000..272efb6ca3f240859091bbbfc7c5802d52793b0b --- /dev/null +++ b/code/UI/CalendarViewSwitch/entry/oh_modules/calendarswitch/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/code/UI/CalendarViewSwitch/entry/oh_modules/calendarswitch/oh-package.json5 b/code/UI/CalendarViewSwitch/entry/oh_modules/calendarswitch/oh-package.json5 new file mode 100644 index 0000000000000000000000000000000000000000..d210418cdce241269dc6b36ac3160d7c74ffbc76 --- /dev/null +++ b/code/UI/CalendarViewSwitch/entry/oh_modules/calendarswitch/oh-package.json5 @@ -0,0 +1,23 @@ +/* + * 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. + */ +{ + "name": "calendarswitch", + "version": "1.0.0", + "description": "Please describe the basic information.", + "main": "Index.ets", + "author": "", + "license": "Apache-2.0", + "dependencies": {} +} \ No newline at end of file diff --git a/code/UI/CalendarViewSwitch/entry/oh_modules/calendarswitch/src/main/ets/customcalendar/common/CommonData.ets b/code/UI/CalendarViewSwitch/entry/oh_modules/calendarswitch/src/main/ets/customcalendar/common/CommonData.ets new file mode 100644 index 0000000000000000000000000000000000000000..68407eaf15f1963e1c7bbb14a15074a824a5c670 --- /dev/null +++ b/code/UI/CalendarViewSwitch/entry/oh_modules/calendarswitch/src/main/ets/customcalendar/common/CommonData.ets @@ -0,0 +1,31 @@ +/* + * 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 { DeviceType, ScheduleInfo } from '../model/CalendarModel'; + +/** + * 公共数据类 + */ +export default class CommonData { + // 日程点年月日数组,用于月视图和周视图日程点刷新 + public static SCHEDULE_ARRAY: string[] = []; + // 日程点数组,用于应用持久化存储日程信息 + public static SCHEDULE_DATA: Array = []; + // 月视图/周视图上当前选中的日期 + public static CURRENT_SELECT_DATE: string = ''; + // 设备类型 + public static DEVICE_TYPE: DeviceType = DeviceType.PHONE; + // 是否是折叠屏设备 + public static IS_FOLD: boolean = false; +} \ No newline at end of file diff --git a/code/UI/CalendarViewSwitch/entry/oh_modules/calendarswitch/src/main/ets/customcalendar/components/CustomCalendar.ets b/code/UI/CalendarViewSwitch/entry/oh_modules/calendarswitch/src/main/ets/customcalendar/components/CustomCalendar.ets new file mode 100644 index 0000000000000000000000000000000000000000..2774970cf6536f15a772d113c3875f9d5f5c0865 --- /dev/null +++ b/code/UI/CalendarViewSwitch/entry/oh_modules/calendarswitch/src/main/ets/customcalendar/components/CustomCalendar.ets @@ -0,0 +1,263 @@ +/* + * 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 { CalendarStyle, CalendarSwitch } from '../model/CalendarModel'; +import { YearView } from '../view/YearView'; +import { MonthView } from '../view/MonthView'; +import { WeekView } from '../view/WeekView'; +import CommonData from '../common/CommonData'; +import { preferences } from '@kit.ArkData'; +import { common } from '@kit.AbilityKit'; +import { BusinessError } from '@kit.BasicServicesKit'; +import { TimeUtils } from '../utils/TimeUtils'; + +/** + * 自定义日历组件CustomCalendar(年视图,月视图,周视图) + * + * 功能描述: + * 1.提供可左右滑动切换年、月、周的年视图,月视图,周视图。 + * 2.月视图和周视图支持点击日期监听onDateClick,年视图支持点击月份监听onMonthClick,提供左右滑动切换年月监听onChangeYearMonth。 + * 3.月视图支持点击非当前月日期切换月份。 + * 4.月视图和周视图支持自定义设置公历、农历、星期、年月信息标题文字缩放比例,今天选中日期的背景色,本月公历日期颜色,本月农历字体颜色。月视图支持 + * 设置非本月公历日期颜色。 + * + * 实现思路 + * 1.根据自定义日历类型CalendarViewType,分别显示对应年(YearView)、月(MonthView)、周视图(WeekView)。 + * 2.月视图和周视图参考[日历三方库@xsqd/calendar](https://ohpm.openharmony.cn/#/cn/detail/@xsqd%2Fcalendar)的部分源码使用两个ForEach + * 循环实现日历的月视图和周视图的日期布局效果。通过CalendarViewType条件渲染对应的月视图或周视图。年视图使用Canvas绘制显示年视图中每个月。使用 + * OffscreenCanvasRenderingContext2D在Canvas上进行离屏绘制(主要使用fillText绘制月份,星期,日期等文本数据),它会将需要绘制的内容先绘制 + * 在缓存区,然后使用transferToImageBitmap将其转换成图片,一次性绘制到canvas上,以加快绘制速度。 + * 3.年视图、月视图、周视图都是根据Swiper的onAnimationStart事件(切换动画开始时触发该回调)进行年、月、周的切换。以月视图为例,通过oldMonthViewIndex + * 存储上一次的Swiper索引值,然后跟本次切换的索引targetIndex进行比较,来识别月份是左滑还是右滑。然后根据当前切换后的索引值去刷新所需的月份。例如, + * 假设swiper索引0(7月),swiper索引1(8月),swiper索引2(9月),当前Swiper显示的索引为1。当Swiper右滑从索引1(8月)切换到索引0(7月)时, + * 需要把Swiper里索引2(9月)的月份更新为6月的数据。年视图和周视图也是类似的逻辑,这里不再赘述。 + * + * 接口: + * CustomCalendar({ calendarViewType: CalendarViewType, calendarStyle?: CalendarStyle, calendarSwitch?: CalendarSwitch, + * onDateClick: (year: number, month: number, date: number)=>void, onChangeYearMonth: (year: number, month: number)=> + * void, onMonthClick: (year: number, month: number)=>void}) + * + * 参数: + * @param { CalendarViewType } calendarViewType - 自定义日历类型。YEAR年视图 MONTH月视图 WEEK周视图。必选项。 + * + * @param { CalendarStyle } calendarStyle - 自定义日历样式。仅用于月、周视图。可选项。 + * 包含以下子项: + * @param { number } textScaling - 月视图和周视图中的公历、农历、星期、年月信息标题文字缩放比例。可选项。 + * @param { Color | number | string | Resource } backgroundColor - 今天选中日期的背景色。可选项。 + * @param { Color | number | string | Resource } monthDayColor - 本月公历日期颜色。可选项。 + * @param { Color | number | string | Resource } noMonthDayColor - 非本月公历日期颜色,仅对月视图有效。可选项。 + * @param { Color | number | string | Resource } lunarColor - 本月农历字体颜色。可选项。 + * + * @param { CalendarSwitch } calendarSwitch - 年、月、周视图切换场景的相关设置。可选项。 + * 包含以下子项: + * @param { CalendarController } controller - 自定义日历控制器,用于视图切换后的数据刷新。可选项。 + * @param { DayInfo } currentSelectDay - 记录月、周视图中点击选中的日期信息。可选项。 + * @param { boolean } isYearMonthHidden - 是否隐藏自定义日历年、月、周视图中自带的年月信息标题。可选项。 + * + * @param { function } onDateClick - 日期点击回调。返回点击日期的年月日信息。仅用于月、周视图。可选项。 + * + * @param { function } onChangeYearMonth - 年、月、周视图左右滑动切换回调,返回左右切换视图后的年月信息,其中年视图切换实际只返回切换后年份信息。可选项。 + * + * @param { function } onMonthClick - 年视图月份点击回调。返回年视图点击的年月信息。仅用于年视图。可选项。 + * + * 基本用法: + * CustomCalendar() // 不传参默认显示月视图 + * CustomCalendar({ calendarViewType: CalendarViewType.YEAR }) // 年视图 + * CustomCalendar({ calendarViewType: CalendarViewType.MONTH }) // 月视图 + * CustomCalendar({ calendarViewType: CalendarViewType.WEEK }) // 周视图 + */ +@Component +export struct CustomCalendar { + // 自定义日历类型 + @Prop calendarViewType: CalendarViewType = CalendarViewType.MONTH; + // 自定义日历样式 + calendarStyle: CalendarStyle = {}; + // 年、月、周视图切换场景的相关设置 + calendarSwitch: CalendarSwitch = { isYearMonthHidden: false }; + // 年视图月份点击回调。返回年视图点击的年月信息。仅用于年视图。 + onMonthClick: (year: number, month: number) => void = () => { + }; + // 日期点击回调。返回点击日期的年月日信息。仅用于月、周视图。 + onDateClick: (year: number, month: number, date: number) => void = () => { + }; + // 年、月、周视图左右滑动切换回调。返回左右切换年、月、周后的年月信息,其中年视图切换实际只返回切换后年份信息。 + onChangeYearMonth: (year: number, month: number) => void = () => { + }; + + aboutToAppear() { + // 月视图和周视图加载时,从持久化存储dataPreferences中获取应用的日程数据 + if (this.calendarViewType === CalendarViewType.MONTH || this.calendarViewType === CalendarViewType.WEEK) { + let dataPreferences: preferences.Preferences | null = null; + let context: common.UIAbilityContext = getContext(this) as common.UIAbilityContext; + // Preferences实例配置选项,自定义Preferences实例的名称mySchedule + let options: preferences.Options = { name: 'mySchedule' }; + // 获取Preferences实例 + dataPreferences = preferences.getPreferencesSync(context, options); + // 检查缓存的Preferences实例中是否包含名为给定Key的存储键值对 + if (dataPreferences?.hasSync('schedule')) { + // schedule如果存在,则获取schedule里所有日程数据,刷新到月视图和周视图上 + dataPreferences?.get('schedule', '', (err: BusinessError, val: preferences.ValueType) => { + if (err) { + return; + } + if (val !== '') { + CommonData.SCHEDULE_DATA = JSON.parse(JSON.stringify(val)); + for (let i = 0; i < CommonData.SCHEDULE_DATA.length; i++) { + TimeUtils.addSchedule(CommonData.SCHEDULE_DATA[i].startTime, CommonData.SCHEDULE_DATA[i].endTime); + } + } + if (this.calendarViewType === CalendarViewType.WEEK) { + // 获取到持久化日程数据之后,主动调用一次周视图数据刷新 + this.calendarSwitch.controller?.schedulePointRefresh(); + } + }) + } + } + } + + build() { + if (this.calendarViewType === CalendarViewType.YEAR) { + // 年视图 + // onMonthClick: 年视图月份点击回调。返回年视图点击的年月信息。仅用于年视图。 + // onChangeYearMonth: 年、月、周视图左右滑动切换回调。返回左右切换年、月、周后的年月信息,其中年视图切换实际只返回切换后年份信息。 + // CalendarSwitch: 年、月、周视图切换场景的相关设置。 + // - controller: 自定义日历控制器,用于视图切换后的数据刷新。 + // - currentSelectDay: 记录月、周视图中点击选中的日期信息。 + // - isYearMonthHidden:是否隐藏自定义日历年、月、周视图中自带的年月信息标题。 + YearView({ + onMonthClick: (year: number, month: number) => { + this.onMonthClick(year, month); + }, + onChangeYearMonth: (year: number, month: number) => { + this.onChangeYearMonth(year, month); + }, + calendarSwitch: { + controller: this.calendarSwitch.controller, + currentSelectDay: this.calendarSwitch.currentSelectDay, + isYearMonthHidden: this.calendarSwitch.isYearMonthHidden + }, + }) + } else if (this.calendarViewType === CalendarViewType.MONTH) { + // 月视图 + // CalendarStyle: 自定义日历样式。仅用于月、周视图。 + // - textScaling: 月视图和周视图中的公历、农历、星期、年月信息标题文字缩放比例。 + // - backgroundColor: 今天选中日期的背景色。 + // - monthDayColor: 本月公历日期颜色。 + // - noMonthDayColor: 非本月公历日期颜色,仅对月视图有效。 + // - lunarColor: 本月农历字体颜色。 + // onDateClick: 日期点击回调。返回点击日期的年月日信息。仅用于月、周视图。 + // onChangeYearMonth: 年、月、周视图左右滑动切换回调,返回左右切换视图后的年月信息,其中年视图切换实际只返回切换后年份信息。 + // CalendarSwitch: 用于年、月、周视图切换场景的相关设置。 + // - controller: 自定义日历控制器,用于视图切换后的数据刷新。 + // - currentSelectDay: 记录月、周视图中点击选中的日期信息。 + // - isYearMonthHidden:是否隐藏自定义日历年、月、周视图中自带的年月信息标题。 + MonthView({ + calendarStyle: { + textScaling: this.calendarStyle.textScaling, + backgroundColor: this.calendarStyle.backgroundColor, + monthDayColor: this.calendarStyle.monthDayColor, + noMonthDayColor: this.calendarStyle.noMonthDayColor, + lunarColor: this.calendarStyle.lunarColor + }, + onDateClick: (year: number, month: number, date: number) => { + this.onDateClick(year, month, date); + }, + onChangeYearMonth: (year: number, month: number) => { + this.onChangeYearMonth(year, month); + }, + calendarSwitch: { + controller: this.calendarSwitch.controller, + currentSelectDay: this.calendarSwitch.currentSelectDay, + isYearMonthHidden: this.calendarSwitch.isYearMonthHidden + } + }) + } else if (this.calendarViewType === CalendarViewType.WEEK) { + // 周视图 + // CalendarStyle: 自定义日历样式。仅用于月、周视图。 + // - textScaling: 月视图和周视图中的公历、农历、星期、年月信息标题文字缩放比例。 + // - backgroundColor: 今天选中日期的背景色。 + // - monthDayColor: 本月公历日期颜色。 + // - noMonthDayColor: 非本月公历日期颜色,仅对月视图有效。 + // - lunarColor: 本月农历字体颜色。 + // onDateClick: 日期点击回调。返回点击日期的年月日信息。仅用于月、周视图。 + // onChangeYearMonth: 年、月、周视图左右滑动切换回调,返回左右切换视图后的年月信息,其中年视图切换实际只返回切换后年份信息。 + // CalendarSwitch: 用于年、月、周视图切换场景的相关设置。 + // - controller: 自定义日历控制器,用于视图切换后的数据刷新。 + // - currentSelectDay: 记录月、周视图中点击选中的日期信息。 + // - isYearMonthHidden:是否隐藏自定义日历年、月、周视图中自带的年月信息标题。 + WeekView({ + calendarStyle: { + textScaling: this.calendarStyle.textScaling, + backgroundColor: this.calendarStyle.backgroundColor, + monthDayColor: this.calendarStyle.monthDayColor, + lunarColor: this.calendarStyle.lunarColor + }, + onDateClick: (year: number, month: number, date: number) => { + this.onDateClick(year, month, date); + }, + onChangeYearMonth: (year: number, month: number) => { + this.onChangeYearMonth(year, month); + }, + calendarSwitch: { + controller: this.calendarSwitch.controller, + currentSelectDay: this.calendarSwitch.currentSelectDay, + isYearMonthHidden: this.calendarSwitch.isYearMonthHidden + } + }) + } + } +} + +/** + * 一天的信息 + */ +@Observed +export class DayInfo { + public year: number; // 年 + public month: number; // 月 + public date: number; // 日 + public week: number; // 月视图周信息。月视图上点击上个月日期进行月份切换时需要用到 + + constructor(year: number, month: number, date: number, week: number) { + this.year = year; + this.month = month; + this.date = date; + this.week = week; + } +} + +/** + * 自定义日历控制器。用于控制年、月、周视图间切换场景下刷新日期数据。 + */ +export class CalendarController { + // 视图刷新接口 + public swiperRefresh = (value: CalendarViewType) => { + }; + // 视图刷新接口(仅用于在年视图上点击月份切换到月视图时刷新) + public swiperYearToMonthRefresh = (year: number, month: number) => { + }; + // 日程点数据刷新接口 + public schedulePointRefresh = () => { + }; +} + +/** + * 自定义日历类型:YEAR年视图 MONTH月视图 WEEK周视图 + */ +export enum CalendarViewType { + YEAR, // 年视图 + MONTH, // 月视图 + WEEK, // 周视图 +} \ No newline at end of file diff --git a/code/UI/CalendarViewSwitch/entry/oh_modules/calendarswitch/src/main/ets/customcalendar/components/SchedulePoint.ets b/code/UI/CalendarViewSwitch/entry/oh_modules/calendarswitch/src/main/ets/customcalendar/components/SchedulePoint.ets new file mode 100644 index 0000000000000000000000000000000000000000..3c034d93ec4741dd84176094ccce775f884edfdf --- /dev/null +++ b/code/UI/CalendarViewSwitch/entry/oh_modules/calendarswitch/src/main/ets/customcalendar/components/SchedulePoint.ets @@ -0,0 +1,499 @@ +/* + * 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 { abilityAccessCtrl, common, PermissionRequestResult, Permissions } from '@kit.AbilityKit'; +import { BusinessError } from '@kit.BasicServicesKit'; +import { CalendarController } from './CustomCalendar'; +import { calendarManager } from '@kit.CalendarKit'; // 日程管理模块 +import Constants from '../constant/Constants'; +import CommonData from '../common/CommonData'; +import { intl } from '@kit.LocalizationKit'; // 国际化模块 +import { promptAction } from '@kit.ArkUI'; +import { preferences } from '@kit.ArkData'; // 用户首选项 +import { ScheduleInfo } from '../model/CalendarModel'; +import { JSON } from '@kit.ArkTS'; +import { TimeUtils } from '../utils/TimeUtils'; +import { logger } from '../utils/Logger'; + +const TAG = 'Calendar_Switch'; +const COLUMN_SPACE = 25; +const FONT_WEIGHT = 500; +const OPACITY = 0.5; +const STROKE_WIDTH = 1; + +/** + * 自定义添加日程组件SchedulePoint + * + * 功能描述: + * 1.提供添加日程以及日程提醒功能,添加的日程会同步到系统日历,Calendar Kit日历服务会根据创建的日程自动进行相应的日程提醒。 + * 2.添加日程后,添加的日程点可以显示在CustomCalendar的月视图和周视图上。 + * + * 实现思路 + * 1.通过Calendar Kit日历与日程管理能力,使用getCalendarManager获取管理日历对象,然后createCalendar创建日历账户。 + * 2.使用getCalendar获取日历对象,然后通过配置CalendarConfig中enableReminder为true启用日程提醒功能。 + * 3.配置日程参数calendarManager.Event,然后传入addEvent创建日程。Calendar Kit日历服务会根据创建的日程进行相应的日程提醒。 + * + * 接口: + * CustomCalendar({ monthViewController?: CalendarController, weekViewController?: CalendarController }) + * + * 参数: + * @param { CalendarController } monthViewController - 月视图控制器,用于控制日程点刷新。可选项。 + * + * @param { CalendarController } weekViewController - 周视图控制器,用于控制日程点刷新。可选项。 + * + * 基本用法: + * SchedulePoint({ + * monthViewController: this.calendarMonthController, + * weekViewController: this.calendarWeekController + * }) + * 对于需要在月视图或者周视图上显示添加的日程点的场景,需要传入对应的控制器,否则新增的日程点不会刷新 + */ +@Component +export struct SchedulePoint { + // 是否显示半模态转场标志位 + @State isShow: boolean = false; + // 日程标题 + @State title: string = ''; + // 日程地点 + @State location: string = ''; + // 日程开始时间 + @State startTime: Date = new Date(); + // 日程开始时间字符串 + @State scheduleStartTime: string = ''; + // 日程结束时间 + @State endTime: Date = new Date(); + // 日程结束时间字符串 + @State scheduleEndTime: string = ''; + // 日程说明 + @State describe: string = ''; + // 日程提醒时间index + @State reminderTimeIndex: number = 0; + // 日程提醒时间选项 + private reminderTimeArray: number[] = [0]; + // CalendarManager日程管理对象 + private calendarMgr: calendarManager.CalendarManager | null = null; + // 用于添加日程时,刷新月视图数据的控制器 + private monthViewController = new CalendarController(); + // 用于添加日程时,刷新周视图数据的控制器 + private weekViewController = new CalendarController(); + // 创建时间、日期格式化对象。 + private dateFormat = new intl.DateTimeFormat('zh-CN', { dateStyle: 'short', timeStyle: 'short' }); + // Preferences对象 + private dataPreferences: preferences.Preferences | null = null; + // 上下文context + private context: common.UIAbilityContext = getContext(this) as common.UIAbilityContext; + // 日历Calendar对象 + private calendar: calendarManager.Calendar | undefined = undefined; + // 日历配置信息 + private config: calendarManager.CalendarConfig = { + // 打开日程提醒 + enableReminder: true, + // 设置日历账户颜色。可选项 + color: Color.Green + }; + // 日历账户信息 + private myCalendarAccount: calendarManager.CalendarAccount = { + // 日历账户名称 + name: 'test', + // 日历账户类型。LOCAL表示本地账户 + type: calendarManager.CalendarType.LOCAL, + // 日历账户显示名称,该字段如果不填,创建的日历账户在界面显示为空字符串。 + displayName: 'test' + }; + + aboutToAppear() { + const permissions: Permissions[] = ['ohos.permission.READ_CALENDAR', 'ohos.permission.WRITE_CALENDAR']; + // 获取AtManager实例 + let atManager = abilityAccessCtrl.createAtManager(); + // 向用户申请系统日历读写权限 + atManager.requestPermissionsFromUser(this.context, permissions).then((result: PermissionRequestResult) => { + logger.info(TAG, `get Permission success, result: ${JSON.stringify(result)}`); + // TODO 知识点:通过getCalendarManager获取管理日历对象,使用getCalendar获取日历对象,然后使用createCalendar创建自己的日历账户, + // 通过配置CalendarConfig中enableReminder为true启用日程提醒功能。 + // 根据上下文获取CalendarManager对象,用于管理日历。 + this.calendarMgr = calendarManager.getCalendarManager(this.context); + // 获取Calendar对象 + this.calendarMgr.getCalendar(this.myCalendarAccount).then((data: calendarManager.Calendar) => { + this.calendar = data; + // 设置日历配置信息 + this.calendar.setConfig(this.config).then(() => { + logger.info(TAG, `Succeeded in setting config, data->${JSON.stringify(this.config)}`); + }).catch((err: BusinessError) => { + logger.error(TAG, `Failed to set config. Code: ${err.code}, message: ${err.message}`); + }); + }).catch(() => { + // 如果日历账户不存在,则创建日历账户 + this.calendarMgr?.createCalendar(this.myCalendarAccount).then((data: calendarManager.Calendar) => { + // 请确保日历账户创建成功后,再进行后续相关操作 + this.calendar = data; + // 设置日历账户 + this.calendar?.setConfig(this.config).then(() => { + logger.info(TAG, `Succeeded in setting config, data->${JSON.stringify(this.config)}`); + }).catch((err: BusinessError) => { + logger.error(TAG, `Failed to set config. Code: ${err.code}, message: ${err.message}`); + }); + }).catch((error: BusinessError) => { + logger.error(TAG, `Failed to create calendar. Code: ${error.code}, message: ${error.message}`); + }); + }); + }).catch((error: BusinessError) => { + logger.error(TAG, `get Permission error, error. Code: ${error.code}, message: ${error.message}`); + }) + } + + /** + * 日程提醒时间菜单 + */ + @Builder + reminderTimeMenu() { + Column() { + ForEach(Constants.REMINDER_TIME_OPTION, (item: string, index: number) => { + Column() { + Flex({ + direction: FlexDirection.Row, + justifyContent: FlexAlign.SpaceBetween, + alignItems: ItemAlign.Center + }) { + Text(item) + .fontSize($r('app.integer.calendar_switch_size_fifteen')) + Image($r('app.media.calendar_switch_ok')) + .visibility(this.reminderTimeIndex === index ? Visibility.Visible : Visibility.Hidden) + .width($r('app.integer.calendar_switch_size_twenty')) + .height($r('app.integer.calendar_switch_size_twenty')) + } + .height($r('app.integer.calendar_switch_size_forty_eight')) + + Divider() + .visibility(index !== Constants.REMINDER_TIME_OPTION.length - 1 ? Visibility.Visible : Visibility.Hidden) + .width($r('app.string.calendar_switch_full_size')) + } + .width($r('app.integer.calendar_switch_size_ninety')) + .onClick(() => { + this.reminderTimeIndex = index; + switch (item) { + case '准时': + this.reminderTimeArray = [0]; + break; + case '1分钟前': + this.reminderTimeArray = [1]; + break; + case '5分钟前': + this.reminderTimeArray = [5]; + break; + case '30分钟前': + this.reminderTimeArray = [30]; + break; + case '1小时前': + this.reminderTimeArray = [60]; + break; + } + }) + }, (item: string) => item) + } + .borderRadius($r('app.integer.calendar_switch_size_ten')) + .width($r('app.integer.calendar_switch_size_hundred')) + .backgroundColor(Color.White) + .focusable(false) + } + + /** + * 提醒时间设置项 + */ + @Builder + reminderTime() { + Row() { + Text($r('app.string.calendar_switch_reminder_time')) + .fontSize($r('app.integer.calendar_switch_size_sixteen')) + .fontColor(Color.Gray) + Blank() + Row() { + Text(Constants.REMINDER_TIME_OPTION[this.reminderTimeIndex]) + .fontSize($r('app.integer.calendar_switch_size_fifteen')) + Image($r('app.media.calendar_switch_spinner')) + .width($r('app.integer.calendar_switch_size_twenty_two')) + .height($r('app.integer.calendar_switch_size_sixteen')) + .margin({ + left: $r('app.integer.calendar_switch_size_eight'), + right: $r('app.integer.calendar_switch_size_eight') + }) + } + .bindMenu(this.reminderTimeMenu(), { placement: Placement.Top }) + } + .width($r('app.string.calendar_switch_full_size')) + .height($r('app.integer.calendar_switch_size_forty_eight')) + .borderRadius($r('app.integer.calendar_switch_size_ten')) + .backgroundColor(Color.White) + .padding({ + left: $r('app.integer.calendar_switch_size_sixteen'), + right: $r('app.integer.calendar_switch_size_sixteen') + }) + } + + /** + * 新建日程页面 + */ + @Builder + addScheduleInfo() { + Column({ space: COLUMN_SPACE }) { + Row() { + Text($r('app.string.calendar_switch_cancel')) + .fontSize($r('app.integer.calendar_switch_size_eighteen')) + .fontColor(Color.Red) + .onClick(() => { + this.isShow = false; + }) + Text($r('app.string.calendar_switch_new_schedule')) + .fontSize($r('app.integer.calendar_switch_size_twenty_two')) + .fontWeight(FONT_WEIGHT) + Text($r('app.string.calendar_switch_add')) + .fontSize($r('app.integer.calendar_switch_size_eighteen')) + .fontColor(this.title === '' ? Color.Gray : Color.Black) + .onClick(() => { + if (this.title === '') { + AlertDialog.show({ + message: $r('app.string.calendar_switch_title_msg'), + alignment: DialogAlignment.Center + }); + return; + } + if (this.startTime.getTime() >= this.endTime.getTime()) { + // 结束时间需要大于开始时间 + AlertDialog.show({ + message: $r('app.string.calendar_switch_time_msg'), + alignment: DialogAlignment.Center + }); + return; + } + // TODO 知识点:配置日程参数calendarManager.Event,然后传入addEvent创建日程,Calendar Kit日历服务会根据创建的日程进行相应 + // 的日程提醒。同时使用持久化preferences存储添加的日程信息,用于月视图和周视图中显示相应的日程点。 + // 配置日程参数。本案例中日程重复规则recurrenceRule未设置,表示不重复 + const EVENT_NOT_REPEATED: calendarManager.Event = { + // 日程标题 + title: this.title, + // 地点 + location: { location: this.location }, + // 日程类型,NORMAL:普通日程,例如会议,闹钟等日常提醒的日程。 IMPORTANT:重要日程,例如结婚纪念日等具有重要意义的日期,不推 + // 荐三方开发者使用,重要日程类型不支持一键服务跳转功能及无法自定义提醒时间。 + type: calendarManager.EventType.NORMAL, + // 日程开始时间,需要13位时间戳。 + startTime: this.startTime.getTime(), + // 日程结束时间,需要13位时间戳。 + endTime: this.endTime.getTime(), + // 日程提醒时间,单位为分钟。填写x分钟,即距开始时间提前x分钟提醒,不填时,默认为不提醒。为负值时表示延期多长时间提醒。 + reminderTime: this.reminderTimeArray + }; + // 创建日程 + this.calendar?.addEvent(EVENT_NOT_REPEATED).then((data: number) => { + logger.info(TAG, `Succeeded in adding event, id -> ${data}`); + }).catch((err: BusinessError) => { + logger.error(TAG, `Failed to addEvent. Code: ${err.code}, message: ${err.message}`); + }); + // 新增日程 + const PARTS: string[] = this.scheduleStartTime.split(' '); + CommonData.SCHEDULE_DATA.push(new ScheduleInfo(this.title, this.location, this.startTime, + this.endTime, this.describe, PARTS[0], this.reminderTimeArray)); + TimeUtils.addSchedule(this.startTime, this.endTime); + // 获取Preferences实例 + let options: preferences.Options = { name: 'mySchedule' }; + this.dataPreferences = preferences.getPreferencesSync(this.context, options); + // 将数据写入缓存的Preferences实例 + this.dataPreferences.putSync('schedule', CommonData.SCHEDULE_DATA); + // 通过flush将Preferences实例持久化 + this.dataPreferences.flush(); + // 刷新月视图。用于更新日程点 + this.monthViewController?.schedulePointRefresh(); + // 刷新周视图。用于更新日程点 + this.weekViewController?.schedulePointRefresh(); + this.isShow = false; + promptAction.showToast({ + message: $r('app.string.calendar_switch_add_completed') + }); + }) + } + .width($r('app.string.calendar_switch_full_size')) + .height($r('app.integer.calendar_switch_size_sixty')) + .justifyContent(FlexAlign.SpaceBetween) + + Column() { + TextInput({ placeholder: $r('app.string.calendar_switch_title') }) + .backgroundColor(Color.White) + .height($r('app.integer.calendar_switch_size_forty_eight')) + .onChange((value: string) => { + this.title = value; + }) + Divider() + .width($r('app.string.calendar_switch_full_size')) + .strokeWidth(STROKE_WIDTH) + .opacity(OPACITY) + TextInput({ placeholder: $r('app.string.calendar_switch_location') }) + .backgroundColor(Color.White) + .height($r('app.integer.calendar_switch_size_forty_eight')) + .onChange((value: string) => { + this.location = value; + }) + Divider() + .width($r('app.string.calendar_switch_full_size')) + .strokeWidth(STROKE_WIDTH) + .opacity(OPACITY) + Row() { + Text($r('app.string.calendar_switch_start_time')) + .height($r('app.integer.calendar_switch_size_forty_eight')) + .fontSize($r('app.integer.calendar_switch_size_sixteen')) + .fontColor(Color.Gray) + Text(this.scheduleStartTime) + .fontSize($r('app.integer.calendar_switch_size_sixteen')) + } + .justifyContent(FlexAlign.SpaceBetween) + .width($r('app.string.calendar_switch_full_size')) + .padding({ + left: $r('app.integer.calendar_switch_size_sixteen'), + right: $r('app.integer.calendar_switch_size_sixteen') + }) + .onClick(() => { + DatePickerDialog.show({ + selected: this.startTime, + showTime: true, // 是否展示时间项,true表示显示时间,false表示不显示时间。 + acceptButtonStyle: { + // 设置确认按钮显示样式 + fontSize: $r('app.string.calendar_switch_button_style') + }, + cancelButtonStyle: { + // 设置取消按钮显示样式 + fontSize: $r('app.string.calendar_switch_button_style') + }, + // 点击弹窗中的“确定”按钮时触发该回调。 + onDateAccept: (value: Date) => { + // 格式化时间并保存 + this.startTime = value; + this.scheduleStartTime = this.dateFormat.format(this.startTime); + } + }) + }) + + Divider() + .width($r('app.string.calendar_switch_full_size')) + .strokeWidth(STROKE_WIDTH) + .opacity(OPACITY) + Row() { + Text($r('app.string.calendar_switch_end_time')) + .height($r('app.integer.calendar_switch_size_forty_eight')) + .fontSize($r('app.integer.calendar_switch_size_sixteen')) + .fontColor(Color.Gray) + Text(this.scheduleEndTime) + .fontSize($r('app.integer.calendar_switch_size_sixteen')) + } + .onClick(() => { + DatePickerDialog.show({ + selected: this.endTime, + showTime: true, + acceptButtonStyle: { + fontSize: $r('app.string.calendar_switch_button_style') + }, + cancelButtonStyle: { + fontSize: $r('app.string.calendar_switch_button_style') + }, + onDateAccept: (value: Date) => { + this.endTime = value; + this.scheduleEndTime = this.dateFormat.format(this.endTime); + } + }) + }) + .justifyContent(FlexAlign.SpaceBetween) + .width($r('app.string.calendar_switch_full_size')) + .padding({ + left: $r('app.integer.calendar_switch_size_sixteen'), + right: $r('app.integer.calendar_switch_size_sixteen') + }) + + Divider() + .width($r('app.string.calendar_switch_full_size')) + .strokeWidth(STROKE_WIDTH) + .opacity(OPACITY) + this.reminderTime() + } + .padding({ + top: $r('app.integer.calendar_switch_size_four'), + bottom: $r('app.integer.calendar_switch_size_four'), + left: $r('app.integer.calendar_switch_margin_size_twelve'), + right: $r('app.integer.calendar_switch_margin_size_twelve') + }) + .borderRadius($r('app.integer.calendar_switch_size_fourteen')) + .backgroundColor(Color.White) + .width($r('app.string.calendar_switch_full_size')) + + Column() { + TextArea({ placeholder: $r('app.string.calendar_switch_describe') }) + .backgroundColor(Color.White) + .height($r('app.string.calendar_switch_full_size')) + .onChange((value: string) => { + this.describe = value; + }) + } + .height($r('app.integer.calendar_switch_two_hundred_fifty')) + .backgroundColor(Color.White) + .padding({ + top: $r('app.integer.calendar_switch_size_four'), + bottom: $r('app.integer.calendar_switch_size_four'), + left: $r('app.integer.calendar_switch_margin_size_twelve'), + right: $r('app.integer.calendar_switch_margin_size_twelve') + }) + .borderRadius($r('app.integer.calendar_switch_size_fourteen')) + } + .height($r('app.string.calendar_switch_full_size')) + .padding({ + top: $r('app.integer.calendar_switch_size_ten'), + left: $r('app.integer.calendar_switch_size_twenty'), + right: $r('app.integer.calendar_switch_size_twenty') + }) + .width($r('app.string.calendar_switch_full_size')) + } + + build() { + // 添加日程按钮 + Image($r('app.media.calendar_switch_add')) + .bindSheet($$this.isShow, this.addScheduleInfo(), { + height: $r('app.string.calendar_switch_full_size'), + showClose: false, + }) + .onClick(() => { + // 初始化 + this.title = ''; + this.location = ''; + this.describe = ''; + this.reminderTimeIndex = 0; + this.reminderTimeArray = [0]; + // 拉起添加日程页面 + this.isShow = true; + if (CommonData.CURRENT_SELECT_DATE === '') { + this.startTime = new Date(); + } else { + const PARTS: string[] = CommonData.CURRENT_SELECT_DATE.split('-'); + if (Number(PARTS[0]) === Constants.TODAY_YEAR && Number(PARTS[1]) === Constants.TODAY_MONTH && + Number(PARTS[2]) === Constants.TODAY) { + this.startTime = new Date(); + } else { + this.startTime = new Date(Number(PARTS[0]), Number(PARTS[1]) - 1, Number(PARTS[2])); + } + } + // 设置添加日程页面上显示的结束时间 = 开始时间 + 1h(60 * 60 * 1000) + this.endTime.setTime(this.startTime.getTime() + 60 * 60 * 1000); + // 格式化时间 + this.scheduleStartTime = this.dateFormat.format(this.startTime); + this.scheduleEndTime = this.dateFormat.format(this.endTime); + }) + .width($r('app.integer.calendar_switch_size_thirty')) + .height($r('app.integer.calendar_switch_size_thirty')) + } +} \ No newline at end of file diff --git a/code/UI/CalendarViewSwitch/entry/oh_modules/calendarswitch/src/main/ets/customcalendar/constant/Constants.ets b/code/UI/CalendarViewSwitch/entry/oh_modules/calendarswitch/src/main/ets/customcalendar/constant/Constants.ets new file mode 100644 index 0000000000000000000000000000000000000000..6e4145685ec0581cfb456ec91002474273ce0dfc --- /dev/null +++ b/code/UI/CalendarViewSwitch/entry/oh_modules/calendarswitch/src/main/ets/customcalendar/constant/Constants.ets @@ -0,0 +1,108 @@ +/* + * 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. + */ + +/** + * 常量 + */ +export default class Constants { + // 月份数组 + public static readonly MONTHS: string[] = + ['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月']; + // 星期 + public static readonly WEEKS: string[] = ['日', '一', '二', '三', '四', '五', '六']; + // 农历月份的中文表示 + public static readonly CHINESE_MONTH: string[] = + ['正', '二', '三', '四', '五', '六', '七', '八', '九', '十', '十一', '腊']; + // 农历日期的中文表示 + public static readonly CHINESE_DAY: string[] = + ['初一', '初二', '初三', '初四', '初五', '初六', '初七', '初八', '初九', '初十', + '十一', '十二', '十三', '十四', '十五', '十六', '十七', '十八', '十九', '二十', + '廿一', '廿二', '廿三', '廿四', '廿五', '廿六', '廿七', '廿八', '廿九', '三十']; + // 一天的总毫秒数。1000 * 60 * 60 * 24 + public static readonly TOTAL_MILLISECONDS_IN_DAY: number = 86400000; + // 一周的天数 + public static readonly DAYS_IN_WEEK: number = 7; + // 公历字体大小 + public static readonly DAY_FONT_SIZE: number = 16; + // 农历字体大小 + public static readonly LUNAR_DAY_FONT_SIZE: number = 10; + // 星期字体大小 + public static readonly WEEK_FONT_SIZE: number = 10; + // 年月信息字体大小 + public static readonly YEAR_MONTH_FONT_SIZE: number = 18; + // 字体倍数 + public static readonly FONT_MULTIPLIER: number = 1; + // 选中日期边框宽度 + public static readonly SELECT_DATE_BORDER_WIDTH: number = 1; + // 默认值 + public static readonly DEFAULT: number = 0; + // 一年12个月 + public static readonly MONTHS_NUM: number = 12; + // 字体粗细设置 + public static readonly FONT_WEIGHT_FIVE_HUNDRED: number = 500; + public static readonly FONT_WEIGHT_EIGHT_HUNDRED: number = 800; + // 今天日期 + public static readonly TODAY: number = new Date().getDate(); + // 本月 + public static readonly TODAY_MONTH: number = new Date().getMonth() + 1; + // 本年 + public static readonly TODAY_YEAR: number = new Date().getFullYear(); + // 年视图宽度 + public static readonly YEAR_VIEW_WIDTH: number | string = '100%'; + // 年视图高度 + public static readonly YEAR_VIEW_HEIGHT: number | string = 650; + // 月视图宽度 + public static readonly MONTH_VIEW_WIDTH: number | string = '100%'; + // 月视图高度 + public static readonly MONTH_VIEW_HEIGHT: number | string = 400; + // 周视图宽度 + public static readonly WEEK_VIEW_WIDTH: number | string = '100%'; + // 周视图高度 + public static readonly WEEK_VIEW_HEIGHT: number | string = 120; + // 日程点直径 + public static readonly SCHEDULE_POINT_DIAMETER: number = 5; + // 年视图字体颜色灰色 + public static readonly YEAR_VIEW_FONT_COLOR: string = '#7e7e7e'; + // 日程提醒时间选项 + public static readonly REMINDER_TIME_OPTION: string[] = ['准时', '1分钟前', '5分钟前', '30分钟前', '1小时前']; + // 年视图月份字体大小,粗细 + public static readonly YEAR_VIEW_MONTH_FONT: string[] = ['60px 500', '70px 500', '25px 400']; + // 年视图星期字体大小 + public static readonly YEAR_VIEW_WEEK_FONT: string[] = ['35px', '45px', '20px']; + // 年视图日期字体大小,粗细 + public static readonly YEAR_VIEW_DAY_FONT: string[] = ['30px 400', '40px 400', '16px 400']; + // 年视图下滑线 + public static readonly YEAR_VIEW_UNDERLINE: string[] = ['80px', '100px', '40px']; + // 年视图月份高度 + public static readonly YEAR_VIEW_MONTH_HEIGHT: number[] = [20, 20, 15]; + // 年视图星期,日期水平绘制偏移量 + public static readonly YEAR_VIEW_HORIZONTAL_OFFSET: number[] = [15, 30, 20]; + // 年视图日期垂直绘制偏移量 + public static readonly YEAR_VIEW_VERTICAL_OFFSET: number[] = [18, 18, 20]; + // 年视图星期初始高度 + public static readonly YEAR_VIEW_WEEK_HEIGHT: number[] = [40, 40, 33]; + // 年视图日期初始垂直绘制高度 + public static readonly YEAR_VIEW_DAY_HEIGHT: number[] = [58, 58, 58]; + // 年视图今天日期的圆半径 + public static readonly YEAR_VIEW_TODAY_RADIUS: number[] = [6, 9, 7]; + // 年视图今天日期的圆的弧线的终止弧度 + public static readonly YEAR_VIEW_TODAY_END_ANGLE: number[] = [6.28, 6.28, 6.28]; + // 年视图月数据的x轴初始值 + public static readonly YEAR_VIEW_INIT_THREE: number[] = [3, 3, 3]; + // 年视图今天日期的圆x轴位置微调 + public static readonly YEAR_VIEW_X_AXIS: number[] = [0, 2, 1]; + // 年视图今天日期的圆y轴位置微调 + public static readonly YEAR_VIEW_Y_AXIS: number[] = [0, 1, 0]; +} \ No newline at end of file diff --git a/code/UI/CalendarViewSwitch/entry/oh_modules/calendarswitch/src/main/ets/customcalendar/model/CalendarModel.ets b/code/UI/CalendarViewSwitch/entry/oh_modules/calendarswitch/src/main/ets/customcalendar/model/CalendarModel.ets new file mode 100644 index 0000000000000000000000000000000000000000..2b7b46bc21e7cc60aa2c0c76656c50c28fa00e90 --- /dev/null +++ b/code/UI/CalendarViewSwitch/entry/oh_modules/calendarswitch/src/main/ets/customcalendar/model/CalendarModel.ets @@ -0,0 +1,89 @@ +/* + * 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 { CalendarController, DayInfo } from '../components/CustomCalendar'; + +/** + * 自定义日历样式。仅用于月、周视图。 + */ +export interface CalendarStyle { + textScaling?: number; // 月视图和周视图中的公历、农历、星期、年月信息标题文字缩放比例。 + backgroundColor?: Color | number | string | Resource; // 今天选中日期的背景色 + monthDayColor?: Color | number | string | Resource; // 本月公历日期颜色 + noMonthDayColor?: Color | number | string | Resource; // 非本月公历日期颜色,仅对月视图有效。 + lunarColor?: Color | number | string | Resource; // 本月农历字体颜色 +} + +/** + * 一天的信息。包含农历 + */ +export interface Day { + dayNum: number, // 日期 + lunarDay: string, // 农历中文日期 + dayInfo: DayInfo, // 一天的年月日信息 + isShowSchedulePoint: boolean // 是否显示日程点 +} + +/** + * 日期信息。用于选择日期回调 + */ +export interface CalendarData { + year: number, // 年 + month: number, // 月 + date: number // 日 +} + +/** + * 年、月、周视图切换场景的相关设置 + */ +export interface CalendarSwitch { + controller?: CalendarController; // 自定义日历控制器,用于视图切换后的数据刷新。 + currentSelectDay?: DayInfo; // 记录月、周视图中点击选中的日期信息。 + isYearMonthHidden?: boolean; // 是否隐藏自定义日历年、月、周视图中自带的年月信息标题。 +} + +/** + * 日程点相关信息 + */ +@Observed +export class ScheduleInfo { + public title: string; // 标题 + public location: string; // 地点 + public startTime: Date; // 开始时间 + public endTime: Date; // 结束时间 + public describe: string; // 说明 + public dateString: string; // 开始时间startTime里的年月日,如'2024/11/5' + public reminderTime: number[]; // 提醒时间 + + constructor(title: string, location: string, startTime: Date, endTime: Date, describe: string, + dateString: string, reminderTime: number[]) { + this.title = title; + this.location = location; + this.startTime = startTime; + this.endTime = endTime; + this.describe = describe; + this.dateString = dateString; + this.reminderTime = reminderTime; + } +} + +/** + * 设备类型 + */ +export enum DeviceType { + PHONE, // 手机或者折叠态折叠屏 + EXPAND_FOLD, // 展开态折叠屏 + RK, // RK3568 +} \ No newline at end of file diff --git a/code/UI/CalendarViewSwitch/entry/oh_modules/calendarswitch/src/main/ets/customcalendar/model/OffscreenCanvas.ets b/code/UI/CalendarViewSwitch/entry/oh_modules/calendarswitch/src/main/ets/customcalendar/model/OffscreenCanvas.ets new file mode 100644 index 0000000000000000000000000000000000000000..455780d05b57014eee5500e54bf8ba34fa8a7139 --- /dev/null +++ b/code/UI/CalendarViewSwitch/entry/oh_modules/calendarswitch/src/main/ets/customcalendar/model/OffscreenCanvas.ets @@ -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. + */ + +/** + * 离屏画布类 + */ +export class OffscreenCanvas { + public settings: RenderingContextSettings = new RenderingContextSettings(true); + public context: CanvasRenderingContext2D = new CanvasRenderingContext2D(this.settings); + // OffscreenCanvasRenderingContext2D入参分别是离屏画布的宽度,离屏画布的高度,用来配置OffscreenCanvasRenderingContext2D对象的参数 + public offContext: OffscreenCanvasRenderingContext2D = + new OffscreenCanvasRenderingContext2D(200, 200, this.settings); + public year: number; + public month: number; + + constructor(year: number, month: number) { + this.year = year; + this.month = month; + } +} \ No newline at end of file diff --git a/code/UI/CalendarViewSwitch/entry/oh_modules/calendarswitch/src/main/ets/customcalendar/utils/Logger.ets b/code/UI/CalendarViewSwitch/entry/oh_modules/calendarswitch/src/main/ets/customcalendar/utils/Logger.ets new file mode 100644 index 0000000000000000000000000000000000000000..e8062fb517df1d2d8f8df2fab652b2ce0e5a4226 --- /dev/null +++ b/code/UI/CalendarViewSwitch/entry/oh_modules/calendarswitch/src/main/ets/customcalendar/utils/Logger.ets @@ -0,0 +1,49 @@ +/* + * 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 { hilog } from '@kit.PerformanceAnalysisKit'; + +/** + * 日志打印类 + */ +class Logger { + private domain: number; + private prefix: string; + private format: string = '%{public}s, %{public}s'; + + constructor(prefix: string) { + this.prefix = prefix; + this.domain = 0xFF00; + this.format.toUpperCase(); + } + + debug(...args: string[]) { + hilog.debug(this.domain, this.prefix, this.format, args); + } + + info(...args: string[]) { + hilog.info(this.domain, this.prefix, this.format, args); + } + + warn(...args: string[]) { + hilog.warn(this.domain, this.prefix, this.format, args); + } + + error(...args: string[]) { + hilog.error(this.domain, this.prefix, this.format, args); + } +} + +export let logger = new Logger('[CalendarView]') \ No newline at end of file diff --git a/code/UI/CalendarViewSwitch/entry/oh_modules/calendarswitch/src/main/ets/customcalendar/utils/StyleUtils.ets b/code/UI/CalendarViewSwitch/entry/oh_modules/calendarswitch/src/main/ets/customcalendar/utils/StyleUtils.ets new file mode 100644 index 0000000000000000000000000000000000000000..bdb2c3b8ec425ffe639380ca4228ecdbfb481842 --- /dev/null +++ b/code/UI/CalendarViewSwitch/entry/oh_modules/calendarswitch/src/main/ets/customcalendar/utils/StyleUtils.ets @@ -0,0 +1,141 @@ +/* + * 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 Constants from '../constant/Constants'; +import { CalendarViewType, DayInfo } from '../components/CustomCalendar'; +import { Day, CalendarStyle } from '../model/CalendarModel'; + +/** + * 样式工具类 + */ +export class StyleUtils { + /** + * 获取公历日期字体颜色(仅用于月视图和周视图) + * @param day 日期信息 + * @param month 月份 + * @param currentSelectDay 当前选择的日期 + * @param calendarViewType 日历视图类型 + * @param calendarStyle 自定义日历样式 + * @returns 返回颜色 + */ + static getColor(day: Day, month: number, currentSelectDay: DayInfo, calendarViewType: CalendarViewType, + calendarStyle: CalendarStyle): Color | number | string | Resource { + const IS_SELECT_DAY: boolean = + currentSelectDay.year === day.dayInfo.year && currentSelectDay.month === day.dayInfo.month && + currentSelectDay.date === day.dayInfo.date; + const IS_TODAY: boolean = + day.dayInfo.year === Constants.TODAY_YEAR && day.dayInfo.month === Constants.TODAY_MONTH && + day.dayInfo.date === Constants.TODAY; + const IS_CURRENT_MONTH: boolean = (month === day.dayInfo.month); + if (!IS_CURRENT_MONTH && (calendarViewType === CalendarViewType.MONTH)) { + return calendarStyle.noMonthDayColor ? calendarStyle.noMonthDayColor : Color.Gray; + } else if (IS_SELECT_DAY && IS_TODAY) { + return Color.White; + } else if (!IS_SELECT_DAY && IS_TODAY) { + return Color.Red; + } else { + return calendarStyle.monthDayColor ? calendarStyle.monthDayColor : Color.Black; + } + } + + /** + * 获取日期农历字体颜色(仅用于月视图和周视图) + * @param day 日期信息 + * @param month 月 + * @param currentSelectDay 当前选择的日期 + * @param calendarViewType 日历视图类型 + * @param calendarStyle 自定义日历样式 + * @returns 返回颜色 + */ + static getLunarDayColor(day: Day, month: number, currentSelectDay: DayInfo, calendarViewType: CalendarViewType, + calendarStyle: CalendarStyle): Color | number | string | Resource { + const IS_SELECT_DAY: boolean = + currentSelectDay.year === day.dayInfo.year && currentSelectDay.month === day.dayInfo.month && + currentSelectDay.date === day.dayInfo.date; + const IS_TODAY: boolean = + day.dayInfo.year === Constants.TODAY_YEAR && day.dayInfo.month === Constants.TODAY_MONTH && + day.dayInfo.date === Constants.TODAY; + const IS_CURRENT_MONTH: boolean = (month === day.dayInfo.month); + if (!IS_CURRENT_MONTH && (calendarViewType === CalendarViewType.MONTH)) { + return Color.Gray; + } else if (IS_SELECT_DAY && IS_TODAY) { + return Color.White; + } else if (!IS_SELECT_DAY && IS_TODAY) { + return Color.Red; + } else { + return calendarStyle.lunarColor ? calendarStyle.lunarColor : Color.Gray; + } + } + + /** + * 获取日期背景色(仅用于月视图和周视图) + * @param day 日期信息 + * @param currentSelectDay 当前选择的日期 + * @param calendarStyle 自定义日历样式 + * @returns 返回颜色 + */ + static getBackgroundColor(day: Day, currentSelectDay: DayInfo, + calendarStyle: CalendarStyle): Color | number | string | Resource { + const IS_SELECT_DAY: boolean = + currentSelectDay.year === day.dayInfo.year && currentSelectDay.month === day.dayInfo.month && + currentSelectDay.date === day.dayInfo.date; + const IS_TODAY: boolean = + day.dayInfo.year === Constants.TODAY_YEAR && day.dayInfo.month === Constants.TODAY_MONTH && + day.dayInfo.date === Constants.TODAY; + if (IS_TODAY && IS_SELECT_DAY) { + return calendarStyle.backgroundColor ? calendarStyle.backgroundColor : Color.Red; + } else { + return Color.Transparent; + } + } + + /** + * 获取日期选中框宽度(仅用于月视图和周视图) + * @param day 日期信息 + * @param month 月 + * @param currentSelectDay 当前选择的日期 + * @param calendarViewType 日历视图类型 + * @returns 返回颜色 + */ + static getBorderWidth(day: Day, month: number, currentSelectDay: DayInfo, + calendarViewType: CalendarViewType): number { + const IS_SELECT_DAY: boolean = + currentSelectDay.year === day.dayInfo.year && currentSelectDay.month === day.dayInfo.month && + currentSelectDay.date === day.dayInfo.date; + const IS_TODAY: boolean = + day.dayInfo.year === Constants.TODAY_YEAR && day.dayInfo.month === Constants.TODAY_MONTH && + day.dayInfo.date === Constants.TODAY; + // 判断是否为选定日期且非今天 + const IS_SELECTED_AND_NOT_TODAY = !IS_TODAY && IS_SELECT_DAY; + switch (calendarViewType) { + case CalendarViewType.MONTH: + // 如果处于月视图,并且日期是选定且非今天,且月份匹配 + if (IS_SELECTED_AND_NOT_TODAY && day.dayInfo.month === month) { + return Constants.SELECT_DATE_BORDER_WIDTH; + } + break; + case CalendarViewType.WEEK: + // 如果处于周视图,并且日期是选定且非今天 + if (IS_SELECTED_AND_NOT_TODAY) { + return Constants.SELECT_DATE_BORDER_WIDTH; + } + break; + default: + break; + } + // 如果不满足上述任何条件,返回默认值 + return Constants.DEFAULT; + } +} \ No newline at end of file diff --git a/code/UI/CalendarViewSwitch/entry/oh_modules/calendarswitch/src/main/ets/customcalendar/utils/TimeUtils.ets b/code/UI/CalendarViewSwitch/entry/oh_modules/calendarswitch/src/main/ets/customcalendar/utils/TimeUtils.ets new file mode 100644 index 0000000000000000000000000000000000000000..3af2b106d925ccabfae97ac91d322df3321f4edf --- /dev/null +++ b/code/UI/CalendarViewSwitch/entry/oh_modules/calendarswitch/src/main/ets/customcalendar/utils/TimeUtils.ets @@ -0,0 +1,381 @@ +/* + * 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 Constants from '../constant/Constants'; +import { Day, CalendarData } from '../model/CalendarModel'; +import { i18n } from '@kit.LocalizationKit'; // 提供系统相关的或者增强的国际化能力。本例使用日历相关能力 +import { DayInfo } from '../components/CustomCalendar'; +import CommonData from '../common/CommonData'; + +/** + * 日期计算工具类 + */ +export class TimeUtils { + /** + * 确定给的日期是星期几 + * @param year 年 + * @param month 月 + * @param day 日 + * @returns 周几 + */ + static getWeekDay(year: number, month: number, day: number): number { + const DATE = new Date(year, month - 1, day); + return DATE.getDay(); + } + + /** + * 给定年份和月份的最后一天是几号 + * @param year 年 + * @param month 月 + * @returns + */ + static getLastDayOfMonth(year: number, month: number): number { + return new Date(year, month, 0).getDate(); + } + + /** + * 用于获取前一个月的最后几天 + * @param year 年 + * @param month 月 + * @param firstDayOfWeek 当前月份的第一天是周几 + * @returns + */ + static getPreviousMonthLastDays(year: number, month: number, firstDayOfWeek: number): number[] { + const DAYS: number[] = []; + const LAST_DAY_OF_PREVIOUS_MONTH = TimeUtils.getLastDayOfMonth(year, month - 1); + for (let i = LAST_DAY_OF_PREVIOUS_MONTH - firstDayOfWeek + 1; i <= LAST_DAY_OF_PREVIOUS_MONTH; i++) { + DAYS.push(i); + } + return DAYS; + } + + /** + * 用于生成当前月份的所有天数的数组 + * @param year 年 + * @param month 月 + * @returns 返回所有天数 + */ + static getCurrentMonthDays(year: number, month: number): number[] { + const DAYS: number[] = []; + const LAST_DAY_OF_MONTH = TimeUtils.getLastDayOfMonth(year, month); + for (let i = 1; i <= LAST_DAY_OF_MONTH; i++) { + DAYS.push(i); + } + return DAYS; + } + + /** + * 返回农历月份的中文表示 + * @param month 农历月 + * @returns + */ + static lunarMonthToChinese(month: number): string { + // 检查是否是闰月 + const IS_LEAP_MONTH = month < 0; + const LUNAR_MONTH = IS_LEAP_MONTH ? -month : month; + // 转换月份 + const CHINSES_MONTH_STR = `${Constants.CHINESE_MONTH[LUNAR_MONTH - 1]}月`; + // 如果是闰月,加上“闰”字 + return IS_LEAP_MONTH ? `闰${CHINSES_MONTH_STR}` : CHINSES_MONTH_STR; + } + + /** + * 返回农历日期的中文表示 + * @param day 农历日 + * @returns + */ + static lunarDayToChinese(day: number): string { + // 转换日期 + return Constants.CHINESE_DAY[day - 1]; + } + + /** + * 获取某年某月的所有日期 + * @param year + * @param month + * @returns + */ + static byMonthDayForYear(year: number, month: number): Day[][] { + const FIRST_DAY_OF_WEEK = TimeUtils.getWeekDay(year, month, 1); + const PREVIOUS_MONTH_DAYS = TimeUtils.getPreviousMonthLastDays(year, month, FIRST_DAY_OF_WEEK); + const CURRENT_MONTH_DAYS = TimeUtils.getCurrentMonthDays(year, month); + const CALENDAR: Day[][] = []; + let week: Day[] = []; + let i18nCalendar: i18n.Calendar = i18n.getCalendar('zh-Hans', 'chinese'); + // 用上个月的天数填充第一周 + for (let i = 0; i < FIRST_DAY_OF_WEEK; i++) { + i18nCalendar.setTime(new Date(year, month - 2, PREVIOUS_MONTH_DAYS[i])) + week.push({ + dayNum: PREVIOUS_MONTH_DAYS[i], + lunarDay: TimeUtils.lunarDayToChinese(i18nCalendar.get('date')), + dayInfo: new DayInfo((month - 1 < 1) ? year - 1 : year, (month - 1 < 1) ? 12 : month - 1, + PREVIOUS_MONTH_DAYS[i], 0), + isShowSchedulePoint: CommonData.SCHEDULE_ARRAY.includes(((month - 1 < 1) ? year - 1 : year) + '/' + + ((month - 1 < 1) ? 12 : month - 1) + '/' + PREVIOUS_MONTH_DAYS[i]) ? true : false + }); + } + + // 用当月的天数填充剩余的周数 + for (const day of CURRENT_MONTH_DAYS) { + i18nCalendar.setTime(new Date(year, month - 1, day)) + week.push({ + dayNum: day, + lunarDay: i18nCalendar.get('date') === 1 ? TimeUtils.lunarMonthToChinese(i18nCalendar.get('month') + 1) : + TimeUtils.lunarDayToChinese(i18nCalendar.get('date')), + dayInfo: new DayInfo(year, month, day, 1), + isShowSchedulePoint: CommonData.SCHEDULE_ARRAY.includes(year + '/' + month + '/' + day) ? true : false + }); + if (week.length === Constants.DAYS_IN_WEEK) { + CALENDAR.push(week); + week = []; + } + } + + // 用下个月的日子填满最后一周 + if (week.length > 0) { + // week.length表示当月最后一周中的当月天数。用7-week.length计算得到的x表示在下个月第一周中的下个月天数 + let x = (Constants.DAYS_IN_WEEK - week.length) + for (let index = 1; index <= x; index++) { + i18nCalendar.setTime(new Date(year, month, index)) + week.push({ + dayNum: index, + lunarDay: index === 0 ? TimeUtils.lunarMonthToChinese(i18nCalendar.get('month') + 1) : + TimeUtils.lunarDayToChinese(i18nCalendar.get('date')), + dayInfo: new DayInfo((month + 1 > 12) ? year + 1 : year, (month + 1 > 12) ? 1 : month + 1, index, 2), + isShowSchedulePoint: CommonData.SCHEDULE_ARRAY.includes(((month + 1 > 12) ? year + 1 : year) + '/' + + ((month + 1 > 12) ? 1 : month + 1) + '/' + index) ? true : false + }) + } + CALENDAR.push(week); + } + return CALENDAR; + } + + /** + * 获取某周的所有日期 + * @param weekNum 距离本周的周数。本周用0表示,负数表示过去周,正数表示未来周,以此类推。 + * @returns 返回某周的所有日期 + */ + static getWeekDays(weekNum: number): Day[][] { + // 获取当前日期 + const CURRENT_DATE = new Date(); + // 获取当前日期是本周的周几(0-6,其中0代表周日,1代表周一,以此类推) + const WEEK_DAY = CURRENT_DATE.getDay(); + // 计算并设置目标周的第一天(周日)的日期 + const BEGIN_DATE = new Date(CURRENT_DATE.getTime() - WEEK_DAY * Constants.TOTAL_MILLISECONDS_IN_DAY + + (Constants.DAYS_IN_WEEK * weekNum) * Constants.TOTAL_MILLISECONDS_IN_DAY); + let days: CalendarData[] = []; + let week: Day[] = []; + const CALENDAR: Day[][] = []; + let i18nCalendar: i18n.Calendar = i18n.getCalendar('zh-Hans', 'chinese'); + for (let i = 0; i < Constants.DAYS_IN_WEEK; i++) { + const DAY = new Date(BEGIN_DATE.getTime() + i * Constants.TOTAL_MILLISECONDS_IN_DAY); + days[i] = { + year: DAY.getFullYear(), + month: DAY.getMonth() + 1, + date: DAY.getDate() + }; + // 设置日历对象内部的时间日期 + i18nCalendar.setTime(new Date(days[i].year, days[i].month - 1, days[i].date)) + week.push({ + dayNum: days[i].date, + lunarDay: i18nCalendar.get('date') === 1 ? TimeUtils.lunarMonthToChinese(i18nCalendar.get('month') + 1) : + TimeUtils.lunarDayToChinese(i18nCalendar.get('date')), + dayInfo: new DayInfo(days[i].year, days[i].month, days[i].date, 0), + isShowSchedulePoint: CommonData.SCHEDULE_ARRAY.includes(days[i].year + '/' + days[i].month + '/' + + days[i].date) ? true : false + }) + } + CALENDAR.push(week); + return CALENDAR; + } + + /** + * 用于获取给定日期所在周的第一天(上周日) + * @param date 给定日期 + * @returns 返回给定日期所在周的第一天 + */ + static getLastSunday(date: Date): Date { + // 获取当前日期是本周的周几(0-6,其中0代表周日,1代表周一,以此类推) + const DAY_OF_WEEK = date.getDay(); + // 创建一个新的Date对象,其值与传入的date相同,用于后续计算而不改变原始date对象 + const LAST_SUNDAY = new Date(date); + // 这里假设周日是一周的第一天,因此直接减去dayOfWeek就能得到本周的周日 + LAST_SUNDAY.setDate(LAST_SUNDAY.getDate() - DAY_OF_WEEK); + // 返回计算后的日期,即本周的周日 + return LAST_SUNDAY; + } + + /** + * 获取每个月的天数 + * @param year 年 + * @param month 月 + * @returns 返回对应天数 + */ + static getMonthDays(year: number, month: number): number { + switch (month) { + case 1: + case 3: + case 5: + case 7: + case 8: + case 10: + case 12: + // 1月、3月、5月、7月、8月、10月和12月各有31天。 + return 31; + case 4: + case 6: + case 9: + case 11: + // 4月、6月、9月和11月各有30天。 + return 30; + case 2: + // 如果年份能被4整除但不能被100整除,或者能被400整除,则是闰年29天 + if ((year % 4 === 0 && year % 100 !== 0) || (year % 400 === 0)) { + return 29; + } else { + return 28; + } + default: + return 0; + } + } + + /** + * 判断是否农历初一 + * @param year 年 + * @param month 月 + * @param day 日 + * @returns 返回是否农历初一 + */ + static isLunarFirstDayOfMonth(year: number, month: number, day: number): boolean { + let i18nCalendar: i18n.Calendar = i18n.getCalendar('zh-Hans', 'chinese'); + i18nCalendar.setTime(new Date(year, month - 1, day)); + return i18nCalendar.get('date') === 1 ? true : false; + } + + /** + * 获取上个月的年份和月份 + * @param year 年 + * @param month 月 + * @returns 返回上个月的年份和月份 + */ + static getLastYearMonth(year: number, month: number): string { + year = (month - 1 < 1) ? year - 1 : year; + month = (month - 1 < 1) ? 12 : month - 1; + return year + '-' + month; + } + + /** + * 获取下个月的年份和月份 + * @param year 年 + * @param month 月 + * @returns 返回下个月的年份和月份 + */ + static getNextYearMonth(year: number, month: number): string { + year = (month + 1 > 12) ? year + 1 : year; + month = (month + 1 > 12) ? 1 : month + 1; + return year + '-' + month; + } + + /** + * 获取上个月的年份 + * @param year 年 + * @param month 月 + * @returns + */ + static getLastYear(year: number, month: number): number { + return (month - 1 < 1) ? year - 1 : year; + } + + /** + * 获取上个月的月份 + * @param year 年 + * @param month 月 + * @returns 返回上个月的月份 + */ + static getLastMonth(year: number, month: number): number { + return (month - 1 < 1) ? 12 : month - 1; + } + + /** + * 获取下个月的年份 + * @param year 年 + * @param month 月 + * @returns 下个月的年份 + */ + static getNextYear(year: number, month: number): number { + return (month + 1 > 12) ? year + 1 : year; + } + + /** + * 获取下个月的月份 + * @param year 年 + * @param month 月 + * @returns 返回下个月的月份 + */ + static getNextMonth(year: number, month: number): number { + return (month + 1 > 12) ? 1 : month + 1; + } + + /** + * 计算某个日期距离今天相差的周数,过去周用负数表示,未来周用正数表示 + * @param selectDay 日期 + * @returns 返回某个日期距离今天相差的周数 + */ + static weeksBetweenDates(selectDay: Date): number { + const TODAY = new Date(); + // 周视图中选中日期所属周的第一天(周日) + let selectLastSunday: Date = TimeUtils.getLastSunday(selectDay); + // 周视图中今天所属周的第一天(周日) + let todayLastSunday: Date = TimeUtils.getLastSunday(TODAY); + // 计算两个日期之间的毫秒差 + const DIFF = todayLastSunday.getTime() - selectLastSunday.getTime(); + // 将毫秒差转换为天数 + const DIFF_DAYS = DIFF / Constants.TOTAL_MILLISECONDS_IN_DAY; + // 将天数差转换为周数,向下取整得到完整的周数 + const DIFF_WEEKS = Math.floor(DIFF_DAYS / Constants.DAYS_IN_WEEK); + return -DIFF_WEEKS; + } + + /** + * 开始时间和结束时间涉及多日时,支持在月视图和周视图中显示添加的多日日程点 + * @param startDate 开始时间 + * @param endDate 结束时间 + */ + static addSchedule(startDate: Date, endDate: Date) { + // 当前日期设置为开始日期 + let currentDateTime = new Date(startDate); + let endDateTime = new Date(endDate); + // 使用 while 循环遍历日期范围 + while (currentDateTime <= endDateTime) { + // 只保留日期部分,格式化为 yyyy/MM/dd + let scheduleTime = + currentDateTime.toLocaleDateString('zh-CN', { year: 'numeric', month: '2-digit', day: '2-digit' }) + .replace(/\//g, '/'); + // 将生成的字符串中的月份和日期部分的零去掉(如果有的话) + let parts = scheduleTime.split('/'); + // 去掉日期前的零(如果有的话) + let formattedMonth = parts[1].replace(/^0/, ''); + let formattedDay = parts[2].replace(/^0/, ''); + scheduleTime = `${parts[0]}/${formattedMonth}/${formattedDay}`; + // 将当前日期加一天 + currentDateTime.setDate(currentDateTime.getDate() + 1); + if (!CommonData.SCHEDULE_ARRAY.includes(scheduleTime)) { + CommonData.SCHEDULE_ARRAY.push(scheduleTime); + } + } + } +} \ No newline at end of file diff --git a/code/UI/CalendarViewSwitch/entry/oh_modules/calendarswitch/src/main/ets/customcalendar/view/MonthView.ets b/code/UI/CalendarViewSwitch/entry/oh_modules/calendarswitch/src/main/ets/customcalendar/view/MonthView.ets new file mode 100644 index 0000000000000000000000000000000000000000..5f19f2f9f0bf6c0c2297d50dbf0c5add612d2d4d --- /dev/null +++ b/code/UI/CalendarViewSwitch/entry/oh_modules/calendarswitch/src/main/ets/customcalendar/view/MonthView.ets @@ -0,0 +1,312 @@ +/* + * 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 Constants from '../constant/Constants'; +import { CalendarController, CalendarViewType } from '../components/CustomCalendar'; +import { CalendarStyle, CalendarSwitch } from '../model/CalendarModel'; +import { MonthViewItem } from './MonthViewItem'; +import { TimeUtils } from '../utils/TimeUtils'; +import CommonData from '../common/CommonData'; + +/** + * 月视图 + */ +@Component +export struct MonthView { + // 当前显示的年份 + @State currentShowYear: number = Constants.TODAY_YEAR; + // 当前显示的月份 + @State currentShowMonth: number = Constants.TODAY_MONTH; + // 当前显示的年月 + @State currentYearMonth: string = Constants.TODAY_YEAR + '-' + Constants.TODAY_MONTH; + // 当前选中的日期 + @State @Watch('onSelectDayChange') currentSelectDate: string = + Constants.TODAY_YEAR + '-' + Constants.TODAY_MONTH + '-' + Constants.TODAY + '-' + '0'; + // 下个月对应的年月信息 + @State nextYearMonth: string = TimeUtils.getNextYearMonth(this.currentShowYear, this.currentShowMonth); + // 上个月对应的年月信息 + @State lastYearMonth: string = TimeUtils.getLastYearMonth(this.currentShowYear, this.currentShowMonth); + // swiper当前显示的子组件索引 + @State swiperMonthIndex: number = 1; + // 记录swiper上一次显示的子组件索引。 + private oldMonthViewIndex: number = 1; + // 添加日程后,重新刷新月视图的控制器 + private oneController = new CalendarController(); + private twoController = new CalendarController(); + private threeController = new CalendarController(); + // 自定义日历样式 + calendarStyle: CalendarStyle = {}; + // 年、月、周视图切换场景的相关设置 + calendarSwitch: CalendarSwitch = { isYearMonthHidden: false }; + // 日期点击回调 + onDateClick: (year: number, month: number, date: number) => void = () => { + }; + // 年、月、周视图左右滑动切换回调 + onChangeYearMonth: (year: number, month: number) => void = () => { + }; + /** + * 用于年、月、周视图间切换场景下刷新日期数据 + */ + private swiperRefresh = (value: CalendarViewType) => { + if (value === CalendarViewType.MONTH) { + if (this.calendarSwitch.currentSelectDay) { + // 重置swiper索引 + this.swiperMonthIndex = 1; + this.oldMonthViewIndex = 1; + // 获取当前选中的日期 + this.currentSelectDate = + this.calendarSwitch.currentSelectDay.year + '-' + this.calendarSwitch.currentSelectDay.month + '-' + + this.calendarSwitch.currentSelectDay.date; + // 更新年月数据 + this.currentShowYear = this.calendarSwitch.currentSelectDay.year; + this.currentShowMonth = this.calendarSwitch.currentSelectDay.month; + this.currentYearMonth = + this.calendarSwitch.currentSelectDay.year + '-' + this.calendarSwitch.currentSelectDay.month; + this.lastYearMonth = TimeUtils.getLastYearMonth(this.calendarSwitch.currentSelectDay.year, + this.calendarSwitch.currentSelectDay.month); + this.nextYearMonth = TimeUtils.getNextYearMonth(this.calendarSwitch.currentSelectDay.year, + this.calendarSwitch.currentSelectDay.month); + } + } + } + /** + * 用于刷新在年视图上点击月后要跳转的月视图数据 + */ + private swiperYearToMonthRefresh = (year: number, month: number) => { + // 重置swiper索引 + this.swiperMonthIndex = 1; + this.oldMonthViewIndex = 1; + // 更新年月数据 + this.currentShowYear = year; + this.currentShowMonth = month; + this.currentYearMonth = year + '-' + month; + this.lastYearMonth = TimeUtils.getLastYearMonth(year, month); + this.nextYearMonth = TimeUtils.getNextYearMonth(year, month); + } + /** + * 用于添加日程后,重刷月视图数据 + */ + private schedulePointRefresh = () => { + this.oneController.schedulePointRefresh(); + this.twoController.schedulePointRefresh(); + this.threeController.schedulePointRefresh(); + } + + aboutToAppear() { + if (this.calendarSwitch.controller) { + // 给controller对应的方法赋值 + this.calendarSwitch.controller.swiperRefresh = this.swiperRefresh; + this.calendarSwitch.controller.swiperYearToMonthRefresh = this.swiperYearToMonthRefresh; + this.calendarSwitch.controller.schedulePointRefresh = this.schedulePointRefresh; + } + } + + /** + * 日期选择改变 + */ + onSelectDayChange() { + // 记录选中的月视图日期,拉起添加日程页面会根据选中日期显示对应的"开始时间" + CommonData.CURRENT_SELECT_DATE = this.currentSelectDate; + const PARTS: string[] = this.currentSelectDate.split('-'); + // 更新年月数据 + this.currentShowYear = Number(PARTS[0]); + this.currentShowMonth = Number(PARTS[1]); + this.onChangeYearMonth(this.currentShowYear, this.currentShowMonth); + const WEEK = Number(PARTS[3]); + /** + * 月视图中点击非当月日期时,会切换相应的月份,根据日期中的this.currentSelectDay.week值进行判断是切换上个月(week等于0)还是下个月(week + * 等于2)。在TimeUtils的byMonthDayForYear中月视图中上个月日期中的week会写入0,下个月的日期写入2。 + */ + if (WEEK === 0) { + // 月视图中如果点击了上个月的日期,即切换到上个月,类似右滑切换月份,只是swiper索引不变,所以需要刷新两个月 + if (this.oldMonthViewIndex === 0) { + // 月视图当前swiper显示的是索引0,则刷新索引1和2的月份 + this.currentYearMonth = TimeUtils.getNextYearMonth(this.currentShowYear, this.currentShowMonth); + this.nextYearMonth = TimeUtils.getLastYearMonth(this.currentShowYear, this.currentShowMonth); + } else if (this.oldMonthViewIndex === 1) { + // 月视图当前swiper显示的是索引1,则刷新索引0和2的月份 + this.lastYearMonth = TimeUtils.getLastYearMonth(this.currentShowYear, this.currentShowMonth); + this.nextYearMonth = TimeUtils.getNextYearMonth(this.currentShowYear, this.currentShowMonth); + } else if (this.oldMonthViewIndex === 2) { + // 月视图当前swiper显示的是索引2,则刷新索引0和1的月份 + this.lastYearMonth = TimeUtils.getNextYearMonth(this.currentShowYear, this.currentShowMonth); + this.currentYearMonth = TimeUtils.getLastYearMonth(this.currentShowYear, this.currentShowMonth); + } + } else if (WEEK === 2) { + // 月视图中如果点击了下个月的日期,即切换到下个月,类似左滑切换月份,只是swiper索引不变,所以需要刷新两个月 + if (this.oldMonthViewIndex === 0) { + //刷新索引1和2 + this.currentYearMonth = TimeUtils.getNextYearMonth(this.currentShowYear, this.currentShowMonth); + this.nextYearMonth = TimeUtils.getLastYearMonth(this.currentShowYear, this.currentShowMonth); + } else if (this.oldMonthViewIndex === 1) { + //刷新索引0和2 + this.lastYearMonth = TimeUtils.getLastYearMonth(this.currentShowYear, this.currentShowMonth); + this.nextYearMonth = TimeUtils.getNextYearMonth(this.currentShowYear, this.currentShowMonth); + } else if (this.oldMonthViewIndex === 2) { + //刷新索引0和1 + this.lastYearMonth = TimeUtils.getNextYearMonth(this.currentShowYear, this.currentShowMonth); + this.currentYearMonth = TimeUtils.getLastYearMonth(this.currentShowYear, this.currentShowMonth); + } + } + } + + /** + * 星期 + */ + @Builder + weeks() { + Row() { + ForEach(Constants.WEEKS, (text: string, index: number) => { + Text(text) + .fontSize(Constants.WEEK_FONT_SIZE * + (this.calendarStyle.textScaling ? this.calendarStyle.textScaling : Constants.FONT_MULTIPLIER)) + .fontColor((index === 0 || index === 6) ? Color.Grey : Color.Black) + .width($r('app.integer.calendar_switch_size_forty')) + .textAlign(TextAlign.Center) + }, (text: string) => text) + } + .width(Constants.MONTH_VIEW_WIDTH) + .justifyContent(FlexAlign.SpaceBetween) + .margin({ bottom: $r('app.integer.calendar_switch_size_ten') }) + } + + build() { + // 月视图 + Column() { + if (!this.calendarSwitch.isYearMonthHidden) { + // 年月信息标题 + Text(`${this.currentShowYear}年${this.currentShowMonth}月`) + .fontSize(Constants.YEAR_MONTH_FONT_SIZE * + (this.calendarStyle.textScaling ? this.calendarStyle.textScaling : Constants.FONT_MULTIPLIER)) + .fontWeight(Constants.FONT_WEIGHT_FIVE_HUNDRED) + .width($r('app.string.calendar_switch_full_size')) + .padding({ left: $r('app.integer.calendar_switch_size_ten') }) + .margin({ bottom: $r('app.integer.calendar_switch_size_ten') }) + } + // 星期 + this.weeks() + + Swiper() { + // 月视图子组件 + MonthViewItem({ + yearMonth: this.lastYearMonth, + currentSelectDate: this.currentSelectDate, + onDateClick: (year: number, month: number, date: number) => { + this.onDateClick(year, month, date); + }, + calendarStyle: { + textScaling: this.calendarStyle.textScaling, + backgroundColor: this.calendarStyle.backgroundColor, + monthDayColor: this.calendarStyle.monthDayColor, + noMonthDayColor: this.calendarStyle.noMonthDayColor, + lunarColor: this.calendarStyle.lunarColor + }, + controller: this.oneController + }) + MonthViewItem({ + yearMonth: this.currentYearMonth, + currentSelectDate: this.currentSelectDate, + onDateClick: (year: number, month: number, date: number) => { + this.onDateClick(year, month, date); + }, + calendarStyle: { + textScaling: this.calendarStyle.textScaling, + backgroundColor: this.calendarStyle.backgroundColor, + monthDayColor: this.calendarStyle.monthDayColor, + noMonthDayColor: this.calendarStyle.noMonthDayColor, + lunarColor: this.calendarStyle.lunarColor + }, + controller: this.twoController + }) + MonthViewItem({ + yearMonth: this.nextYearMonth, + currentSelectDate: this.currentSelectDate, + onDateClick: (year: number, month: number, date: number) => { + this.onDateClick(year, month, date); + }, + calendarStyle: { + textScaling: this.calendarStyle.textScaling, + backgroundColor: this.calendarStyle.backgroundColor, + monthDayColor: this.calendarStyle.monthDayColor, + noMonthDayColor: this.calendarStyle.noMonthDayColor, + lunarColor: this.calendarStyle.lunarColor + }, + controller: this.threeController + }) + } + .onAnimationStart((index: number, targetIndex: number, extraInfo: SwiperAnimationEvent) => { + /** + * TODO 知识点:年视图,月视图和周视图都是根据Swiper的onAnimationStart事件(切换动画开始时触发该回调)进行年,月或周的切换。以月 + * 视图为例,通过oldMonthViewIndex存储上一次的Swiper索引值,然后跟本次切换的索引targetIndex进行比较,来识别月份是左滑还是右滑。 + * 然后根据当前切换后的索引值去刷新所需的月份。例如,假设swiper索引0(7月),swiper索引1(8月),swiper索引2(9月),当前Swiper + * 显示的索引为1。当Swiper右滑从索引1(8月)切换到索引0(7月)时,需要把Swiper里索引2(9月)的月份更新为6月的数据。年视图和周视图 + * 的onAnimationStart也是类似处理逻辑,这里不再赘述。 + */ + if (this.oldMonthViewIndex === targetIndex) { + // 如果手指滑动swiper松开时,targetIndex和之前记录子组件索引oldMonthViewIndex一样,说明swiper没有切换子组件,不需要切换月份 + return; + } + // 记录子组件索引 + this.oldMonthViewIndex = targetIndex; + // 判断是否右滑切换月份 + const IS_RIGHT_SLIDE: boolean = (index === 1 && targetIndex === 0) || (index === 0 && targetIndex === 2) || + (index === 2 && targetIndex === 1); + // TODO: 高性能知识点: 左右滑动切换月时,每次切换月只更新一个月的数据,以优化月视图左右切换时的性能。年视图和周视图也类似,这里不再赘述。 + // 右滑切换到上个月 + if (IS_RIGHT_SLIDE) { + // 将当前月份设置为上个月 + this.currentShowYear = TimeUtils.getLastYear(this.currentShowYear, this.currentShowMonth); + this.currentShowMonth = TimeUtils.getLastMonth(this.currentShowYear, this.currentShowMonth); + this.onChangeYearMonth(this.currentShowYear, this.currentShowMonth); + if (targetIndex === 0) { + /** + * swiper索引右滑到0时,修改swiper索引2的月份为当前月份(索引0)的上一个月。比如,假设swiper索引0(7月),swiper索引1(8月) + * ,swiper索引2(9月)。当右滑切换到索引0(7月)时,需要把索引2(9月)的月份改成6月。 + */ + // 修改swiper索引2的月份为当前月份(索引0)的上一个月 + this.nextYearMonth = TimeUtils.getLastYearMonth(this.currentShowYear, this.currentShowMonth); + } else if (targetIndex === 1) { + // swiper索引右滑到1时,修改swiper索引0的月份为当前月份(索引1)的上一个月。 + this.lastYearMonth = TimeUtils.getLastYearMonth(this.currentShowYear, this.currentShowMonth); + } else if (targetIndex === 2) { + // swiper索引右滑到2时,修改swiper索引1的月份为当前月份(索引2)的上一个月。 + this.currentYearMonth = TimeUtils.getLastYearMonth(this.currentShowYear, this.currentShowMonth); + } + } else { + // 右滑切换到下个月 + // 将当前月份设置为下个月 + this.currentShowYear = TimeUtils.getNextYear(this.currentShowYear, this.currentShowMonth); + this.currentShowMonth = TimeUtils.getNextMonth(this.currentShowYear, this.currentShowMonth); + this.onChangeYearMonth(this.currentShowYear, this.currentShowMonth); + if (targetIndex === 0) { + // swiper索引左滑到0时,修改swiper索引1的月份为当前月份(索引0)的下一个月。 + this.currentYearMonth = TimeUtils.getNextYearMonth(this.currentShowYear, this.currentShowMonth); + } else if (targetIndex === 1) { + // swiper索引左滑到1时,修改swiper索引2的月份为当前月份(索引1)的下一个月。 + this.nextYearMonth = TimeUtils.getNextYearMonth(this.currentShowYear, this.currentShowMonth); + } else if (targetIndex === 2) { + // swiper索引左滑到2时,修改swiper索引0的月份为当前月份(索引2)的下一个月。 + this.lastYearMonth = TimeUtils.getNextYearMonth(this.currentShowYear, this.currentShowMonth); + } + } + }) + .indicator(false) + .loop(true) + .index($$this.swiperMonthIndex) + } + .width(Constants.MONTH_VIEW_WIDTH) + .height(Constants.MONTH_VIEW_HEIGHT) + } +} \ No newline at end of file diff --git a/code/UI/CalendarViewSwitch/entry/oh_modules/calendarswitch/src/main/ets/customcalendar/view/MonthViewItem.ets b/code/UI/CalendarViewSwitch/entry/oh_modules/calendarswitch/src/main/ets/customcalendar/view/MonthViewItem.ets new file mode 100644 index 0000000000000000000000000000000000000000..3535395ea22ebc20a7b5c644a39cf792be5154bc --- /dev/null +++ b/code/UI/CalendarViewSwitch/entry/oh_modules/calendarswitch/src/main/ets/customcalendar/view/MonthViewItem.ets @@ -0,0 +1,184 @@ +/* + * 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 Constants from '../constant/Constants'; +import { CalendarController, CalendarViewType, DayInfo } from '../components/CustomCalendar'; +import { CalendarStyle, Day } from '../model/CalendarModel'; +import { StyleUtils } from '../utils/StyleUtils'; +import { TimeUtils } from '../utils/TimeUtils'; // 时间计算工具类 + +/** + * 月视图子组件 + */ +@Component +export struct MonthViewItem { + // 月视图日期数据 + @State monthDays: Day[][] = []; + // 年月信息 + @Link @Watch('updateMonthData') yearMonth: string; + // 当前选中的日期 + @State currentSelectDay: DayInfo = + new DayInfo(new Date().getFullYear(), new Date().getMonth() + 1, new Date().getDate(), 0); // 当前选中的日期 + // 当前选中的日期,格式'year-month-date-week' + @Link @Watch('OnChangeSelectDate') currentSelectDate: string; + private year: number = Constants.TODAY_YEAR; + private month: number = Constants.TODAY_MONTH; + // 自定义日历样式 + calendarStyle: CalendarStyle = {}; + // 日期点击回调 + onDateClick: (year: number, month: number, date: number) => void = () => { + }; + // 日历控制器。这里用于控制添加日程后月视图数据刷新 + controller?: CalendarController; + + /** + * 日期选择监听 + */ + OnChangeSelectDate() { + const PARTS: string[] = this.currentSelectDate.split('-'); + this.currentSelectDay.year = Number(PARTS[0]); + this.currentSelectDay.month = Number(PARTS[1]); + this.currentSelectDay.date = Number(PARTS[2]); + } + + /** + * 获取指定月份数据 + */ + getMonthViewData(year: number, month: number) { + this.monthDays = [...TimeUtils.byMonthDayForYear(year, month)]; + } + + /** + * 更新月数据 + */ + updateMonthData() { + const PARTS: string[] = this.yearMonth.split('-'); + this.year = Number(PARTS[0]); + this.month = Number(PARTS[1]); + this.getMonthViewData(this.year, this.month); + } + + /** + * 刷新日程点数据 + */ + private schedulePointRefresh = () => { + this.updateMonthData(); + } + + aboutToAppear() { + if (this.controller) { + this.controller.schedulePointRefresh = this.schedulePointRefresh; + } + const PARTS: string[] = this.yearMonth.split('-'); + this.year = Number(PARTS[0]); + this.month = Number(PARTS[1]); + this.getMonthViewData(this.year, this.month); + } + + /** + * 月视图点击上个月或下个月日期切换月份 + * @param is true为下月,false为上一月 + */ + nextMouth(is: boolean) { + if (is) { + this.year = + (this.month + 1 > 12) ? this.year + 1 : this.year; + this.month = (this.month + 1 > 12) ? 1 : this.month + 1; + } else { + this.year = + (this.month - 1 < 1) ? this.year - 1 : this.year; + this.month = (this.month - 1 < 1) ? 12 : this.month - 1; + } + this.yearMonth = this.year + '-' + this.month; + } + + /** + * 月视图一天的子组件 + * @param day 日期 + * @param week 月视图周信息。0上个月,1当前月,2下个月 + */ + @Builder + monthDayBuilder(day: Day, week: number) { + Column() { + Text(day.dayNum + '') + .fontColor(StyleUtils.getColor(day, this.month, this.currentSelectDay, CalendarViewType.MONTH, + this.calendarStyle)) + .fontSize(Constants.DAY_FONT_SIZE * + (this.calendarStyle.textScaling ? this.calendarStyle.textScaling : Constants.FONT_MULTIPLIER)) + .fontWeight(FontWeight.Medium) + Text(day.lunarDay) + .fontColor(StyleUtils.getLunarDayColor(day, this.month, this.currentSelectDay, CalendarViewType.MONTH, + this.calendarStyle)) + .fontSize(Constants.LUNAR_DAY_FONT_SIZE * + (this.calendarStyle.textScaling ? this.calendarStyle.textScaling : Constants.FONT_MULTIPLIER)) + } + .width($r('app.integer.calendar_switch_size_forty')) + .height($r('app.integer.calendar_switch_size_forty')) + .borderRadius($r('app.integer.calendar_switch_size_forty')) + .borderColor($r('app.color.calendar_switch_border_color')) + .borderWidth(StyleUtils.getBorderWidth(day, this.month, this.currentSelectDay, CalendarViewType.MONTH)) + .backgroundColor(StyleUtils.getBackgroundColor(day, this.currentSelectDay, this.calendarStyle)) + .justifyContent(FlexAlign.Center) + .alignItems(HorizontalAlign.Center) + .onClick(() => { + this.onDateClick(day.dayInfo.year, day.dayInfo.month, day.dayInfo.date); + // 月视图需要拼接day.dayInfo.week + this.currentSelectDate = + day.dayInfo.year + '-' + day.dayInfo.month + '-' + day.dayInfo.date + '-' + day.dayInfo.week; + this.currentSelectDay.year = day.dayInfo.year; + this.currentSelectDay.month = day.dayInfo.month; + this.currentSelectDay.date = day.dayInfo.date; + // 必须记录点击日期的week值。CalendarSwitch的onSelectDayChange监听中需要根据week值点击的是上个月还是下个月的日期,从而进行相应的月份切换 + this.currentSelectDay.week = day.dayInfo.week; + // 选中了上个月的日期,切换到上个月。1表示月视图第一周 + if (week == 1 && day.dayNum > Constants.DAYS_IN_WEEK) { + this.nextMouth(false); + } + // 选中了下个月的日期,切换到下个月。2表示月视图第二周 + if (week > 2 && day.dayNum < Constants.DAYS_IN_WEEK) { + this.nextMouth(true); + } + }) + } + + build() { + Column() { + ForEach(this.monthDays, (items: Day[], index: number) => { + Row() { + ForEach(items, (item: Day) => { + Column() { + this.monthDayBuilder(item, index + 1) + if (item.isShowSchedulePoint) { + // 日程点 + Circle({ width: Constants.SCHEDULE_POINT_DIAMETER, height: Constants.SCHEDULE_POINT_DIAMETER }) + .fill($r('app.color.calendar_switch_schedule_point_color')) + .margin({ top: $r('app.integer.calendar_switch_size_one') }) + } + } + .height($r('app.integer.calendar_switch_size_forty_six')) + }, (item: Day, index: number) => { + return item.dayNum + '' + index + item.isShowSchedulePoint; + }) + } + .width($r('app.string.calendar_switch_full_size')) + .justifyContent(FlexAlign.SpaceBetween) + }, (item: Day[], index: number) => { + return item.reduce((item1, item2) => { + return item1 + item2.dayInfo.year + item2.dayInfo.month + item2.dayInfo.date + item2.isShowSchedulePoint + }, '') + index + }) + }.width($r('app.string.calendar_switch_full_size')) + } +} \ No newline at end of file diff --git a/code/UI/CalendarViewSwitch/entry/oh_modules/calendarswitch/src/main/ets/customcalendar/view/WeekView.ets b/code/UI/CalendarViewSwitch/entry/oh_modules/calendarswitch/src/main/ets/customcalendar/view/WeekView.ets new file mode 100644 index 0000000000000000000000000000000000000000..d6ba55132634391dc5ad4d533377d8a95001ba53 --- /dev/null +++ b/code/UI/CalendarViewSwitch/entry/oh_modules/calendarswitch/src/main/ets/customcalendar/view/WeekView.ets @@ -0,0 +1,263 @@ +/* + * 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 Constants from '../constant/Constants'; +import { CalendarController, CalendarViewType } from '../components/CustomCalendar'; +import { CalendarData, CalendarStyle, CalendarSwitch } from '../model/CalendarModel'; +import { TimeUtils } from '../utils/TimeUtils'; +import { WeekViewItem } from './WeekViewItem'; +import CommonData from '../common/CommonData'; + +/** + * 周视图 + */ +@Component +export struct WeekView { + // swiper当前显示的子组件索引 + @State swiperWeekIndex: number = 1; + // 控制周视图每个WeekViewItem显示哪一周的数据 + @State weekNumOne: number = -1; + @State weekNumTwo: number = 0; + @State weekNumThree: number = 1; + // 当前选中的日期 + @State @Watch('onSelectDayChange') currentSelectDate: string = + Constants.TODAY_YEAR + '-' + Constants.TODAY_MONTH + '-' + Constants.TODAY + '-' + '0'; + // 当前显示的年份 + @State currentShowYear: number = Constants.TODAY_YEAR; + // 当前显示的月份 + @State currentShowMonth: number = Constants.TODAY_MONTH; + // 用于记录周视图每个WeekViewItem第一天(周日)的年月信息 + private weekYearOne: number = 0; + private weekYearTwo: number = 0; + private weekYearThree: number = 0; + private weekMonthOne: number = 0; + private weekMonthTwo: number = 0; + private weekMonthThree: number = 0; + // 记录swiper上一次显示的子组件索引。 + private oldWeekViewIndex: number = 1; + // 添加日程后,重新刷新周视图的控制器 + private oneController = new CalendarController(); + private twoController = new CalendarController(); + private threeController = new CalendarController(); + // 自定义日历样式 + calendarStyle: CalendarStyle = {}; + // 年、月、周视图切换场景的相关设置 + calendarSwitch: CalendarSwitch = { isYearMonthHidden: false }; + // 日期点击回调 + onDateClick: (year: number, month: number, date: number) => void = () => { + }; + // 年、月、周视图左右滑动切换回调 + onChangeYearMonth: (year: number, month: number) => void = () => { + }; + /** + * 用于年、月、周视图间切换场景下刷新日期数据 + */ + private swiperRefresh = (value: CalendarViewType) => { + if (value === CalendarViewType.WEEK) { + if (this.calendarSwitch.currentSelectDay) { + // 重置 + this.swiperWeekIndex = 1; + this.oldWeekViewIndex = 1; + this.currentSelectDate = + this.calendarSwitch.currentSelectDay.year + '-' + this.calendarSwitch.currentSelectDay.month + '-' + + this.calendarSwitch.currentSelectDay.date; + // 更新年月数据 + this.currentShowYear = this.calendarSwitch.currentSelectDay.year; + this.currentShowMonth = this.calendarSwitch.currentSelectDay.month; + const SELECT_DAY = + new Date(this.calendarSwitch.currentSelectDay.year, this.calendarSwitch.currentSelectDay.month - 1, + this.calendarSwitch.currentSelectDay.date); + // 计算目前选中的日期selectDay距离今天相差的周数 + const WEEKS_BETWEEN = TimeUtils.weeksBetweenDates(SELECT_DAY); + this.weekNumOne = WEEKS_BETWEEN - 1; // 设置上一周 + this.weekNumTwo = WEEKS_BETWEEN; // 设置本周 + this.weekNumThree = WEEKS_BETWEEN + 1; // 设置下一周 + } + } + } + + /** + * 日期选择改变 + */ + onSelectDayChange() { + CommonData.CURRENT_SELECT_DATE = this.currentSelectDate; + const PARTS: string[] = this.currentSelectDate.split('-'); + // 更新年月数据 + this.currentShowYear = Number(PARTS[0]); + this.currentShowMonth = Number(PARTS[1]); + this.onChangeYearMonth(this.currentShowYear, this.currentShowMonth); + } + + /** + * 用于添加日程后,重刷周视图数据 + */ + private schedulePointRefresh = () => { + this.oneController.schedulePointRefresh(); + this.twoController.schedulePointRefresh(); + this.threeController.schedulePointRefresh(); + } + + aboutToAppear() { + if (this.calendarSwitch.controller) { + // 给controller对应的方法赋值 + this.calendarSwitch.controller.swiperRefresh = this.swiperRefresh; + this.calendarSwitch.controller.schedulePointRefresh = this.schedulePointRefresh; + } + } + + /** + * 星期 + */ + @Builder + weeks() { + Row() { + ForEach(Constants.WEEKS, (text: string, index: number) => { + Text(text) + .fontSize(Constants.WEEK_FONT_SIZE * + (this.calendarStyle.textScaling ? this.calendarStyle.textScaling : Constants.FONT_MULTIPLIER)) + .fontColor((index === 0 || index === 6) ? Color.Grey : Color.Black) + .width($r('app.integer.calendar_switch_size_forty')) + .textAlign(TextAlign.Center) + }, (text: string) => text) + } + .width(Constants.WEEK_VIEW_WIDTH) + .justifyContent(FlexAlign.SpaceBetween) + .margin({ bottom: $r('app.integer.calendar_switch_size_ten') }) + } + + build() { + // 月视图 + Column() { + if (!this.calendarSwitch.isYearMonthHidden) { + // 年月信息标题 + Text(`${this.currentShowYear}年${this.currentShowMonth}月`) + .fontSize(Constants.YEAR_MONTH_FONT_SIZE * + (this.calendarStyle.textScaling ? this.calendarStyle.textScaling : Constants.FONT_MULTIPLIER)) + .fontWeight(Constants.FONT_WEIGHT_FIVE_HUNDRED) + .width($r('app.string.calendar_switch_full_size')) + .padding({ left: $r('app.integer.calendar_switch_size_ten') }) + .margin({ bottom: $r('app.integer.calendar_switch_size_ten') }) + } + // 星期 + this.weeks() + + Swiper() { + // 周视图子组件 + WeekViewItem({ + weekNum: this.weekNumOne, + currentSelectDate: this.currentSelectDate, + onWeekSwitch: (item: CalendarData) => { + // 周视图获取第一天(周日)日期数据 + this.weekYearOne = item.year; + this.weekMonthOne = item.month; + }, + onDateClick: (year: number, month: number, date: number) => { + this.onDateClick(year, month, date); + }, + calendarStyle: { + textScaling: this.calendarStyle.textScaling, + backgroundColor: this.calendarStyle.backgroundColor, + monthDayColor: this.calendarStyle.monthDayColor, + lunarColor: this.calendarStyle.lunarColor + }, + controller: this.oneController + }) + WeekViewItem({ + weekNum: this.weekNumTwo, + currentSelectDate: this.currentSelectDate, + onWeekSwitch: (item: CalendarData) => { + // 周视图获取第一天(周日)日期数据 + this.weekYearTwo = item.year; + this.weekMonthTwo = item.month; + }, + onDateClick: (year: number, month: number, date: number) => { + this.onDateClick(year, month, date); + }, + calendarStyle: { + textScaling: this.calendarStyle.textScaling, + backgroundColor: this.calendarStyle.backgroundColor, + monthDayColor: this.calendarStyle.monthDayColor, + lunarColor: this.calendarStyle.lunarColor + }, + controller: this.twoController + }) + WeekViewItem({ + weekNum: this.weekNumThree, + currentSelectDate: this.currentSelectDate, + onWeekSwitch: (item: CalendarData) => { + // 周视图获取第一天(周日)日期数据 + this.weekYearThree = item.year; + this.weekMonthThree = item.month; + }, + onDateClick: (year: number, month: number, date: number) => { + this.onDateClick(year, month, date); + }, + calendarStyle: { + textScaling: this.calendarStyle.textScaling, + backgroundColor: this.calendarStyle.backgroundColor, + monthDayColor: this.calendarStyle.monthDayColor, + lunarColor: this.calendarStyle.lunarColor + }, + controller: this.threeController + }) + } + .onAnimationStart((index: number, targetIndex: number, extraInfo: SwiperAnimationEvent) => { + // 判断是否右滑切换周 + const IS_RIGHT_SLIDE: boolean = (index === 1 && targetIndex === 0) || (index === 0 && targetIndex === 2) || + (index === 2 && targetIndex === 1); + // 记录子组件索引 + this.oldWeekViewIndex = targetIndex; + // 切换周时,刷新当前周视图第一天(周日)对应的年月信息 + this.currentShowYear = + (this.oldWeekViewIndex === 0) ? this.weekYearOne : + ((this.oldWeekViewIndex === 1) ? this.weekYearTwo : this.weekYearThree); + this.currentShowMonth = + ((this.oldWeekViewIndex === 0) ? this.weekMonthOne : + ((this.oldWeekViewIndex === 1) ? this.weekMonthTwo : this.weekMonthThree)); + this.onChangeYearMonth(this.currentShowYear, this.currentShowMonth); + // 右滑切换周 + if (IS_RIGHT_SLIDE) { + if (targetIndex === 0) { + // swiper索引右滑到0时,修改swiper索引2的周为当前周(索引0)的上一周。 + this.weekNumThree = this.weekNumOne - 1; + } else if (targetIndex === 1) { + // swiper索引右滑到1时,修改swiper索引0的周为当前周(索引1)的上一周。 + this.weekNumOne = this.weekNumTwo - 1; + } else if (targetIndex === 2) { + // swiper索引右滑到2时,修改swiper索引1的周为当前周(索引2)的上一周。 + this.weekNumTwo = this.weekNumThree - 1; + } + } else { + // 左滑切换周 + if (targetIndex === 0) { + // swiper索引左滑到0时,修改swiper索引1的周为当前周(索引0)的下一周。 + this.weekNumTwo = this.weekNumOne + 1; + } else if (targetIndex === 1) { + // swiper索引左滑到1时,修改swiper索引2的周为当前周(索引1)的下一周。 + this.weekNumThree = this.weekNumTwo + 1; + } else if (targetIndex === 2) { + // swiper索引右滑到2时,修改swiper索引0的周为当前周(索引2)的下一周。 + this.weekNumOne = this.weekNumThree + 1; + } + } + }) + .indicator(false) + .loop(true) + .index($$this.swiperWeekIndex) + } + .width(Constants.WEEK_VIEW_WIDTH) + .height(Constants.WEEK_VIEW_HEIGHT) + } +} \ No newline at end of file diff --git a/code/UI/CalendarViewSwitch/entry/oh_modules/calendarswitch/src/main/ets/customcalendar/view/WeekViewItem.ets b/code/UI/CalendarViewSwitch/entry/oh_modules/calendarswitch/src/main/ets/customcalendar/view/WeekViewItem.ets new file mode 100644 index 0000000000000000000000000000000000000000..7b662ea0a27cbc460f74e90c0110d3cbd4725f8d --- /dev/null +++ b/code/UI/CalendarViewSwitch/entry/oh_modules/calendarswitch/src/main/ets/customcalendar/view/WeekViewItem.ets @@ -0,0 +1,164 @@ +/* + * 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 Constants from '../constant/Constants'; +import { CalendarController, CalendarViewType, DayInfo } from '../components/CustomCalendar'; +import { CalendarData, CalendarStyle, Day } from '../model/CalendarModel'; +import { StyleUtils } from '../utils/StyleUtils'; +import { TimeUtils } from '../utils/TimeUtils'; // 时间计算工具类 + +/** + * 周视图子组件 + */ +@Component +export struct WeekViewItem { + // 周视图日期数据 + @State @Watch('getFirstDayData') weekDays: Day[][] = []; + // 当前选中的日期 + @State currentSelectDay: DayInfo = + new DayInfo(new Date().getFullYear(), new Date().getMonth() + 1, new Date().getDate(), 0); // 当前选中的日期 + // 当前选中的日期,格式'year-month-date' + @Link @Watch('OnChangeSelectDate') currentSelectDate: string; + // 表示周视图第几周 + @Prop @Watch('updateWeekData') weekNum: number; + private month: number = Constants.TODAY_MONTH; + // 自定义日历样式 + calendarStyle: CalendarStyle = {}; + // 周视图数据切换回调,返回一周中第一天的数据 + onWeekSwitch: (item: CalendarData) => void = () => { + }; + // 日期点击回调 + onDateClick: (year: number, month: number, date: number) => void = () => { + }; + // 日历控制器。这里用于控制添加日程后月视图数据刷新 + controller?: CalendarController; + + /** + * 日期选择监听 + */ + OnChangeSelectDate() { + const PARTS: string[] = this.currentSelectDate.split('-'); + this.currentSelectDay.year = Number(PARTS[0]); + this.currentSelectDay.month = Number(PARTS[1]); + this.currentSelectDay.date = Number(PARTS[2]); + } + + /** + * 周视图切换时,将当前周数据的第一天(周日)日期数据传出去 + */ + getFirstDayData() { + if (this.weekDays && this.weekDays[0][0].dayInfo) { + this.onWeekSwitch({ + date: this.weekDays[0][0].dayInfo.date, + month: this.weekDays[0][0].dayInfo.month, + year: this.weekDays[0][0].dayInfo.year + }) + } + } + + /** + * 更新周数据 + */ + updateWeekData() { + this.getWeekViewData(this.weekNum); + } + + /** + * 获取指定周数据 + */ + getWeekViewData(weekNum: number) { + this.weekDays = [...TimeUtils.getWeekDays(weekNum)]; + } + + aboutToAppear() { + if (this.controller) { + this.controller.schedulePointRefresh = this.schedulePointRefresh; + } + this.getWeekViewData(this.weekNum); + } + + /** + * 刷新日程点数据 + */ + private schedulePointRefresh = () => { + this.updateWeekData(); + } + + /** + * 周视图一天的子组件 + * @param day 日期 + */ + @Builder + weekDayBuilder(day: Day) { + Column() { + Text(day.dayNum + '') + .fontColor(StyleUtils.getColor(day, this.month, this.currentSelectDay, CalendarViewType.WEEK, + this.calendarStyle)) + .fontSize(Constants.DAY_FONT_SIZE * + (this.calendarStyle.textScaling ? this.calendarStyle.textScaling : Constants.FONT_MULTIPLIER)) + .fontWeight(FontWeight.Medium) + Text(day.lunarDay) + .fontColor(StyleUtils.getLunarDayColor(day, this.month, this.currentSelectDay, CalendarViewType.WEEK, + this.calendarStyle)) + .fontSize(Constants.LUNAR_DAY_FONT_SIZE * + (this.calendarStyle.textScaling ? this.calendarStyle.textScaling : Constants.FONT_MULTIPLIER)) + } + .width($r('app.integer.calendar_switch_size_forty')) + .height($r('app.integer.calendar_switch_size_forty')) + .borderRadius($r('app.integer.calendar_switch_size_forty')) + .borderColor($r('app.color.calendar_switch_border_color')) + .borderWidth(StyleUtils.getBorderWidth(day, this.month, this.currentSelectDay, CalendarViewType.WEEK)) + .backgroundColor(StyleUtils.getBackgroundColor(day, this.currentSelectDay, this.calendarStyle)) + .justifyContent(FlexAlign.Center) + .alignItems(HorizontalAlign.Center) + .onClick(() => { + // 获取年月日信息 + this.onDateClick(day.dayInfo.year, day.dayInfo.month, day.dayInfo.date); + this.currentSelectDate = day.dayInfo.year + '-' + day.dayInfo.month + '-' + day.dayInfo.date; + this.currentSelectDay.year = day.dayInfo.year; + this.currentSelectDay.month = day.dayInfo.month; + this.currentSelectDay.date = day.dayInfo.date; + }) + } + + build() { + Column() { + ForEach(this.weekDays, (items: Day[]) => { + Row() { + ForEach(items, (item: Day) => { + Column() { + this.weekDayBuilder(item) + if (item.isShowSchedulePoint) { + // 日程点 + Circle({ width: Constants.SCHEDULE_POINT_DIAMETER, height: Constants.SCHEDULE_POINT_DIAMETER }) + .fill($r('app.color.calendar_switch_schedule_point_color')) + .margin({ top: $r('app.integer.calendar_switch_size_one') }) + } + } + .height($r('app.integer.calendar_switch_size_forty_six')) + }, (item: Day, index: number) => { + return item.dayNum + '' + index + item.isShowSchedulePoint; + }) + } + .width($r('app.string.calendar_switch_full_size')) + .justifyContent(FlexAlign.SpaceBetween) + }, (item: Day[], index: number) => { + return item.reduce((item1, item2) => { + return item1 + item2.dayInfo.year + item2.dayInfo.month + item2.dayInfo.date + item2.isShowSchedulePoint + }, '') + index; + }) + }.width($r('app.string.calendar_switch_full_size')) + } +} \ No newline at end of file diff --git a/code/UI/CalendarViewSwitch/entry/oh_modules/calendarswitch/src/main/ets/customcalendar/view/YearView.ets b/code/UI/CalendarViewSwitch/entry/oh_modules/calendarswitch/src/main/ets/customcalendar/view/YearView.ets new file mode 100644 index 0000000000000000000000000000000000000000..719a5d20ce9f71e0148b364aa391329918a2bc5e --- /dev/null +++ b/code/UI/CalendarViewSwitch/entry/oh_modules/calendarswitch/src/main/ets/customcalendar/view/YearView.ets @@ -0,0 +1,161 @@ +/* + * 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 Constants from '../constant/Constants'; +import { CalendarViewType } from '../components/CustomCalendar'; +import { CalendarSwitch } from '../model/CalendarModel'; +import { YearViewItem } from './YearViewItem'; + +/** + * 年视图 + */ +@Component +export struct YearView { + // swiper当前显示的子组件索引 + @State swiperYearIndex: number = 1; + // 当前显示的年 + @State currentShowYear: number = Constants.TODAY_YEAR; + // 上一年 + @State lastYear: number = Constants.TODAY_YEAR - 1; + // 当前年 + @State currentYear: number = Constants.TODAY_YEAR; + // 下一年 + @State nextYear: number = Constants.TODAY_YEAR + 1; + // 记录swiper上一次显示的子组件索引。 + private oldYearViewIndex: number = 1; + // 年、月、周视图切换场景的相关设置 + calendarSwitch: CalendarSwitch = { isYearMonthHidden: false }; + // 年、月、周视图左右滑动切换回调 + onChangeYearMonth: (year: number, month: number) => void = () => { + }; + // 年视图月份点击回调 + onMonthClick: (year: number, month: number) => void = () => { + }; + /** + * 用于年、月、周视图间切换场景下刷新日期数据 + */ + private swiperRefresh = (value: CalendarViewType) => { + if (value === CalendarViewType.YEAR) { + if (this.calendarSwitch.currentSelectDay) { + if (this.calendarSwitch.currentSelectDay.year === this.currentShowYear && this.swiperYearIndex === 1) { + return; + } + this.currentShowYear = this.calendarSwitch.currentSelectDay.year; + // 重置swiper索引 + this.swiperYearIndex = 1; + this.oldYearViewIndex = 1; + // 更新年月数据 + this.currentYear = this.calendarSwitch.currentSelectDay.year; + this.lastYear = this.calendarSwitch.currentSelectDay.year - 1; + this.nextYear = this.calendarSwitch.currentSelectDay.year + 1; + } + } + } + + aboutToAppear() { + if (this.calendarSwitch.controller) { + // 给controller对应的方法赋值 + this.calendarSwitch.controller.swiperRefresh = this.swiperRefresh; + } + } + + build() { + // 年视图 + Column() { + if (!this.calendarSwitch.isYearMonthHidden) { + // 年月信息标题 + Text(`${this.currentShowYear}年`) + .fontSize($r('app.integer.calendar_switch_size_twenty_five')) + .fontWeight(Constants.FONT_WEIGHT_EIGHT_HUNDRED) + .fontColor(Color.Red) + .width($r('app.string.calendar_switch_full_size')) + Divider() + .strokeWidth('1px') + .margin({ + top: $r('app.integer.calendar_switch_size_four'), + bottom: $r('app.integer.calendar_switch_size_four') + }) + } + Swiper() { + // 年视图子组件 + YearViewItem({ + year: this.lastYear, + onMonthClick: (year: number, month: number) => { + this.onMonthClick(year, month); + } + }) + YearViewItem({ + year: this.currentYear, + onMonthClick: (year: number, month: number) => { + this.onMonthClick(year, month); + } + }) + YearViewItem({ + year: this.nextYear, + onMonthClick: (year: number, month: number) => { + this.onMonthClick(year, month); + }, + }) + } + .onAnimationStart((index: number, targetIndex: number, extraInfo: SwiperAnimationEvent) => { + if (this.oldYearViewIndex === targetIndex) { + // 如果手指滑动swiper松开时,targetIndex和之前记录子组件索引oldYearViewIndex一样,说明swiper没有切换子组件,不需要切换年份 + return; + } + // 记录子组件索引 + this.oldYearViewIndex = targetIndex; + // 判断是否右滑切换年 + const IS_RIGHT_SLIDE: boolean = (index === 1 && targetIndex === 0) || (index === 0 && targetIndex === 2) || + (index === 2 && targetIndex === 1); + // 手指右滑切换上一年 + if (IS_RIGHT_SLIDE) { + // 滑到上一年,当前显示的年份-1 + this.currentShowYear = this.currentShowYear - 1; + if (targetIndex === 0) { + // swiper索引右滑到0时,修改swiper索引2的年为当前年(索引0)的上一年。比如,假设swiper索引0(2023年),swiper索引1(2024年) + // ,swiper索引2(2025年)。当右滑切换到索引0(2023年)时,需要把索引2(2025年)改成2022年。 + this.nextYear = this.currentShowYear - 1; + } else if (targetIndex === 1) { + // swiper索引右滑到1时,修改swiper索引0的年为当前年(索引1)的上一年。 + this.lastYear = this.currentShowYear - 1; + } else if (targetIndex === 2) { + // swiper索引右滑到2时,修改swiper索引1的年为当前年(索引2)的上一年。 + this.currentYear = this.currentShowYear - 1; + } + } else { + // 左滑切换下一年 + // 滑到下一年,当前显示的年份+1 + this.currentShowYear = this.currentShowYear + 1; + if (targetIndex === 0) { + // swiper索引左滑到0时,修改swiper索引1的年为当前年(索引0)的下一年。 + this.currentYear = this.currentShowYear + 1; + } else if (targetIndex === 1) { + // swiper索引左滑到1时,修改swiper索引2的年为当前年(索引1)的下一年。 + this.nextYear = this.currentShowYear + 1; + } else if (targetIndex === 2) { + // swiper索引左滑到2时,修改swiper索引0的年为当前年(索引2)的下一年。 + this.lastYear = this.currentShowYear + 1; + } + } + this.onChangeYearMonth(this.currentShowYear, Constants.DEFAULT); + }) + .indicator(false) + .loop(true) + .index($$this.swiperYearIndex) + } + .width(Constants.YEAR_VIEW_WIDTH) + .height(Constants.YEAR_VIEW_HEIGHT) + } +} \ No newline at end of file diff --git a/code/UI/CalendarViewSwitch/entry/oh_modules/calendarswitch/src/main/ets/customcalendar/view/YearViewItem.ets b/code/UI/CalendarViewSwitch/entry/oh_modules/calendarswitch/src/main/ets/customcalendar/view/YearViewItem.ets new file mode 100644 index 0000000000000000000000000000000000000000..3b89d8fdc25f7aff63b80d07ed4249bd70ad748d --- /dev/null +++ b/code/UI/CalendarViewSwitch/entry/oh_modules/calendarswitch/src/main/ets/customcalendar/view/YearViewItem.ets @@ -0,0 +1,178 @@ +/* + * 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 CommonData from '../common/CommonData'; +import Constants from '../constant/Constants'; +import { OffscreenCanvas } from '../model/OffscreenCanvas'; // 离屏画布类 +import { TimeUtils } from '../utils/TimeUtils'; // 时间计算工具类 + +/** + * 年视图子组件 + */ +@Component +export struct YearViewItem { + // 年视图离屏画布列表 + @State yearViewList: Array = []; + @Prop @Watch('updateYearData') year: number; + // 年视图月份点击回调 + onMonthClick: (year: number, month: number) => void = () => { + }; + + /** + * 更新年数据 + */ + updateYearData() { + this.yearViewList = []; + for (let i = 1; i <= Constants.MONTHS_NUM; i++) { + this.yearViewList.push(new OffscreenCanvas(this.year, i)); + } + } + + aboutToAppear(): void { + // 添加年视图中每个月的离屏画布对象。一个画布对象绘制一个月数据。 + for (let i = 1; i <= Constants.MONTHS_NUM; i++) { + this.yearViewList.push(new OffscreenCanvas(this.year, i)); + } + } + + build() { + Grid() { + ForEach(this.yearViewList, (monthItem: OffscreenCanvas) => { + GridItem() { + // TODO: 高性能知识点: 年视图使用Canvas绘制显示年视图中每个月,以减少节点数量,同时使用OffscreenCanvasRenderingContext2D离 + // 屏绘制,将需要绘制的内容先绘制在缓存区,然后将其转换成图片,一次性绘制到canvas上,以加快绘制速度。 + Canvas(monthItem.context) + .width($r('app.string.calendar_switch_full_size')) + .height($r('app.string.calendar_switch_full_size')) + .onReady(() => { + // 绘制年视图中一个月的数据 + let isTodayMoth: boolean = + monthItem.year === Constants.TODAY_YEAR && monthItem.month === Constants.TODAY_MONTH; + // 画月 + monthItem.offContext.font = Constants.YEAR_VIEW_MONTH_FONT[CommonData.DEVICE_TYPE]; + monthItem.offContext.fillStyle = isTodayMoth ? Color.Red : Color.Black; + monthItem.offContext.fillText(Constants.MONTHS[monthItem.month-1], + Constants.YEAR_VIEW_INIT_THREE[CommonData.DEVICE_TYPE], + Constants.YEAR_VIEW_MONTH_HEIGHT[CommonData.DEVICE_TYPE]); + // 水平偏移 + let horizontalOffset = Constants.YEAR_VIEW_INIT_THREE[CommonData.DEVICE_TYPE]; + // 画星期 + monthItem.offContext.font = Constants.YEAR_VIEW_WEEK_FONT[CommonData.DEVICE_TYPE]; + for (let i = 0; i < Constants.WEEKS.length; i++) { + // 星期六,日字体颜色设置灰色 + if (i === 0 || i === 6) { + monthItem.offContext.fillStyle = Constants.YEAR_VIEW_FONT_COLOR; + } + monthItem.offContext.fillText(Constants.WEEKS[i], horizontalOffset, + Constants.YEAR_VIEW_WEEK_HEIGHT[CommonData.DEVICE_TYPE]); + monthItem.offContext.fillStyle = Color.Black; + horizontalOffset += Constants.YEAR_VIEW_HORIZONTAL_OFFSET[CommonData.DEVICE_TYPE]; + } + // 获取月份日期前占位个数 + const INTERVAL_COUNT: number = TimeUtils.getWeekDay(monthItem.year, monthItem.month, 1); + // 画日期 + monthItem.offContext.font = Constants.YEAR_VIEW_DAY_FONT[CommonData.DEVICE_TYPE]; + monthItem.offContext.fillStyle = Color.Black; + // 获取每个月的总天数 + const TOTAL_DAYS_IN_MONTH: number = TimeUtils.getMonthDays(monthItem.year, monthItem.month); + // 获取一个月占几周。向上取整 + const WEEK_LENGTH = Math.ceil((INTERVAL_COUNT + TOTAL_DAYS_IN_MONTH) / 7); + // 初始化绘制日期。从1号开始绘制 + let dayIndex = 1; + // 日期垂直偏移 + let verticalOffset = Constants.YEAR_VIEW_DAY_HEIGHT[CommonData.DEVICE_TYPE]; + for (let i = 0; i < WEEK_LENGTH; i++) { + horizontalOffset = Constants.YEAR_VIEW_INIT_THREE[CommonData.DEVICE_TYPE]; + // 画一周 + for (let j = 1; j <= Constants.DAYS_IN_WEEK; j++) { + if (i === 0 && j <= INTERVAL_COUNT) { + // 月份日期前占位 + } else { + // 判断绘制的日期是不是今天。如果是今天,日期绘制圆圈红色背景,白色字体。如果不是今天,黑色字体 + if (isTodayMoth && Constants.TODAY === dayIndex) { + + // 画圆圈 + monthItem.offContext.fillStyle = Color.Red; + // 绘制弧线路径。这里绘制圆圈。arc入参分别是弧线圆心的x坐标值,弧线圆心的y坐标值,弧线的圆半径,弧线的起始弧度,弧线的 + // 终止弧度。5和3是圆圈x,y坐标绘制位置的微调值 + monthItem.offContext.arc(horizontalOffset + 5 + + Constants.YEAR_VIEW_X_AXIS[CommonData.DEVICE_TYPE], + verticalOffset - 3 - Constants.YEAR_VIEW_Y_AXIS[CommonData.DEVICE_TYPE], + Constants.YEAR_VIEW_TODAY_RADIUS[CommonData.DEVICE_TYPE], Constants.DEFAULT, + Constants.YEAR_VIEW_TODAY_END_ANGLE[CommonData.DEVICE_TYPE]); + // 对封闭路径进行填充。 + monthItem.offContext.fill(); + // 设置白色字体 + monthItem.offContext.fillStyle = Color.White; + } else { + if (j === 1 || j === 7) { + // 星期日和星期六字体设置灰色 + monthItem.offContext.fillStyle = Constants.YEAR_VIEW_FONT_COLOR; + } else { + monthItem.offContext.fillStyle = Color.Black; + } + } + // 画日期 + if (dayIndex < 10) { + // 日期1-9号,字体水平位置微调3vp + monthItem.offContext.fillText(dayIndex.toString(), 3 + horizontalOffset, verticalOffset); + } else { + monthItem.offContext.fillText(dayIndex.toString(), horizontalOffset, verticalOffset); + } + // 画线。如果是农历初一,则日期底部加下划线 + if (TimeUtils.isLunarFirstDayOfMonth(monthItem.year, monthItem.month, dayIndex)) { + monthItem.offContext.font = Constants.YEAR_VIEW_UNDERLINE[CommonData.DEVICE_TYPE]; + monthItem.offContext.fillStyle = Color.Red; + monthItem.offContext.fillText('_', 1 + horizontalOffset, verticalOffset); + monthItem.offContext.font = Constants.YEAR_VIEW_DAY_FONT[CommonData.DEVICE_TYPE]; + } + // 重置日期字体颜色 + monthItem.offContext.fillStyle = Color.Black; + dayIndex++; + } + // 日期绘制水平偏移 + horizontalOffset += Constants.YEAR_VIEW_HORIZONTAL_OFFSET[CommonData.DEVICE_TYPE]; + if (dayIndex > TOTAL_DAYS_IN_MONTH) { + break; + } + } + // 周绘制垂直偏移 + verticalOffset += Constants.YEAR_VIEW_VERTICAL_OFFSET[CommonData.DEVICE_TYPE]; + } + // 从OffscreenCanvas组件中最近渲染的图像创建一个ImageBitmap对象 + const IMAGE = monthItem.offContext.transferToImageBitmap(); + // 显示给定的ImageBitmap对象 + monthItem.context.transferFromImageBitmap(IMAGE); + }) + } + .height($r('app.string.calendar_switch_size_twenty_three')) + .width($r('app.string.calendar_switch_size_twenty_five')) + .onClick(() => { + this.onMonthClick(monthItem.year, monthItem.month); + }) + }, (monthItem: OffscreenCanvas) => monthItem.year + '' + monthItem.month + '' + CommonData.DEVICE_TYPE) + } + .onSizeChange(() => { + if (CommonData.IS_FOLD) { + // 折叠屏展开态和折叠态尺寸变化时需要刷新当前年视图 + this.updateYearData(); + } + }) + .scrollBar(BarState.Off) + .columnsTemplate('1fr 1fr 1fr') + .columnsGap($r('app.integer.calendar_switch_columns_gap')) + .rowsGap($r('app.integer.calendar_switch_rows_gap')) + } +} \ No newline at end of file diff --git a/code/UI/CalendarViewSwitch/entry/oh_modules/calendarswitch/src/main/ets/pages/CustomCalendarSample.ets b/code/UI/CalendarViewSwitch/entry/oh_modules/calendarswitch/src/main/ets/pages/CustomCalendarSample.ets new file mode 100644 index 0000000000000000000000000000000000000000..03341bcdf1b0cbc08bd004a1125c69a294bcf66a --- /dev/null +++ b/code/UI/CalendarViewSwitch/entry/oh_modules/calendarswitch/src/main/ets/pages/CustomCalendarSample.ets @@ -0,0 +1,473 @@ +/* + * 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 { + CustomCalendar, // 自定义日历组件 + CalendarViewType, // 自定义日历类型:年视图YEAR,月视图MONTH,周视图WEEK。 + CalendarController, // 自定义日历控制器。用于控制年、月、周视图间切换场景下刷新日期数据。 + DayInfo // 日期信息类 +} from '../customcalendar/components/CustomCalendar'; // 自定义日历组件 +import { SchedulePoint } from '../customcalendar/components/SchedulePoint'; // 自定义添加日程组件 +import { display } from '@kit.ArkUI'; // 屏幕属性模块 +import CommonData from '../customcalendar/common/CommonData'; +import { DeviceType } from '../customcalendar/model/CalendarModel'; + +// 布局权重 +const LAYOUT_WEIGHT_ONE: number = 1; +const LAYOUT_WEIGHT_THREE: number = 3; +// 字体缩放倍数 +const TEXT_SCALING: number = 0.95; +// 当前年 +const TODAY_YEAR: number = new Date().getFullYear(); +// 当前月 +const TODAY_MONTH: number = new Date().getMonth() + 1; +// 内边距。和下面的自定义的分段按钮customSegmentButtonItem有关联 +const PADDING: number = 15; +// 自定义分段按钮中Column宽度 +const COLUMN_WIDTH: number = 50; +// 自定义分段按钮白色滑块空隙间距 +const GAP_SPACE: number = 6; +// 自定义分段按钮'月'Text的宽度 +const CUSTOM_SEGMENT_BUTTON_MONTH_WIDTH: number = 30; +// 自定义分段按钮选中和未选中的字体粗细 +const FONT_WEIGHT_FOUR_HUNDRED: number = 400; +const FONT_WEIGHT_FIVE_HUNDRED: number = 500; +// 月份数组 +const MONTHS: string[] = + ['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月']; +// 年月信息标题字体大小 +const FONT_SIZE: number = 18; + +/** + * 功能描述:本示例介绍如何使用自定义日历组件CustomCalendar实现日历年视图,月视图,周视图以及视图切换功能。还有如何使用Calendar Kit日历服务实现日程提醒的功能。 + * + * 推荐场景:需要使用左右滑动切换年视图,月视图,周视图以及需要添加日程提醒的应用场景 + * + * 核心组件: + * 1.CustomCalendar + * 2.SchedulePoint + * + * 实现步骤: + * 日历切换场景: + * 1.使用Tabs进行年、月、周视图页面布局。 + * 2.调用自定义日历组件CustomCalendar组件分别在TabContent中显示对应年、月、周视图。 + * 3.点击自定义分段按钮customSegmentButton进行年、月、周视图间切换。使用视图控制器的swiperRefresh方法刷新对应视图数据。 + * 日程提醒场景: + * 1.通过getCalendarManager获取管理日历对象,使用getCalendar获取日历对象,然后使用createCalendar创建自己的日历账户,通过配置 + * CalendarConfig中enableReminder为true启用日程提醒功能。 + * 2.配置日程参数calendarManager.Event,然后传入addEvent创建日程,Calendar Kit日历服务会根据创建的日程进行相应的日程提醒。同时使用持久 + * 化preferences存储添加的日程信息,用于月视图和周视图中显示相应的日程点。 + */ +@Component +export struct CustomCalendarSamplePage { + // 屏幕宽度 + @State screenWidth: number = 0; + // 当前显示的年份 + @State currentShowYear: number = TODAY_YEAR; + // 当前显示的月份 + @State currentShowMonth: number = TODAY_MONTH; + // 当前月视图或周视图中选中的日期 + @State currentSelectDay: DayInfo = + new DayInfo(new Date().getFullYear(), new Date().getMonth() + 1, new Date().getDate(), 0); + // 是否隐藏年、月、周视图中年月信息标题 + @State isYearMonthHidden: boolean = true; + // 当前选中的自定义分段按钮。0年视图,1月视图,2周视图 + @State currentIndex: number = 1; + // 自定义分段按钮左侧边距 + @State indicatorLeftMargin: number = 0; + // 自定义分段按钮白色滑块宽度 + @State indicatorWidth: number = 0; + // 记录自定义分段按钮切换的index。如果切换到年视图,隐藏年月信息标题中月份 + @State tabSelectedIndex: number = 1; + // 自定义分段按钮滑动动画时长 + private animationDuration: number = 300; + // Tabs控制器 + private tabController: TabsController = new TabsController(); + // 自定义日历年视图控制器 + private calendarYearController = new CalendarController(); + // 自定义日历月视图控制器 + private calendarMonthController = new CalendarController(); + // 自定义日历周视图控制器 + private calendarWeekController = new CalendarController(); + // 检查设备是否可折叠 + private isFoldable: boolean = false; + // 折叠设备屏幕显示模式回调 + private callback: Callback = (mode: display.FoldDisplayMode) => { + // 可折叠设备的显示模式改变时(如展开或者折叠),重新获取屏幕宽度 + this.screenWidth = this.getCurrentScreenWidth(); + // 重新计算indicatorLeftMargin + if (this.currentIndex === CalendarViewType.YEAR) { + this.indicatorLeftMargin = (this.screenWidth - PADDING * 2) / 6 * 1 - COLUMN_WIDTH / 2; + } else if (this.currentIndex === CalendarViewType.MONTH) { + this.indicatorLeftMargin = (this.screenWidth - PADDING * 2) / 2 - COLUMN_WIDTH / 2; + } else if (this.currentIndex === CalendarViewType.WEEK) { + this.indicatorLeftMargin = (this.screenWidth - PADDING * 2) / 6 * 5 - COLUMN_WIDTH / 2; + } + if (mode === display.FoldDisplayMode.FOLD_DISPLAY_MODE_FULL) { + // 折叠屏展开态显示 + CommonData.DEVICE_TYPE = DeviceType.EXPAND_FOLD; + } else if (mode === display.FoldDisplayMode.FOLD_DISPLAY_MODE_MAIN) { + // 折叠屏折叠态显示 + CommonData.DEVICE_TYPE = DeviceType.PHONE; + } + }; + // 依据cases工程Navigation的mode属性说明,如使用Auto,窗口宽度>=600vp时,采用Split模式显示;窗口宽度<600vp时,采用Stack模式显示。 + private readonly DEVICESIZE: number = 600; + // 不传isPlugin,默认为true。作为插件使用。设置false,适配cases。 + isPlugin: boolean = true; + + /** + * 获取当前屏幕宽度 + */ + getCurrentScreenWidth(): number { + let screenWidth: number = px2vp(display.getDefaultDisplaySync().width); + // 适配cases中Navigation在不同mode时,计算相对需要使用的屏幕宽度。当屏幕宽度大于600vp时,cases工程Navigation的mode采用Split模式显 + // 示,需要重新计算实际页面所需的屏幕宽度。 + if (!this.isPlugin && screenWidth >= this.DEVICESIZE) { + return screenWidth / 2; + } else { + return screenWidth; + } + } + + aboutToAppear() { + // 检查设备是否可折叠。false表示不可折叠,true表示可折叠。 + this.isFoldable = display.isFoldable(); + CommonData.IS_FOLD = this.isFoldable; + if (this.isFoldable) { + // 如果是可折叠设备,注册折叠设备屏幕显示模式变化监听 + display.on('foldDisplayModeChange', this.callback); + if (display.getFoldDisplayMode() === display.FoldDisplayMode.FOLD_DISPLAY_MODE_FULL) { + // 设置折叠屏展开态 + CommonData.DEVICE_TYPE = DeviceType.EXPAND_FOLD; + } + } + // 获取屏幕宽度 + this.screenWidth = this.getCurrentScreenWidth(); + // 初始化自定义分段按钮白色滑块的位置,本案例默认首次加载显示月视图。由于onAreaChange获取indicatorLeftMargin有延迟,首次加载会出现白色 + // 滑块跳变,所以这里计算indicatorLeftMargin的初始位置。 + this.indicatorLeftMargin = (this.screenWidth - COLUMN_WIDTH) / 2 - PADDING; + // 判断是否是rk3568。用屏幕宽度480vp来判断 + if (px2vp(display.getDefaultDisplaySync().width) === 480) { + CommonData.DEVICE_TYPE = DeviceType.RK; + } + } + + aboutToDisappear() { + if (this.isFoldable) { + // 关闭显示设备变化的监听 + display.off('foldDisplayModeChange', this.callback); + } + } + + /** + * 年月信息标题。月视图和周视图显示年月信息,年视图只显示年信息。周视图中如果选中了日期,则优先根据选中日期显示年月信息。 + */ + @Builder + yearMonthTitle() { + Row() { + Text(`${this.currentShowYear}年 ${this.tabSelectedIndex === CalendarViewType.YEAR ? '' : + MONTHS[this.currentShowMonth-1]}`) + .fontSize(FONT_SIZE * TEXT_SCALING) + .fontWeight(FONT_WEIGHT_FIVE_HUNDRED) + // 自定义添加日程组件 + // monthViewController: 可选项。传入该控制器,添加日程后,对应日程点会刷新到月视图上 + // weekViewController: 可选项。传入该控制器,添加日程后,对应日程点会刷新到周视图上 + SchedulePoint({ + monthViewController: this.calendarMonthController, + weekViewController: this.calendarWeekController + }) + } + .padding({ left: $r('app.integer.calendar_switch_size_ten'), right: $r('app.integer.calendar_switch_size_ten') }) + .justifyContent(FlexAlign.SpaceBetween) + .width($r('app.string.calendar_switch_full_size')) + .height($r('app.integer.calendar_switch_size_thirty')) + } + + /** + * 自定义分段按钮 + */ + @Builder + customSegmentButton() { + Stack({ alignContent: Alignment.TopStart }) { + Row() { + } + .width($r('app.string.calendar_switch_full_size')) + .height($r('app.integer.calendar_switch_size_thirty_five')) + .backgroundColor($r('app.color.calendar_switch_segment_button_row_bgcolor')) + .borderRadius($r('app.integer.calendar_switch_border_radius')) + .layoutWeight(LAYOUT_WEIGHT_THREE) + + Column() { + Row() { + } + .borderRadius($r('app.integer.calendar_switch_border_radius')) + .height($r('app.string.calendar_switch_full_size')) + .width((this.screenWidth - PADDING * 2) / 3 - GAP_SPACE) + .backgroundColor(Color.White) + } + .height($r('app.integer.calendar_switch_size_thirty_five')) + .width(COLUMN_WIDTH) + .margin({ left: this.indicatorLeftMargin }) + .padding({ + top: $r('app.integer.calendar_switch_size_three'), + bottom: $r('app.integer.calendar_switch_size_three') + }) + + Row() { + this.customSegmentButtonItem(CalendarViewType.YEAR, '年') + this.customSegmentButtonItem(CalendarViewType.MONTH, '月') + this.customSegmentButtonItem(CalendarViewType.WEEK, '周') + } + .width($r('app.string.calendar_switch_full_size')) + .height($r('app.integer.calendar_switch_size_thirty_five')) + .borderRadius($r('app.integer.calendar_switch_border_radius')) + .backgroundColor(Color.Transparent) + .layoutWeight(LAYOUT_WEIGHT_THREE) + } + .width($r('app.string.calendar_switch_full_size')) + .height($r('app.integer.calendar_switch_size_thirty_five')) + .margin({ + top: $r('app.integer.calendar_switch_size_ten'), + bottom: $r('app.integer.calendar_switch_margin_size_twelve') + }) + } + + /** + * 自定义分段按钮项 + * @param index 自定义分段按钮索引。这里对应自定义日历视图类型。0:年视图YEAR,1:月视图MONTH,2:周视图WEEK + * @param name 自定义分段按钮名。这里对应'年','月','周' + */ + @Builder + customSegmentButtonItem(index: number, name: string) { + Column() { + Text(name) + .width(CUSTOM_SEGMENT_BUTTON_MONTH_WIDTH) + .textAlign(TextAlign.Center) + .height($r('app.integer.calendar_switch_size_thirty_five')) + .fontSize($r('app.integer.calendar_switch_size_fourteen')) + .fontColor(this.currentIndex === index ? Color.Black : + $r('app.color.calendar_switch_segment_button_font_color')) + .fontWeight(this.currentIndex === index ? FONT_WEIGHT_FIVE_HUNDRED : FONT_WEIGHT_FOUR_HUNDRED) + } + .width($r('app.string.calendar_switch_full_size')) + .height($r('app.integer.calendar_switch_size_thirty_five')) + .layoutWeight(LAYOUT_WEIGHT_ONE) + .onClick(() => { + if (index === this.tabSelectedIndex) { + // 点击同一个自定义分段按钮项,不做切换,避免冗余操作 + return; + } + this.tabSelectedIndex = index; + this.tabController.changeIndex(index); + this.currentShowYear = this.currentSelectDay.year; + this.currentShowMonth = this.currentSelectDay.month; + // TODO 知识点:点击自定义分段按钮customSegmentButton进行年、月、周视图间切换。使用视图控制器的swiperRefresh方法刷新对应视图数据。 + if (this.tabSelectedIndex === CalendarViewType.MONTH) { + // 刷新月视图 + this.calendarMonthController.swiperRefresh(CalendarViewType.MONTH); + } else if (this.tabSelectedIndex === CalendarViewType.WEEK) { + // 刷新周视图 + this.calendarWeekController.swiperRefresh(CalendarViewType.WEEK); + } else if (this.tabSelectedIndex === CalendarViewType.YEAR) { + // 刷新年视图 + this.calendarYearController.swiperRefresh(CalendarViewType.YEAR); + } + }) + } + + /** + * 自定义分段按钮切换动画 + * @param duration 动画时长 + * @param leftMargin 自定义分段按钮左侧边距 + */ + startAnimateTo(duration: number, leftMargin: number) { + animateTo({ + duration: duration, // 动画时长 + curve: Curve.Linear, // 动画曲线 + iterations: 1, // 播放次数 + playMode: PlayMode.Normal, // 动画模式 + }, () => { + this.indicatorLeftMargin = leftMargin; + }) + } + + build() { + Column() { + // 年月信息标题(包含添加日程组件) + this.yearMonthTitle() + // 自定义分段按钮 + this.customSegmentButton() + + Tabs({ barPosition: BarPosition.End, index: CalendarViewType.MONTH, controller: this.tabController }) { + TabContent() { + Column() { + // 自定义年视图 + // calendarViewType: 必选项。自定义日历类型。YEAR年视图 MONTH月视图 WEEK周视图 + // onMonthClick: 可选项。年视图月份点击回调。返回年视图点击的年月信息。仅用于年视图。 + // onChangeYearMonth: 可选项。年、月、周视图左右滑动切换回调。返回左右切换年、月、周后的年月信息,其中年视图切换实际只返回切换后年份信息。 + // calendarSwitch: 可选项。年、月、周视图切换场景的相关设置。 + // - controller: 可选项。自定义日历控制器,用于视图切换后的数据刷新。 + // - currentSelectDay: 可选项。记录月、周视图中点击选中的日期信息。 + // - isYearMonthHidden:可选项。是否隐藏自定义日历年、月、周视图中自带的年月信息标题。 + CustomCalendar({ + calendarViewType: CalendarViewType.YEAR, + onMonthClick: (year: number, month: number) => { + if (this.tabController) { + // 切到月视图 + this.tabController.changeIndex(1); + // 刷新年月信息标题 + this.currentShowYear = year; + this.currentShowMonth = month; + // 刷新在年视图上点击月后要跳转的月视图数据 + this.calendarMonthController.swiperYearToMonthRefresh(year, month); + } + }, + onChangeYearMonth: (year: number, month: number) => { + this.currentShowYear = year; + }, + calendarSwitch: { + controller: this.calendarYearController, + currentSelectDay: this.currentSelectDay, + isYearMonthHidden: this.isYearMonthHidden + } + }) + } + .width($r('app.string.calendar_switch_full_size')) + .height($r('app.string.calendar_switch_full_size')) + } + + TabContent() { + Column() { + // 自定义月视图 + // calendarViewType: 必选项。自定义日历类型。YEAR年视图 MONTH月视图 WEEK周视图 + // calendarStyle: 可选项。自定义日历样式。仅用于月、周视图。 + // - textScaling: 可选项。月视图和周视图中的公历、农历、星期、年月信息标题文字缩放比例。 + // - backgroundColor: 可选项。今天选中日期的背景色。 + // - monthDayColor: 可选项。本月公历日期颜色。 + // - noMonthDayColor: 可选项。非本月公历日期颜色,仅对月视图有效。 + // - lunarColor: 可选项。本月农历字体颜色。 + // onDateClick: 可选项。日期点击回调。返回点击日期的年月日信息。仅用于月、周视图。 + // onChangeYearMonth: 可选项。年、月、周视图左右滑动切换回调,返回左右切换视图后的年月信息,其中年视图切换实际只返回切换后年份信息。 + // calendarSwitch: 可选项。用于年、月、周视图切换场景的相关设置。 + // - controller: 可选项。自定义日历控制器,用于视图切换后的数据刷新。 + // - currentSelectDay: 可选项。记录月、周视图中点击选中的日期信息。 + // - isYearMonthHidden:可选项。是否隐藏自定义日历年、月、周视图中自带的年月信息标题。 + CustomCalendar({ + calendarViewType: CalendarViewType.MONTH, + calendarStyle: { + textScaling: TEXT_SCALING, + backgroundColor: Color.Red, + monthDayColor: Color.Black, + noMonthDayColor: Color.Gray, + lunarColor: Color.Gray + }, + onDateClick: (year: number, month: number, date: number) => { + this.currentSelectDay.year = year; + this.currentSelectDay.month = month; + this.currentSelectDay.date = date; + }, + onChangeYearMonth: (year: number, month: number) => { + this.currentShowYear = year; + this.currentShowMonth = month; + }, + calendarSwitch: { + controller: this.calendarMonthController, + currentSelectDay: this.currentSelectDay, + isYearMonthHidden: this.isYearMonthHidden, + } + }) + } + .width($r('app.string.calendar_switch_full_size')) + .height($r('app.string.calendar_switch_full_size')) + } + + TabContent() { + Column() { + // 自定义周视图 + // calendarViewType: 必选项。自定义日历类型。YEAR年视图 MONTH月视图 WEEK周视图 + // calendarStyle: 可选项。自定义日历样式。仅用于月、周视图。 + // - textScaling: 可选项。月视图和周视图中的公历、农历、星期、年月信息标题文字缩放比例。 + // - backgroundColor: 可选项。今天选中日期的背景色。 + // - monthDayColor: 可选项。本月公历日期颜色。 + // - noMonthDayColor: 可选项。非本月公历日期颜色,仅对月视图有效。 + // - lunarColor: 可选项。本月农历字体颜色。 + // onDateClick: 可选项。日期点击回调。返回点击日期的年月日信息。仅用于月、周视图。 + // onChangeYearMonth: 可选项。年、月、周视图左右滑动切换回调,返回左右切换视图后的年月信息,其中年视图切换实际只返回切换后年份信息。 + // calendarSwitch: 可选项。用于年、月、周视图切换场景的相关设置。 + // - controller: 可选项。自定义日历控制器,用于视图切换后的数据刷新。 + // - currentSelectDay: 可选项。记录月、周视图中点击选中的日期信息。 + // - isYearMonthHidden:可选项。是否隐藏自定义日历年、月、周视图中自带的年月信息标题。 + CustomCalendar({ + calendarViewType: CalendarViewType.WEEK, + calendarStyle: { + textScaling: TEXT_SCALING, + backgroundColor: Color.Red, + monthDayColor: Color.Black, + lunarColor: Color.Gray + }, + onDateClick: (year: number, month: number, date: number) => { + this.currentSelectDay.year = year; + this.currentSelectDay.month = month; + this.currentSelectDay.date = date; + }, + onChangeYearMonth: (year: number, month: number) => { + this.currentShowYear = year; + this.currentShowMonth = month; + }, + calendarSwitch: { + controller: this.calendarWeekController, + currentSelectDay: this.currentSelectDay, + isYearMonthHidden: this.isYearMonthHidden + } + }) + } + .width($r('app.string.calendar_switch_full_size')) + .height($r('app.string.calendar_switch_full_size')) + } + } + .animationDuration(this.animationDuration) + .onAnimationStart((index: number, targetIndex: number, event: TabsAnimationEvent) => { + // 在年视图中点击月,切换到月视图时,需要显示'年月信息标题'中的的月份信息 + if (index === CalendarViewType.YEAR && targetIndex === CalendarViewType.MONTH) { + this.tabSelectedIndex = CalendarViewType.MONTH; + } + // 切换动画开始时触发该回调。白色滑块跟着页面一起滑动。 + this.currentIndex = targetIndex; + if (targetIndex === CalendarViewType.YEAR) { + // 传入自定义分段按钮左侧边距 点击“年”的分段按钮时,分段按钮白色滑块移动到屏幕1/6位置 + this.startAnimateTo(this.animationDuration, (this.screenWidth - PADDING * 2) / 6 * 1 - COLUMN_WIDTH / 2); + } else if (targetIndex === CalendarViewType.MONTH) { + // 传入自定义分段按钮左侧边距 点击“月”的分段按钮时,分段按钮白色滑块移动到屏幕3/6位置 + this.startAnimateTo(this.animationDuration, (this.screenWidth - PADDING * 2) / 2 - COLUMN_WIDTH / 2); + } else if (targetIndex === CalendarViewType.WEEK) { + // 传入自定义分段按钮左侧边距 点击“周”的分段按钮时,分段按钮白色滑块移动到屏幕5/6位置 + this.startAnimateTo(this.animationDuration, (this.screenWidth - PADDING * 2) / 6 * 5 - COLUMN_WIDTH / 2); + } + }) + .onAppear(() => { + // TODO: 高性能知识点: 组件挂载显示后触发此回调,预加载年视图数据,避免首次切换到年视图时出现卡顿问题 + // 针对月视图切换周视图场景,需要预加载周视图(索引2),不然在月视图切换月份,选择日期后,再切换到周视图,周视图不会刷新 + this.tabController.preloadItems([0, 2]); // 索引0对应年视图,索引2对应周视图 + }) + .layoutWeight(LAYOUT_WEIGHT_ONE) + .scrollable(false) + .barHeight($r('app.integer.calendar_switch_zero')) + } + .width($r('app.string.calendar_switch_full_size')) + .height($r('app.string.calendar_switch_full_size')) + .padding(PADDING) + } +} \ No newline at end of file diff --git a/code/UI/CalendarViewSwitch/entry/oh_modules/calendarswitch/src/main/module.json5 b/code/UI/CalendarViewSwitch/entry/oh_modules/calendarswitch/src/main/module.json5 new file mode 100644 index 0000000000000000000000000000000000000000..76af7397b174333b3589d75d1eb27b68be0f2859 --- /dev/null +++ b/code/UI/CalendarViewSwitch/entry/oh_modules/calendarswitch/src/main/module.json5 @@ -0,0 +1,46 @@ +/* + * 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. + */ +{ + "module": { + "name": "calendarswitch", + "type": "har", + "deviceTypes": [ + "default", + "tablet" + ], + "requestPermissions":[ + { + "name" : "ohos.permission.READ_CALENDAR", + "reason": "$string:app_name", + "usedScene": { + "abilities": [ + "FormAbility" + ], + "when":"always" + } + }, + { + "name" : "ohos.permission.WRITE_CALENDAR", + "reason": "$string:app_name", + "usedScene": { + "abilities": [ + "FormAbility" + ], + "when":"always" + } + } + ] + } +} diff --git a/code/UI/CalendarViewSwitch/entry/oh_modules/calendarswitch/src/main/resources/base/element/color.json b/code/UI/CalendarViewSwitch/entry/oh_modules/calendarswitch/src/main/resources/base/element/color.json new file mode 100644 index 0000000000000000000000000000000000000000..508d2817d3c5ca04d0b2ca12994c13c398edd189 --- /dev/null +++ b/code/UI/CalendarViewSwitch/entry/oh_modules/calendarswitch/src/main/resources/base/element/color.json @@ -0,0 +1,24 @@ +{ + "color": [ + { + "name": "calendar_switch_border_color", + "value": "#24A844" + }, + { + "name": "calendar_switch_segment_button_bgcolor", + "value": "#FFFEFEFE" + }, + { + "name": "calendar_switch_segment_button_font_color", + "value": "#4e4e4e" + }, + { + "name": "calendar_switch_segment_button_row_bgcolor", + "value": "#e7e7e7" + }, + { + "name": "calendar_switch_schedule_point_color", + "value": "#ffababab" + } + ] +} \ No newline at end of file diff --git a/code/UI/CalendarViewSwitch/entry/oh_modules/calendarswitch/src/main/resources/base/element/integer.json b/code/UI/CalendarViewSwitch/entry/oh_modules/calendarswitch/src/main/resources/base/element/integer.json new file mode 100644 index 0000000000000000000000000000000000000000..05210e5f549b42d078e9fc824f24c54a49ffb9c1 --- /dev/null +++ b/code/UI/CalendarViewSwitch/entry/oh_modules/calendarswitch/src/main/resources/base/element/integer.json @@ -0,0 +1,124 @@ +{ + "integer": [ + { + "name": "calendar_switch_size_forty", + "value":40 + }, + { + "name": "calendar_switch_size_ten", + "value":10 + }, + { + "name": "calendar_switch_size_twenty_five", + "value":25 + }, + { + "name": "calendar_switch_size_three", + "value":3 + }, + { + "name": "calendar_switch_size_four", + "value":4 + }, + { + "name": "calendar_switch_size_seven", + "value":7 + }, + { + "name": "calendar_switch_margin_left", + "value":6 + }, + { + "name": "calendar_switch_margin_bottom", + "value":5 + }, + { + "name": "calendar_switch_margin_size_twelve", + "value":12 + }, + { + "name": "calendar_switch_size_fifteen", + "value": 15 + }, + { + "name": "calendar_switch_columns_gap", + "value": 0 + }, + { + "name": "calendar_switch_rows_gap", + "value": 10 + }, + { + "name": "calendar_switch_size_thirty_five", + "value": 35 + }, + { + "name": "calendar_switch_size_fourteen", + "value": 14 + }, + { + "name": "calendar_switch_border_radius", + "value": 20 + }, + { + "name": "calendar_switch_zero", + "value": 0 + }, + { + "name": "calendar_switch_size_eighteen", + "value": 18 + }, + { + "name": "calendar_switch_size_twenty", + "value": 20 + }, + { + "name": "calendar_switch_size_thirty", + "value": 30 + }, + { + "name": "calendar_switch_size_forty_six", + "value": 46 + }, + { + "name": "calendar_switch_size_one", + "value": 1 + }, + { + "name": "calendar_switch_size_forty_eight", + "value": 48 + }, + { + "name": "calendar_switch_size_eighty", + "value": 80 + }, + { + "name": "calendar_switch_size_ninety", + "value": 90 + }, + { + "name": "calendar_switch_size_hundred", + "value": 100 + }, + { + "name": "calendar_switch_size_sixteen", + "value": 16 + }, + { + "name": "calendar_switch_size_twenty_two", + "value": 22 + }, + { + "name": "calendar_switch_size_eight", + "value": 8 + }, + { + "name": "calendar_switch_size_sixty", + "value": 60 + }, + { + "name": "calendar_switch_two_hundred_fifty", + "value": 250 + } + ] +} \ No newline at end of file diff --git a/code/UI/CalendarViewSwitch/entry/oh_modules/calendarswitch/src/main/resources/base/element/string.json b/code/UI/CalendarViewSwitch/entry/oh_modules/calendarswitch/src/main/resources/base/element/string.json new file mode 100644 index 0000000000000000000000000000000000000000..f03eb0fdd9d5a11fec51e50a7a3a377fdfda3b2f --- /dev/null +++ b/code/UI/CalendarViewSwitch/entry/oh_modules/calendarswitch/src/main/resources/base/element/string.json @@ -0,0 +1,72 @@ +{ + "string": [ + { + "name": "calendar_switch_full_size", + "value": "100%" + }, + { + "name": "calendar_switch_size_twenty_three", + "value": "23%" + }, + { + "name": "calendar_switch_size_twenty_five", + "value": "25%" + }, + { + "name": "calendar_switch_repeat", + "value": "重复" + }, + { + "name": "calendar_switch_reminder_time", + "value": "提醒时间" + }, + { + "name": "calendar_switch_cancel", + "value": "取消" + }, + { + "name": "calendar_switch_new_schedule", + "value": "新建日程" + }, + { + "name": "calendar_switch_add", + "value": "添加" + }, + { + "name": "calendar_switch_title_msg", + "value": "请填写标题后再试" + }, + { + "name": "calendar_switch_time_msg", + "value": "结束时间需要大于开始时间,请修改后再试" + }, + { + "name": "calendar_switch_add_completed", + "value": "日程已添加,可到系统日历中查看新增日程" + }, + { + "name": "calendar_switch_title", + "value": "标题" + }, + { + "name": "calendar_switch_location", + "value": "地点" + }, + { + "name": "calendar_switch_start_time", + "value": "开始时间" + }, + { + "name": "calendar_switch_end_time", + "value": "结束时间" + }, + { + "name": "calendar_switch_button_style", + "value": "20fp" + }, + { + "name": "calendar_switch_describe", + "value": "说明" + } + ] +} diff --git a/code/UI/CalendarViewSwitch/entry/oh_modules/calendarswitch/src/main/resources/base/media/calendar_switch_add.png b/code/UI/CalendarViewSwitch/entry/oh_modules/calendarswitch/src/main/resources/base/media/calendar_switch_add.png new file mode 100644 index 0000000000000000000000000000000000000000..ecc6e4ad34737b9c8a25239dba0ee94cbdc6a902 Binary files /dev/null and b/code/UI/CalendarViewSwitch/entry/oh_modules/calendarswitch/src/main/resources/base/media/calendar_switch_add.png differ diff --git a/code/UI/CalendarViewSwitch/entry/oh_modules/calendarswitch/src/main/resources/base/media/calendar_switch_ok.svg b/code/UI/CalendarViewSwitch/entry/oh_modules/calendarswitch/src/main/resources/base/media/calendar_switch_ok.svg new file mode 100644 index 0000000000000000000000000000000000000000..d3f2bd7564eb79dd9315599759a68ca285d4c8b9 --- /dev/null +++ b/code/UI/CalendarViewSwitch/entry/oh_modules/calendarswitch/src/main/resources/base/media/calendar_switch_ok.svg @@ -0,0 +1,13 @@ + + + Public/ic_public_ok + + + + + + + + + + \ No newline at end of file diff --git a/code/UI/CalendarViewSwitch/entry/oh_modules/calendarswitch/src/main/resources/base/media/calendar_switch_spinner.svg b/code/UI/CalendarViewSwitch/entry/oh_modules/calendarswitch/src/main/resources/base/media/calendar_switch_spinner.svg new file mode 100644 index 0000000000000000000000000000000000000000..02b175fddf7ec5846b8cfd227ab2c7f6b359685d --- /dev/null +++ b/code/UI/CalendarViewSwitch/entry/oh_modules/calendarswitch/src/main/resources/base/media/calendar_switch_spinner.svg @@ -0,0 +1,13 @@ + + + Public/ic_public_spinner + + + + + + + + + + \ No newline at end of file diff --git a/code/UI/CalendarViewSwitch/local.properties b/code/UI/CalendarViewSwitch/local.properties new file mode 100644 index 0000000000000000000000000000000000000000..9b1429c2fd69d5d4adaa74031cd22995c046929a --- /dev/null +++ b/code/UI/CalendarViewSwitch/local.properties @@ -0,0 +1,8 @@ +# 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. +sdk.dir=D:/IDE/OH_sdk/OpenHarmony/windows \ No newline at end of file diff --git a/code/UI/CalendarViewSwitch/oh-package-lock.json5 b/code/UI/CalendarViewSwitch/oh-package-lock.json5 new file mode 100644 index 0000000000000000000000000000000000000000..f538ae290f499a46efa12e593420d47f6e9024ff --- /dev/null +++ b/code/UI/CalendarViewSwitch/oh-package-lock.json5 @@ -0,0 +1,19 @@ +{ + "meta": { + "stableOrder": true + }, + "lockfileVersion": 3, + "ATTENTION": "THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.", + "specifiers": { + "@ohos/hypium@1.0.19": "@ohos/hypium@1.0.19" + }, + "packages": { + "@ohos/hypium@1.0.19": { + "name": "@ohos/hypium", + "version": "1.0.19", + "integrity": "sha512-cEjDgLFCm3cWZDeRXk7agBUkPqjWxUo6AQeiu0gEkb3J8ESqlduQLSIXeo3cCsm8U/asL7iKjF85ZyOuufAGSQ==", + "resolved": "https://ohpm.openharmony.cn/ohpm/@ohos/hypium/-/hypium-1.0.19.har", + "registryType": "ohpm" + } + } +} \ No newline at end of file diff --git a/code/UI/CalendarViewSwitch/oh_modules/.ohpm/@ohos+hypium@1.0.19/oh_modules/@ohos/hypium/BuildProfile.ets b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/@ohos+hypium@1.0.19/oh_modules/@ohos/hypium/BuildProfile.ets new file mode 100644 index 0000000000000000000000000000000000000000..b054e98af7ad6092740cef28d7dcf6207e514dcb --- /dev/null +++ b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/@ohos+hypium@1.0.19/oh_modules/@ohos/hypium/BuildProfile.ets @@ -0,0 +1,5 @@ +export default class BuildProfile { + static readonly HAR_VERSION = '1.0.19'; + static readonly BUILD_MODE_NAME = 'debug'; + static readonly DEBUG = true; +} \ No newline at end of file diff --git a/code/UI/CalendarViewSwitch/oh_modules/.ohpm/@ohos+hypium@1.0.19/oh_modules/@ohos/hypium/CHANGELOG.md b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/@ohos+hypium@1.0.19/oh_modules/@ohos/hypium/CHANGELOG.md new file mode 100644 index 0000000000000000000000000000000000000000..309d707c3d19f506f5582509edf1d0db9011a5b1 --- /dev/null +++ b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/@ohos+hypium@1.0.19/oh_modules/@ohos/hypium/CHANGELOG.md @@ -0,0 +1,24 @@ +## 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.18-rc.1 +规范日志格式 +## 1.0.19 +- 规范日志格式 +- 代码规范整改 \ No newline at end of file diff --git a/code/UI/CalendarViewSwitch/oh_modules/.ohpm/@ohos+hypium@1.0.19/oh_modules/@ohos/hypium/LICENSE b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/@ohos+hypium@1.0.19/oh_modules/@ohos/hypium/LICENSE new file mode 100644 index 0000000000000000000000000000000000000000..4947287f7b5ccb5d1e8b7b2d3aa5d89f322c160d --- /dev/null +++ b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/@ohos+hypium@1.0.19/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/code/UI/CalendarViewSwitch/oh_modules/.ohpm/@ohos+hypium@1.0.19/oh_modules/@ohos/hypium/README.md b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/@ohos+hypium@1.0.19/oh_modules/@ohos/hypium/README.md new file mode 100644 index 0000000000000000000000000000000000000000..6d795105533a9b3b9949e91d2c3dd14e8f867433 --- /dev/null +++ b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/@ohos+hypium@1.0.19/oh_modules/@ohos/hypium/README.md @@ -0,0 +1,219 @@ +
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后缀 +``` + +## 安装使用 +*** +- 在DevEco Studio内使用Hypium +- 工程级package.json内配置: +```json +"dependencies": { + "@ohos/hypium": "1.0.19" +} +``` +注: +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/code/UI/CalendarViewSwitch/oh_modules/.ohpm/@ohos+hypium@1.0.19/oh_modules/@ohos/hypium/build-profile.json5 b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/@ohos+hypium@1.0.19/oh_modules/@ohos/hypium/build-profile.json5 new file mode 100644 index 0000000000000000000000000000000000000000..312d38eb08629793b3484c7373213f22840c8d82 --- /dev/null +++ b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/@ohos+hypium@1.0.19/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/code/UI/CalendarViewSwitch/oh_modules/.ohpm/@ohos+hypium@1.0.19/oh_modules/@ohos/hypium/hvigorfile.ts b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/@ohos+hypium@1.0.19/oh_modules/@ohos/hypium/hvigorfile.ts new file mode 100644 index 0000000000000000000000000000000000000000..42187071482d292588ad40babeda74f7b8d97a23 --- /dev/null +++ b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/@ohos+hypium@1.0.19/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/code/UI/CalendarViewSwitch/oh_modules/.ohpm/@ohos+hypium@1.0.19/oh_modules/@ohos/hypium/index.d.ts b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/@ohos+hypium@1.0.19/oh_modules/@ohos/hypium/index.d.ts new file mode 100644 index 0000000000000000000000000000000000000000..7272b5fa839a2cd510d0c70d517bb6800133dba2 --- /dev/null +++ b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/@ohos+hypium@1.0.19/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/code/UI/CalendarViewSwitch/oh_modules/.ohpm/@ohos+hypium@1.0.19/oh_modules/@ohos/hypium/index.ets b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/@ohos+hypium@1.0.19/oh_modules/@ohos/hypium/index.ets new file mode 100644 index 0000000000000000000000000000000000000000..02a237f1c999d1f48b3974b6076d5dae9213245a --- /dev/null +++ b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/@ohos+hypium@1.0.19/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): (f?: 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/code/UI/CalendarViewSwitch/oh_modules/.ohpm/@ohos+hypium@1.0.19/oh_modules/@ohos/hypium/index.js b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/@ohos+hypium@1.0.19/oh_modules/@ohos/hypium/index.js new file mode 100644 index 0000000000000000000000000000000000000000..02d06d9d1b4b478aa2aec70ba3a73a5e123c98db --- /dev/null +++ b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/@ohos+hypium@1.0.19/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/code/UI/CalendarViewSwitch/oh_modules/.ohpm/@ohos+hypium@1.0.19/oh_modules/@ohos/hypium/index.ts b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/@ohos+hypium@1.0.19/oh_modules/@ohos/hypium/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..b7082ebc98214b58d41e8681791809f1aee48f12 --- /dev/null +++ b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/@ohos+hypium@1.0.19/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/code/UI/CalendarViewSwitch/oh_modules/.ohpm/@ohos+hypium@1.0.19/oh_modules/@ohos/hypium/oh-package.json5 b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/@ohos+hypium@1.0.19/oh_modules/@ohos/hypium/oh-package.json5 new file mode 100644 index 0000000000000000000000000000000000000000..d91344eda60f35477a5caf4bf5c116ffac2e53db --- /dev/null +++ b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/@ohos+hypium@1.0.19/oh_modules/@ohos/hypium/oh-package.json5 @@ -0,0 +1 @@ +{"name":"@ohos/hypium","version":"1.0.19","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":{}} diff --git a/code/UI/CalendarViewSwitch/oh_modules/.ohpm/@ohos+hypium@1.0.19/oh_modules/@ohos/hypium/src/main/Constant.js b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/@ohos+hypium@1.0.19/oh_modules/@ohos/hypium/src/main/Constant.js new file mode 100644 index 0000000000000000000000000000000000000000..f470d69cd9a3302b19d45c147ca7d7c1dd8a3b18 --- /dev/null +++ b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/@ohos+hypium@1.0.19/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/code/UI/CalendarViewSwitch/oh_modules/.ohpm/@ohos+hypium@1.0.19/oh_modules/@ohos/hypium/src/main/core.js b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/@ohos+hypium@1.0.19/oh_modules/@ohos/hypium/src/main/core.js new file mode 100644 index 0000000000000000000000000000000000000000..cfcb5f17287208f5e6869b4248faf6c9093002d9 --- /dev/null +++ b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/@ohos+hypium@1.0.19/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/code/UI/CalendarViewSwitch/oh_modules/.ohpm/@ohos+hypium@1.0.19/oh_modules/@ohos/hypium/src/main/event.js b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/@ohos+hypium@1.0.19/oh_modules/@ohos/hypium/src/main/event.js new file mode 100644 index 0000000000000000000000000000000000000000..3be0211f01646c9c269c2425cbee82c87ac6d9ea --- /dev/null +++ b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/@ohos+hypium@1.0.19/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/code/UI/CalendarViewSwitch/oh_modules/.ohpm/@ohos+hypium@1.0.19/oh_modules/@ohos/hypium/src/main/interface.js b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/@ohos+hypium@1.0.19/oh_modules/@ohos/hypium/src/main/interface.js new file mode 100644 index 0000000000000000000000000000000000000000..1bf43509ac3f70f1275e1da79388e1511e72a3f9 --- /dev/null +++ b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/@ohos+hypium@1.0.19/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/code/UI/CalendarViewSwitch/oh_modules/.ohpm/@ohos+hypium@1.0.19/oh_modules/@ohos/hypium/src/main/module.json b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/@ohos+hypium@1.0.19/oh_modules/@ohos/hypium/src/main/module.json new file mode 100644 index 0000000000000000000000000000000000000000..b0e022bd13205c4c3310480d6732db4707193b3a --- /dev/null +++ b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/@ohos+hypium@1.0.19/oh_modules/@ohos/hypium/src/main/module.json @@ -0,0 +1,26 @@ +{ + "app": { + "bundleName": "com.ohos.myapplication", + "debug": true, + "versionCode": 1000000, + "versionName": "1.0.0", + "minAPIVersion": 40100011, + "targetAPIVersion": 40100011, + "apiReleaseType": "Beta1", + "compileSdkVersion": "4.1.0.55", + "compileSdkType": "HarmonyOS", + "bundleType": "app" + }, + "module": { + "name": "hypium", + "type": "har", + "deviceTypes": [ + "default", + "tablet", + "tv", + "wearable", + "car" + ], + "installationFree": false + } +} diff --git a/code/UI/CalendarViewSwitch/oh_modules/.ohpm/@ohos+hypium@1.0.19/oh_modules/@ohos/hypium/src/main/module/assert/ExpectExtend.js b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/@ohos+hypium@1.0.19/oh_modules/@ohos/hypium/src/main/module/assert/ExpectExtend.js new file mode 100644 index 0000000000000000000000000000000000000000..d10d15e6f9955c6d04610101f8766c951ee1a35d --- /dev/null +++ b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/@ohos+hypium@1.0.19/oh_modules/@ohos/hypium/src/main/module/assert/ExpectExtend.js @@ -0,0 +1,85 @@ +/* + * 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 assertNull from './assertNull'; +import assertClose from './assertClose'; +import assertContain from './assertContain'; +import assertLess from './assertLess'; +import assertLarger from './assertLarger'; +import assertFail from './assertFail'; +import assertUndefined from './assertUndefined'; +import assertFalse from './assertFalse'; +import assertInstanceOf from './assertInstanceOf'; +import assertThrowError from './assertThrowError'; +import assertLargerOrEqual from './assertLargerOrEqual' +import assertLessOrEqual from './assertLessOrEqual' +import assertNaN from './assertNaN' +import assertNegUnlimited from './assertNegUnlimited' +import assertPosUnlimited from './assertPosUnlimited' +import assertDeepEquals from './deepEquals/assertDeepEquals' +import assertPromiseIsPending from './assertPromiseIsPending'; +import assertPromiseIsRejected from './assertPromiseIsRejected'; +import assertPromiseIsRejectedWith from './assertPromiseIsRejectedWith'; +import assertPromiseIsRejectedWithError from './assertPromiseIsRejectedWithError'; +import assertPromiseIsResolved from './assertPromiseIsResolved'; +import assertPromiseIsResolvedWith from './assertPromiseIsResolvedWith'; +class ExpectExtend { + constructor(attr) { + this.id = attr.id; + this.matchers = {}; + } + + extendsMatchers() { + this.matchers.assertNull = assertNull; + this.matchers.assertClose = assertClose; + this.matchers.assertContain = assertContain; + this.matchers.assertLess = assertLess; + this.matchers.assertLarger = assertLarger; + this.matchers.assertFail = assertFail; + this.matchers.assertUndefined = assertUndefined; + this.matchers.assertFalse = assertFalse; + this.matchers.assertInstanceOf = assertInstanceOf; + this.matchers.assertThrowError = assertThrowError; + this.matchers.assertLargerOrEqual = assertLargerOrEqual; + this.matchers.assertLessOrEqual = assertLessOrEqual; + this.matchers.assertNaN = assertNaN; + this.matchers.assertNegUnlimited = assertNegUnlimited; + this.matchers.assertPosUnlimited = assertPosUnlimited; + this.matchers.assertDeepEquals = assertDeepEquals; + this.matchers.assertPromiseIsPending = assertPromiseIsPending; + this.matchers.assertPromiseIsRejected = assertPromiseIsRejected; + this.matchers.assertPromiseIsRejectedWith = assertPromiseIsRejectedWith; + this.matchers.assertPromiseIsRejectedWithError = assertPromiseIsRejectedWithError; + this.matchers.assertPromiseIsResolved = assertPromiseIsResolved; + this.matchers.assertPromiseIsResolvedWith = assertPromiseIsResolvedWith; + } + + init(coreContext) { + this.coreContext = coreContext; + this.extendsMatchers(); + const expectService = this.coreContext.getDefaultService('expect'); + expectService.addMatchers(this.matchers); + } + + apis() { + return { + 'expect': function (actualValue) { + return this.coreContext.getDefaultService('expect').expect(actualValue); + } + }; + } +} + +export default ExpectExtend; diff --git a/code/UI/CalendarViewSwitch/oh_modules/.ohpm/@ohos+hypium@1.0.19/oh_modules/@ohos/hypium/src/main/module/assert/assertClose.js b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/@ohos+hypium@1.0.19/oh_modules/@ohos/hypium/src/main/module/assert/assertClose.js new file mode 100644 index 0000000000000000000000000000000000000000..7e692bd25f1c026640978a042a9c9f64b0e8d5d3 --- /dev/null +++ b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/@ohos+hypium@1.0.19/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/code/UI/CalendarViewSwitch/oh_modules/.ohpm/@ohos+hypium@1.0.19/oh_modules/@ohos/hypium/src/main/module/assert/assertContain.js b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/@ohos+hypium@1.0.19/oh_modules/@ohos/hypium/src/main/module/assert/assertContain.js new file mode 100644 index 0000000000000000000000000000000000000000..7fba0d9755503e5e926f6c1a4e425e0d1cf47570 --- /dev/null +++ b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/@ohos+hypium@1.0.19/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/code/UI/CalendarViewSwitch/oh_modules/.ohpm/@ohos+hypium@1.0.19/oh_modules/@ohos/hypium/src/main/module/assert/assertFail.js b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/@ohos+hypium@1.0.19/oh_modules/@ohos/hypium/src/main/module/assert/assertFail.js new file mode 100644 index 0000000000000000000000000000000000000000..8ab4ac5caef712c75c4eac49dfbbb91d33669d9a --- /dev/null +++ b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/@ohos+hypium@1.0.19/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/code/UI/CalendarViewSwitch/oh_modules/.ohpm/@ohos+hypium@1.0.19/oh_modules/@ohos/hypium/src/main/module/assert/assertFalse.js b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/@ohos+hypium@1.0.19/oh_modules/@ohos/hypium/src/main/module/assert/assertFalse.js new file mode 100644 index 0000000000000000000000000000000000000000..c5008e94f4b2ce13ed35b604811793c76b542347 --- /dev/null +++ b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/@ohos+hypium@1.0.19/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/code/UI/CalendarViewSwitch/oh_modules/.ohpm/@ohos+hypium@1.0.19/oh_modules/@ohos/hypium/src/main/module/assert/assertInstanceOf.js b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/@ohos+hypium@1.0.19/oh_modules/@ohos/hypium/src/main/module/assert/assertInstanceOf.js new file mode 100644 index 0000000000000000000000000000000000000000..1e11b93f7251c67f5455c5007cd7be268aa53b32 --- /dev/null +++ b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/@ohos+hypium@1.0.19/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/code/UI/CalendarViewSwitch/oh_modules/.ohpm/@ohos+hypium@1.0.19/oh_modules/@ohos/hypium/src/main/module/assert/assertLarger.js b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/@ohos+hypium@1.0.19/oh_modules/@ohos/hypium/src/main/module/assert/assertLarger.js new file mode 100644 index 0000000000000000000000000000000000000000..a74f4a8cedaf3add9c2dc2d3799081a83198732f --- /dev/null +++ b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/@ohos+hypium@1.0.19/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/code/UI/CalendarViewSwitch/oh_modules/.ohpm/@ohos+hypium@1.0.19/oh_modules/@ohos/hypium/src/main/module/assert/assertLargerOrEqual.js b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/@ohos+hypium@1.0.19/oh_modules/@ohos/hypium/src/main/module/assert/assertLargerOrEqual.js new file mode 100644 index 0000000000000000000000000000000000000000..e847e6c217364b7f69c173c66fb98d10efc45ef1 --- /dev/null +++ b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/@ohos+hypium@1.0.19/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/code/UI/CalendarViewSwitch/oh_modules/.ohpm/@ohos+hypium@1.0.19/oh_modules/@ohos/hypium/src/main/module/assert/assertLess.js b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/@ohos+hypium@1.0.19/oh_modules/@ohos/hypium/src/main/module/assert/assertLess.js new file mode 100644 index 0000000000000000000000000000000000000000..17e84b0abaeb20804048a5a15c19e0603634846d --- /dev/null +++ b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/@ohos+hypium@1.0.19/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/code/UI/CalendarViewSwitch/oh_modules/.ohpm/@ohos+hypium@1.0.19/oh_modules/@ohos/hypium/src/main/module/assert/assertLessOrEqual.js b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/@ohos+hypium@1.0.19/oh_modules/@ohos/hypium/src/main/module/assert/assertLessOrEqual.js new file mode 100644 index 0000000000000000000000000000000000000000..f754f97ffa9d24e7852efe2423a1dd35d448af82 --- /dev/null +++ b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/@ohos+hypium@1.0.19/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/code/UI/CalendarViewSwitch/oh_modules/.ohpm/@ohos+hypium@1.0.19/oh_modules/@ohos/hypium/src/main/module/assert/assertNaN.js b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/@ohos+hypium@1.0.19/oh_modules/@ohos/hypium/src/main/module/assert/assertNaN.js new file mode 100644 index 0000000000000000000000000000000000000000..8d45d6a93b86c5ed325a68b32ff014835993a58e --- /dev/null +++ b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/@ohos+hypium@1.0.19/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/code/UI/CalendarViewSwitch/oh_modules/.ohpm/@ohos+hypium@1.0.19/oh_modules/@ohos/hypium/src/main/module/assert/assertNegUnlimited.js b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/@ohos+hypium@1.0.19/oh_modules/@ohos/hypium/src/main/module/assert/assertNegUnlimited.js new file mode 100644 index 0000000000000000000000000000000000000000..ceac555afc826e057970e6cfe9c73b322c672aa2 --- /dev/null +++ b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/@ohos+hypium@1.0.19/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/code/UI/CalendarViewSwitch/oh_modules/.ohpm/@ohos+hypium@1.0.19/oh_modules/@ohos/hypium/src/main/module/assert/assertNull.js b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/@ohos+hypium@1.0.19/oh_modules/@ohos/hypium/src/main/module/assert/assertNull.js new file mode 100644 index 0000000000000000000000000000000000000000..53a7bad827323a98d3302a4e7eea679551b459c5 --- /dev/null +++ b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/@ohos+hypium@1.0.19/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/code/UI/CalendarViewSwitch/oh_modules/.ohpm/@ohos+hypium@1.0.19/oh_modules/@ohos/hypium/src/main/module/assert/assertPosUnlimited.js b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/@ohos+hypium@1.0.19/oh_modules/@ohos/hypium/src/main/module/assert/assertPosUnlimited.js new file mode 100644 index 0000000000000000000000000000000000000000..6e68c0e2b6c499f4dc3dd56c13e9ea1073a3c54c --- /dev/null +++ b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/@ohos+hypium@1.0.19/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; diff --git a/code/UI/CalendarViewSwitch/oh_modules/.ohpm/@ohos+hypium@1.0.19/oh_modules/@ohos/hypium/src/main/module/assert/assertPromiseIsPending.js b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/@ohos+hypium@1.0.19/oh_modules/@ohos/hypium/src/main/module/assert/assertPromiseIsPending.js new file mode 100644 index 0000000000000000000000000000000000000000..7e2ca2ce14d50c39554fc1157d6d4eb9329d5c39 --- /dev/null +++ b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/@ohos+hypium@1.0.19/oh_modules/@ohos/hypium/src/main/module/assert/assertPromiseIsPending.js @@ -0,0 +1,42 @@ +/* + * 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 isPromiseLike from './isPromiseLike'; + +function assertPromiseIsPending(actualPromise) { + if (!isPromiseLike(actualPromise)) { + return Promise.reject().then(function () { + }, function () { + return {pass: false, message: 'Expected not be called on a promise.'}; + }); + } + const helper = {}; + return Promise.race([actualPromise, Promise.resolve(helper)]).then( + function (got) { + return helper === got ? {pass: true, message: 'actualValue is isPending'} + : { + pass: false, + message: 'expect isPending, actualValue is resolve' + }; + }, + function () { + return { + pass: false + , message: 'expect isPending, actualValue is reject' + }; + }); +} + +export default assertPromiseIsPending; \ No newline at end of file diff --git a/code/UI/CalendarViewSwitch/oh_modules/.ohpm/@ohos+hypium@1.0.19/oh_modules/@ohos/hypium/src/main/module/assert/assertPromiseIsRejected.js b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/@ohos+hypium@1.0.19/oh_modules/@ohos/hypium/src/main/module/assert/assertPromiseIsRejected.js new file mode 100644 index 0000000000000000000000000000000000000000..380075a369a84d6856e7f2db366f704e04302a8d --- /dev/null +++ b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/@ohos+hypium@1.0.19/oh_modules/@ohos/hypium/src/main/module/assert/assertPromiseIsRejected.js @@ -0,0 +1,38 @@ +/* + * 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 isPromiseLike from './isPromiseLike'; + +function assertPromiseIsRejected(actualPromise) { + if (!isPromiseLike(actualPromise)) { + return Promise.reject().then(function () { + }, function () { + return {pass: false, message: 'Expected not be called on a promise.'}; + }); + } + return actualPromise.then( + function (got) { + return { + pass: false, + message: 'expect isRejected, but actualValue is resolve' + }; + }, + function () { + return {pass: true, message: 'actualValue is isRejected'}; + } + ); +} + +export default assertPromiseIsRejected; \ No newline at end of file diff --git a/code/UI/CalendarViewSwitch/oh_modules/.ohpm/@ohos+hypium@1.0.19/oh_modules/@ohos/hypium/src/main/module/assert/assertPromiseIsRejectedWith.js b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/@ohos+hypium@1.0.19/oh_modules/@ohos/hypium/src/main/module/assert/assertPromiseIsRejectedWith.js new file mode 100644 index 0000000000000000000000000000000000000000..8179589d5580f9c305d2200b4b197d64ac9d53ae --- /dev/null +++ b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/@ohos+hypium@1.0.19/oh_modules/@ohos/hypium/src/main/module/assert/assertPromiseIsRejectedWith.js @@ -0,0 +1,55 @@ +/* + * 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 isPromiseLike from './isPromiseLike'; + +function assertPromiseIsRejectedWith(actualPromise, expectedValue) { + + if (!isPromiseLike(actualPromise)) { + return Promise.reject().then(function () { + }, function () { + return {pass: false, message: 'Expected not be called on a promise.'}; + }); + } + + function tips(passed) { + return ('Expected a promise ' + (passed ? 'not ' : '') + + 'to be rejected with ' + JSON.stringify(expectedValue[0])); + } + + return actualPromise.then( + function (got) { + return { + pass: false, + message: tips(false) + ' but actualValue is resolve' + }; + }, + function (actualValue) { + if (JSON.stringify(actualValue) == JSON.stringify(expectedValue[0])) { + return { + pass: true, + message: 'actualValue was rejected with ' + JSON.stringify(actualValue) + '.' + }; + } else { + return { + pass: false, + message: tips(false) + ' but it was rejected with ' + JSON.stringify(actualValue) + '.' + }; + } + } + ); +} + +export default assertPromiseIsRejectedWith; \ No newline at end of file diff --git a/code/UI/CalendarViewSwitch/oh_modules/.ohpm/@ohos+hypium@1.0.19/oh_modules/@ohos/hypium/src/main/module/assert/assertPromiseIsRejectedWithError.js b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/@ohos+hypium@1.0.19/oh_modules/@ohos/hypium/src/main/module/assert/assertPromiseIsRejectedWithError.js new file mode 100644 index 0000000000000000000000000000000000000000..291af8e5032b7bcd9bcb3e996a39a4fa8ba23157 --- /dev/null +++ b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/@ohos+hypium@1.0.19/oh_modules/@ohos/hypium/src/main/module/assert/assertPromiseIsRejectedWithError.js @@ -0,0 +1,76 @@ +/* + * 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 isPromiseLike from './isPromiseLike'; + +function assertPromiseIsRejectedWithError(actualPromise, expectedValue) { + if (!isPromiseLike(actualPromise)) { + return Promise.reject().then(function () { + }, function () { + return {pass: false, message: 'Expected not be called on a promise.'}; + }); + } + return actualPromise.then( + function (got) { + return { + pass: false, + message: 'Expected a promise to be rejected but actualValue is resolve' + }; + }, + function (actualValue) { + return matchError(actualValue, expectedValue); + } + ); + +} + +function matchError(actualValue, expectedValue) { + if (expectedValue.length == 1 && typeof expectedValue[0] === 'function') { + if (expectedValue[0].name === actualValue.__proto__.name) { + return {pass: true, message: 'actual error type is ' + actualValue.name + '.'}; + } + return {pass: false, message: `except error type is ${expectedValue[0].name},but actual is ${actualValue.name}.`}; + } + + if (expectedValue.length == 1 && typeof expectedValue[0] === 'string') { + if (expectedValue[0] === actualValue.message) { + return {pass: true, message: `actual error message is ${actualValue.message}.`}; + } + return {pass: false, message: `except error message ${expectedValue[0]},but actual is ${actualValue.message}.`}; + } + + if (expectedValue.length == 1) { + return {pass: false, message: 'When only one parameter, it should be error type or error message.'}; + } + + if (expectedValue.length == 2 && typeof expectedValue[0] === 'function' && expectedValue[0].name === actualValue.name) { + if (typeof expectedValue[1] === 'string' && actualValue.message === expectedValue[1]) { + return {pass: true, message: 'actual error message is ' + actualValue.message + '.'}; + } + return {pass: false, message: `except error message is ${expectedValue[1]},but actual is ${actualValue.message}.`}; + } + + if (expectedValue.length == 2 && typeof expectedValue[0] === 'function' && expectedValue[0].name !== actualValue.name) { + if (typeof expectedValue[1] === 'string' && actualValue.message === expectedValue[1]) { + return {pass: false, message: `except error type is ${expectedValue[0].name},but actual is ${actualValue.name}.`}; + } + return {pass: false, message: 'except error type and message are incorrect.'}; + } + if (expectedValue.length > 2) { + return {pass: false, message: 'Up to two parameters are supported.'}; + } +} + +export default assertPromiseIsRejectedWithError; \ No newline at end of file diff --git a/code/UI/CalendarViewSwitch/oh_modules/.ohpm/@ohos+hypium@1.0.19/oh_modules/@ohos/hypium/src/main/module/assert/assertPromiseIsResolved.js b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/@ohos+hypium@1.0.19/oh_modules/@ohos/hypium/src/main/module/assert/assertPromiseIsResolved.js new file mode 100644 index 0000000000000000000000000000000000000000..86f559c32873f27b95d635452d760029de0ed657 --- /dev/null +++ b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/@ohos+hypium@1.0.19/oh_modules/@ohos/hypium/src/main/module/assert/assertPromiseIsResolved.js @@ -0,0 +1,40 @@ +/* + * 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 isPromiseLike from './isPromiseLike'; + +function assertPromiseIsResolved(actualPromise) { + if (!isPromiseLike(actualPromise)) { + return Promise.reject().then(function () { + }, function () { + return {pass: false, message: 'Expected not be called on a promise.'}; + }); + } + + return actualPromise.then( + function (got) { + return {pass: true, message: 'actualValue is isResolved'}; + }, + function (rej) { + return { + pass: false, + message: 'Expected a promise to be resolved but it was ' + + 'rejected with ' + JSON.stringify(rej) + '.' + }; + } + ); +} + +export default assertPromiseIsResolved; \ No newline at end of file diff --git a/code/UI/CalendarViewSwitch/oh_modules/.ohpm/@ohos+hypium@1.0.19/oh_modules/@ohos/hypium/src/main/module/assert/assertPromiseIsResolvedWith.js b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/@ohos+hypium@1.0.19/oh_modules/@ohos/hypium/src/main/module/assert/assertPromiseIsResolvedWith.js new file mode 100644 index 0000000000000000000000000000000000000000..c6f0ef68fde5b04a589a9fa3c6e2ab2b39acf4d3 --- /dev/null +++ b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/@ohos+hypium@1.0.19/oh_modules/@ohos/hypium/src/main/module/assert/assertPromiseIsResolvedWith.js @@ -0,0 +1,55 @@ +/* + * 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 isPromiseLike from './isPromiseLike'; + +function assertPromiseIsResolvedWith(actualPromise, expectedValue) { + if (!isPromiseLike(actualPromise)) { + return Promise.reject().then(function () { + }, function () { + return {pass: false, message: 'Expected not be called on a promise.'}; + }); + } + + function tips(passed) { + return ( + 'Expected a promise ' + (passed ? 'not ' : '') + + 'to be resolved with ' + JSON.stringify(expectedValue[0])); + } + + return actualPromise.then( + function (got) { + if (JSON.stringify(got) == JSON.stringify(expectedValue[0])) { + return { + pass: true, + message: 'actualValue was resolved with ' + JSON.stringify(got) + '.' + }; + } + return { + pass: false, + message: tips(false) + ' but it was resolved with ' + + JSON.stringify(got) + '.' + }; + }, + function (rej) { + return { + pass: false, + message: tips(false) + ' but it was rejected with ' + JSON.stringify(rej) + '.' + }; + } + ); +} + +export default assertPromiseIsResolvedWith; \ No newline at end of file diff --git a/code/UI/CalendarViewSwitch/oh_modules/.ohpm/@ohos+hypium@1.0.19/oh_modules/@ohos/hypium/src/main/module/assert/assertThrowError.js b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/@ohos+hypium@1.0.19/oh_modules/@ohos/hypium/src/main/module/assert/assertThrowError.js new file mode 100644 index 0000000000000000000000000000000000000000..c4544a7f825bcecd1a07d5e98dd9a7b99d237278 --- /dev/null +++ b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/@ohos+hypium@1.0.19/oh_modules/@ohos/hypium/src/main/module/assert/assertThrowError.js @@ -0,0 +1,48 @@ +/* + * 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. + */ + +function assertThrowError(actualValue, expected) { + let result = false; + let message = ''; + let err; + if (typeof actualValue !== 'function') { + throw new Error('actualValue is not a function'); + } + try { + actualValue(); + return { + pass: result, + message: ' An error is not thrown while it is expected!' + }; + } catch (e) { + err = e; + } + if (err instanceof Error) { + let type = typeof expected[0]; + if (type === 'function') { + result = err.constructor.name === expected[0].name; + message = 'expected throw failed , ' + err.constructor.name + ' is not ' + expected[0].name; + }else if(type === 'string'){ + result = err.message.includes(expected[0]); + message = 'expected throw failed , ' + err.message + ' is not ' + expected[0]; + } + } + return { + pass: result, + message: message + }; +} + +export default assertThrowError; diff --git a/code/UI/CalendarViewSwitch/oh_modules/.ohpm/@ohos+hypium@1.0.19/oh_modules/@ohos/hypium/src/main/module/assert/assertUndefined.js b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/@ohos+hypium@1.0.19/oh_modules/@ohos/hypium/src/main/module/assert/assertUndefined.js new file mode 100644 index 0000000000000000000000000000000000000000..61f092d715dd1630297518b59ff13ef0940991e1 --- /dev/null +++ b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/@ohos+hypium@1.0.19/oh_modules/@ohos/hypium/src/main/module/assert/assertUndefined.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 assertUndefined(actualValue) { + return { + pass: undefined === (actualValue), + message: 'expect Undefined, actualValue is ' + (actualValue) + }; +} + +export default assertUndefined; diff --git a/code/UI/CalendarViewSwitch/oh_modules/.ohpm/@ohos+hypium@1.0.19/oh_modules/@ohos/hypium/src/main/module/assert/deepEquals/DeepTypeUtils.js b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/@ohos+hypium@1.0.19/oh_modules/@ohos/hypium/src/main/module/assert/deepEquals/DeepTypeUtils.js new file mode 100644 index 0000000000000000000000000000000000000000..916824d9cb77a75d1fb35bc3500d7598bfc73e80 --- /dev/null +++ b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/@ohos+hypium@1.0.19/oh_modules/@ohos/hypium/src/main/module/assert/deepEquals/DeepTypeUtils.js @@ -0,0 +1,139 @@ +/* + * Copyright (c) 2022-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 DeepTypeUtils { + static getType(value) { + return Object.prototype.toString.apply(value); + } + static isA(typeName, value) { + return this.getType(value) === '[object ' + typeName + ']'; + } + static isAsymmetricEqualityTester(obj) { + return obj ? this.isA('Function', obj.asymmetricMatch) : false; + } + + /** + * 是否是function + * @param value + */ + static isFunction(value) { + return this.isA('Function', value); + } + + /** + * 是否是undefined + * @param obj + */ + static isUndefined(obj) { + return obj === void 0; + } + + /** + * 是否是Node + * @param obj + */ + static isDomNode(obj) { + return obj !== null && + typeof obj === 'object' && + typeof obj.nodeType === 'number' && + typeof obj.nodeName === 'string'; + } + + /** + * 是否是promise对象 + * @param obj + */ + static isPromise(obj) { + return !!obj && obj.constructor === Promise; + }; + /** + * 是否是map对象 + * @param obj + */ + static isMap(obj) { + return ( + obj !== null && + typeof obj !== 'undefined' && + obj.constructor === Map + ); + } + + /** + * 是否是set对象 + * @param obj 对象 + */ + static isSet(obj) { + return ( + obj !== null && + typeof obj !== 'undefined' && + obj.constructor === Set + ); + } + + /** + * 对象是否有key属性 + * @param obj 对象 + * @param key 对象属性名称 + */ + static has(obj, key) { + return Object.prototype.hasOwnProperty.call(obj, key); + } + + /** + * 获取对象的自有属性 + * @param obj 对象 + * @param isArray 是否是数组,[object Array] + */ + static keys(obj, isArray) { + const extraKeys = []; + // 获取对象所有属性 + const allKeys = this.getAllKeys(obj); + if (!isArray) { + return allKeys; + } + if (allKeys.length === 0) { + return allKeys; + } + for (const k of allKeys) { + if (typeof k === 'symbol' || !/^[0-9]+$/.test(k)) { + extraKeys.push(k); + } + } + return extraKeys; + } + + /** + * 获取obj对象的所有属性 + * @param obj obj对象 + */ + static getAllKeys(obj) { + const keys = []; + for (let key in obj) { + if (this.has(obj, key)) { + keys.push(key); + } + } + const symbols = Object.getOwnPropertySymbols(obj); + for (const sym of symbols) { + // obj.propertyIsEnumerable(sym) + if (Object.prototype.propertyIsEnumerable.call(obj, sym)) { + keys.push(sym); + } + } + return keys; + } + +} +export default DeepTypeUtils; \ No newline at end of file diff --git a/code/UI/CalendarViewSwitch/oh_modules/.ohpm/@ohos+hypium@1.0.19/oh_modules/@ohos/hypium/src/main/module/assert/deepEquals/assertDeepEquals.js b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/@ohos+hypium@1.0.19/oh_modules/@ohos/hypium/src/main/module/assert/deepEquals/assertDeepEquals.js new file mode 100644 index 0000000000000000000000000000000000000000..60de33f7e1afdcfaf205c8c56484ef33dfda8160 --- /dev/null +++ b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/@ohos+hypium@1.0.19/oh_modules/@ohos/hypium/src/main/module/assert/deepEquals/assertDeepEquals.js @@ -0,0 +1,341 @@ +/* + * Copyright (c) 2022-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 DeepTypeUtils from './DeepTypeUtils'; +function assertDeepEquals(actualValue, expected) { + let result = eq(actualValue, expected[0]) + let msg = logMsg(actualValue, expected[0]); + return { + pass: result, + message: msg + }; +} + +/** + * 获取失败显示日志 + * @param actualValue 实际对象 + * @param expected 期待比较对象 + */ +function logMsg(actualValue, expected) { + // 获取a的对象名称 + const aClassName = Object.prototype.toString.call(actualValue); + const bClassName = Object.prototype.toString.call(expected); + let actualMsg; + let expectMsg; + if (aClassName == '[object Function]') { + actualMsg = 'actualValue Function'; + } else if (aClassName == '[object Promise]') { + actualMsg = 'actualValue Promise'; + } else if (aClassName == '[object Set]' || aClassName == '[object Map]') { + actualMsg = JSON.stringify(Array.from(actualValue)); + } else if (aClassName == '[object RegExp]') { + actualMsg = JSON.stringify(actualValue.source.replace('\\','')); + } else if (aClassName == '[object BigInt]') { + actualMsg = actualValue; + } else if (aClassName == '[object Error]') { + actualMsg = actualValue.message; + } else if (aClassName == '[object ArrayBuffer]') { + actualMsg = actualValue.byteLength; + } + else { + actualMsg = JSON.stringify(actualValue); + } + if (bClassName == '[object Function]') { + expectMsg = 'expected Function'; + } else if (bClassName == '[object Promise]') { + expectMsg = 'expected Promise'; + } else if (bClassName == '[object Set]' || bClassName == '[object Map]') { + expectMsg = JSON.stringify(Array.from(expected)); + } else if (bClassName == '[object RegExp]') { + expectMsg = JSON.stringify(expected.source.replace('\\','')); + } else if (bClassName == '[object BigInt]') { + expectMsg = expected; + } else if (bClassName == '[object Error]') { + expectMsg = expected.message; + } else if (bClassName == '[object ArrayBuffer]') { + expectMsg = expected.byteLength; + } + else { + expectMsg = JSON.stringify(expected); + } + return actualMsg + ' is not deep equal ' + expectMsg; +} + +function eq(a, b) { + let result = true; + + if (a === b) { + result = a !== 0 || 1 / a === 1 / b; + return result; + } + + if (a === null || b === null) { + result = a === b; + return result; + } + // 获取a的对象名称 + const aClassName = Object.prototype.toString.call(a); + const bClassName = Object.prototype.toString.call(b); + // 不同类型不同对象 + if (aClassName !== bClassName) { + return false; + } + if (aClassName === '[object String]' || aClassName === '[object Number]' || aClassName === '[object Date]' || + aClassName === '[object Boolean]' || aClassName === '[object ArrayBuffer]' || + aClassName === '[object RegExp]' || aClassName === '[object Error]') { + result = isEqualSampleObj(a, b); + return result; + } + + if (typeof a !== 'object' || typeof b !== 'object') { + return false; + } + + if (DeepTypeUtils.isDomNode(a) || DeepTypeUtils.isPromise(a) || DeepTypeUtils.isFunction(a)) { + result = isEqualNodeOrPromiseOrFunction(a, b); + return result; + } + + if (aClassName === '[object Array]' || aClassName === '[object Map]' || aClassName === '[object Set]') { + result = isEqualCollection(a, b); + return result; + } + + result = isEqualObj(a, b); + return result; +} + +function isEqualNodeOrPromiseOrFunction(a, b) { + let equalNodeOrPromiseOrFunction = true; + if (DeepTypeUtils.isDomNode(a) && DeepTypeUtils.isDomNode(b)) { + const aIsDomNode = DeepTypeUtils.isDomNode(a); + const bIsDomNode = DeepTypeUtils.isDomNode(b); + if (aIsDomNode && bIsDomNode) { + // At first try to use DOM3 method isEqualNode + equalNodeOrPromiseOrFunction = a.isEqualNode(b); + return equalNodeOrPromiseOrFunction; + } + if (aIsDomNode || bIsDomNode) { + equalNodeOrPromiseOrFunction = false; + return false; + } + } + + if (DeepTypeUtils.isPromise(a) && DeepTypeUtils.isPromise(b)) { + const aIsPromise = DeepTypeUtils.isPromise(a); + const bIsPromise = DeepTypeUtils.isPromise(b); + // 俩个Promise对象 + if (aIsPromise && bIsPromise) { + equalNodeOrPromiseOrFunction = a === b; + return a === b; + } + } + if (DeepTypeUtils.isFunction(a) && DeepTypeUtils.isFunction(b)) { + // 俩个函数对象 + const aCtor = a.constructor, + bCtor = b.constructor; + if ( + aCtor !== bCtor && + DeepTypeUtils.isFunction(aCtor) && + DeepTypeUtils.isFunction(bCtor) && + a instanceof aCtor && + b instanceof bCtor && + !(aCtor instanceof aCtor && bCtor instanceof bCtor) + ) { + equalNodeOrPromiseOrFunction = false; + return false; + } + } + return equalNodeOrPromiseOrFunction; +} + +function isEqualCollection(a, b) { + let equalCollection = true; + // 获取a的对象名称 + const aClassName = Object.prototype.toString.call(a); + const bClassName = Object.prototype.toString.call(b); + // 都是数组 + if (aClassName === '[object Array]') { + equalCollection = isEqualArray(a, b); + return equalCollection; + } + + // 都是Map + if (DeepTypeUtils.isMap(a) && DeepTypeUtils.isMap(b)) { + equalCollection = isEqualMap(a, b); + return equalCollection; + } + + // 都是Set + if (DeepTypeUtils.isSet(a) && DeepTypeUtils.isSet(b)) { + equalCollection = isEqualSet(a, b); + return equalCollection; + } + + return true; +} + +function isEqualSampleObj(a, b) { + let equalSampleObj = true; + const aClassName = Object.prototype.toString.call(a); + const bClassName = Object.prototype.toString.call(b); + // 俩个string对象 + if (aClassName === '[object String]') { + equalSampleObj = a === String(b); + return equalSampleObj; + } + // 俩个Number对象 + if (aClassName === '[object Number]') { + equalSampleObj = a !== +a ? b !== +b : a === 0 && b === 0 ? 1 / a === 1 / b : a === +b; + return equalSampleObj; + } + + // 俩个Date对象/ boolean对象 + if (aClassName === '[object Date]' || aClassName === '[object Boolean]') { + equalSampleObj = +a === +b; + return equalSampleObj; + } + + // 俩个ArrayBuffer + if (aClassName === '[object ArrayBuffer]') { + equalSampleObj = eq(new Uint8Array(a), new Uint8Array(b)); + return equalSampleObj; + } + + // 正则表达式 + if (aClassName === '[object RegExp]') { + return ( + a.source === b.source && + a.global === b.global && + a.multiline === b.multiline && + a.ignoreCase === b.ignoreCase + ); + } + + if (a instanceof Error && b instanceof Error) { + equalSampleObj = a.message === b.message; + return equalSampleObj; + } + + return equalSampleObj; +} + +function isEqualObj(a, b) { + let equalObj = true; + const aClassName = Object.prototype.toString.call(a); + const bClassName = Object.prototype.toString.call(b); + const aKeys = DeepTypeUtils.keys(a, aClassName === '[object Array]'); + let size = aKeys.length; + + // 俩个对象属性长度不一致, 俩对象不相同 + if (DeepTypeUtils.keys(b, bClassName === '[object Array]').length !== size) { + return false; + } + + // 俩对象属性数量相同, 递归比较每个属性值得值 + for (const key of aKeys) { + // b 没有 key 属性 + if (!DeepTypeUtils.has(b, key)) { + equalObj = false; + continue; + } + if (!eq(a[key], b[key])) { + equalObj = false; + } + } + return equalObj; +} + +function isEqualArray(a, b) { + let equalArray = true; + const aLength = a.length; + const bLength = b.length; + if (aLength !== bLength) { + // 数组长度不同,不是同一个对象 + return false; + } + for (let i = 0; i < aLength || i < bLength; i++) { + // 递归每一个元素是否相同 + equalArray = eq(i < aLength ? a[i] : void 0, i < bLength ? b[i] : void 0) && equalArray; + } + return equalArray; +} + +function isEqualMap(a, b) { + let equalMap = true; + if (a.size !== b.size) { + return false; + } + const keysA = []; + const keysB = []; + a.forEach(function(valueA, keyA) { + keysA.push(keyA); + }); + b.forEach(function(valueB, keyB) { + keysB.push(keyB); + }); + const mapKeys = [keysA, keysB]; + const cmpKeys = [keysB, keysA]; + for (let i = 0; equalMap && i < mapKeys.length; i++) { + const mapIter = mapKeys[i]; + const cmpIter = cmpKeys[i]; + + for (let j = 0; equalMap && j < mapIter.length; j++) { + const mapKey = mapIter[j]; + const cmpKey = cmpIter[j]; + const mapValueA = a.get(mapKey); + let mapValueB; + if (eq(mapKey, cmpKey)) { + mapValueB = b.get(cmpKey); + } else { + mapValueB = b.get(mapKey); + } + equalMap = eq(mapValueA, mapValueB); + } + } + return equalMap; +} + +function isEqualSet(a, b) { + let equalSet = true; + if (a.size !== b.size) { + return false; + } + const valuesA = []; + a.forEach(function(valueA) { + valuesA.push(valueA); + }); + const valuesB = []; + b.forEach(function(valueB) { + valuesB.push(valueB); + }); + const setPairs = [[valuesA, valuesB], [valuesB, valuesA]]; + for (let i = 0; equalSet && i < setPairs.length; i++) { + const baseValues = setPairs[i][0]; + const otherValues = setPairs[i][1]; + for (const baseValue of baseValues) { + let found = false; + for (let j = 0; !found && j < otherValues.length; j++) { + const otherValue = otherValues[j]; + // 深度比较对象 + found = eq(baseValue, otherValue); + } + equalSet = equalSet && found; + } + } + return equalSet; +} + +export default assertDeepEquals; diff --git a/code/UI/CalendarViewSwitch/oh_modules/.ohpm/@ohos+hypium@1.0.19/oh_modules/@ohos/hypium/src/main/module/assert/isPromiseLike.js b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/@ohos+hypium@1.0.19/oh_modules/@ohos/hypium/src/main/module/assert/isPromiseLike.js new file mode 100644 index 0000000000000000000000000000000000000000..015ab19a2a0c4872d7cb490b61f8e1dd6a8ac90b --- /dev/null +++ b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/@ohos+hypium@1.0.19/oh_modules/@ohos/hypium/src/main/module/assert/isPromiseLike.js @@ -0,0 +1,32 @@ +/* + * 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 isPromiseLike(obj) { + return !!obj && isFunction_(obj.then); +} + +function isFunction_(value) { + return isA_('Function', value); +} + +function isA_(typeName, value) { + return getType_(value) === '[object ' + typeName + ']'; +} + +function getType_(value) { + return Object.prototype.toString.apply(value); +} + +export default isPromiseLike; diff --git a/code/UI/CalendarViewSwitch/oh_modules/.ohpm/@ohos+hypium@1.0.19/oh_modules/@ohos/hypium/src/main/module/config/DataDriver.js b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/@ohos+hypium@1.0.19/oh_modules/@ohos/hypium/src/main/module/config/DataDriver.js new file mode 100644 index 0000000000000000000000000000000000000000..639dffc9cdb912f1f33a6ccb61868c9ed7c695bf --- /dev/null +++ b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/@ohos+hypium@1.0.19/oh_modules/@ohos/hypium/src/main/module/config/DataDriver.js @@ -0,0 +1,116 @@ +/* + * 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. + */ + +const SUITES_KEY = 'suites'; +const SPECS_KEY = 'items'; +const DESCRIBE_KEY = 'describe'; +const IT_KEY = 'it'; +const PARAMS_KEY = 'params'; +const STRESS_KEY = 'stress'; + +class ObjectUtils { + static get(object, name, defaultValue) { + let result = defaultValue; + for (const key in object) { + if (key === name) { + return object[key]; + } + } + return result; + } + + static has(object, key) { + return Object.prototype.hasOwnProperty.call(object, key); + } +} + +class DataDriver { + constructor(attr) { + this.id = 'dataDriver'; + this.data = attr.data || {}; + } + + init(coreContext) { + this.coreContext = coreContext; + this.suiteService = this.coreContext.getDefaultService('suite'); + this.specService = this.coreContext.getDefaultService('spec'); + } + + getSpecParams() { + let specParams = []; + let suiteDesc = this.suiteService.getCurrentRunningSuite().description; + let specDesc = this.specService.getCurrentRunningSpec().description; + let suites = ObjectUtils.get(this.data, SUITES_KEY, []); + for (const suiteItem of suites) { + let describeValue = ObjectUtils.get(suiteItem, DESCRIBE_KEY, ''); + if (ObjectUtils.has(suiteItem, DESCRIBE_KEY) && (typeof describeValue === 'object') && describeValue.constructor === Array && describeValue.includes(suiteDesc)) { + let specs = ObjectUtils.get(suiteItem, SPECS_KEY, []); + for (const specItem of specs) { + if (ObjectUtils.has(specItem, IT_KEY) && ObjectUtils.get(specItem, IT_KEY) === specDesc) { + return ObjectUtils.get(specItem, PARAMS_KEY, specParams); + } + } + } + } + return specParams; + } + + getSuiteParams() { + let suiteParams = {}; + let suiteDesc = this.suiteService.getCurrentRunningSuite().description; + let suites = ObjectUtils.get(this.data, SUITES_KEY, []); + for (const suiteItem of suites) { + let describeValue = ObjectUtils.get(suiteItem, DESCRIBE_KEY, []); + if (ObjectUtils.has(suiteItem, DESCRIBE_KEY) && (typeof describeValue === 'object') && describeValue.constructor === Array && describeValue.includes(suiteDesc)) { + suiteParams = Object.assign({}, suiteParams, ObjectUtils.get(suiteItem, PARAMS_KEY, suiteParams)); + } + } + return suiteParams; + } + + getSpecStress(specDesc) { + let stress = 1; + let suiteDesc = this.suiteService.getCurrentRunningSuite().description; + let suites = ObjectUtils.get(this.data, SUITES_KEY, []); + for (const suiteItem of suites) { + let describeValue = ObjectUtils.get(suiteItem, DESCRIBE_KEY, ''); + if (ObjectUtils.has(suiteItem, DESCRIBE_KEY) && (typeof describeValue === 'object') && describeValue.constructor === Array && describeValue.includes(suiteDesc)) { + let specs = ObjectUtils.get(suiteItem, SPECS_KEY, []); + for (const specItem of specs) { + if (ObjectUtils.has(specItem, IT_KEY) && ObjectUtils.get(specItem, IT_KEY) === specDesc) { + let tempStress = ObjectUtils.get(specItem, STRESS_KEY, stress); + return (Number.isInteger(tempStress) && tempStress >= 1) ? tempStress : stress; + } + } + } + } + return stress; + } + + getSuiteStress(suiteDesc) { + let stress = 1; + let suites = ObjectUtils.get(this.data, SUITES_KEY, []); + for (const suiteItem of suites) { + let describeValue = ObjectUtils.get(suiteItem, DESCRIBE_KEY, []); + if (ObjectUtils.has(suiteItem, DESCRIBE_KEY) && (typeof describeValue === 'object') && describeValue.constructor === Array && describeValue.includes(suiteDesc)) { + let tempStress = ObjectUtils.get(suiteItem, STRESS_KEY, stress); + return (Number.isInteger(tempStress) && tempStress >= 1) ? tempStress : stress; + } + } + return stress; + } +} + +export default DataDriver; diff --git a/code/UI/CalendarViewSwitch/oh_modules/.ohpm/@ohos+hypium@1.0.19/oh_modules/@ohos/hypium/src/main/module/config/Filter.js b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/@ohos+hypium@1.0.19/oh_modules/@ohos/hypium/src/main/module/config/Filter.js new file mode 100644 index 0000000000000000000000000000000000000000..b07e6c681bfff618cc9f5ca92ec85d1d9880202d --- /dev/null +++ b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/@ohos+hypium@1.0.19/oh_modules/@ohos/hypium/src/main/module/config/Filter.js @@ -0,0 +1,143 @@ +/* + * 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 { LEVEL, SIZE, TESTTYPE } from '../../Constant'; + +class ClassFilter { + constructor(suiteName, itName, params) { + this.suiteName = suiteName; + this.itName = itName; + this.params = params; + } + + filterSuite() { + return !this.params.split(',').map(item => item.split('#')[0]).map(item => item == this.suiteName).reduce((pre, cur) => pre || cur, false); + } + + filterIt() { + let classArray = this.params.split(',') || []; + let suiteFilterResult = classArray.filter(item => !item.includes('#')).map(item => item == this.suiteName).reduce((pre, cur) => pre || cur, false); + let itFilterResult = classArray.filter(item => item.includes('#')).map(item => item == (this.suiteName + '#' + this.itName)).reduce((pre, cur) => pre || cur, false); + return !(suiteFilterResult || itFilterResult); + } +} + +class NotClassFilter { + constructor(suiteName, itName, params) { + this.suiteName = suiteName; + this.itName = itName; + this.params = params; + } + + filterSuite() { + return this.params.split(',').map(item => item == this.suiteName).reduce((pre, cur) => pre || cur, false); + } + + filterIt() { + return this.params.split(',').some(item => item == (this.suiteName + '#' + this.itName)); + } +} + +class SuiteAndItNameFilter { + constructor(suiteName, itName, params) { + this.suiteName = suiteName; + this.itName = itName; + this.params = params; + } + + filterSuite() { + return !this.params.split(',').map(item => item == this.suiteName).reduce((pre, cur) => pre || cur, false); + } + + filterIt() { + return !this.params.split(',').map(item => item == this.itName).reduce((pre, cur) => pre || cur, false); + } +} + + +class TestTypesFilter { + constructor(suiteName, itName, fi, params) { + this.suiteName = suiteName; + this.itName = itName; + this.params = params; + this.fi = fi; + } + + filterIt() { + return !((this.params === (this.fi & this.params)) || this.fi === 0); + } +} + +class NestFilter { + filterNestName(targetSuiteArray, targetSpecArray, suiteStack, desc) { + let targetSuiteName = ''; + for (let key in suiteStack) { + targetSuiteName = targetSuiteName + '.' + suiteStack[key].description; + } + targetSuiteName = targetSuiteName.substring(2); + const targetSpecName = targetSuiteName + '#' + desc; + let isFilter = true; + if (targetSpecArray.includes(targetSpecName)) { + return false; + } + for (let index in targetSuiteArray) { + if (targetSuiteName.startsWith(targetSuiteArray[index])) { + return false; + } + } + return isFilter; + } + + filterNotClass(notClass, suiteStack, desc) { + let isFilterNotClass = false; + if (notClass != null) { + let notClassArray = notClass.split(','); + let targetSuiteName = ''; + for (let key in suiteStack) { + targetSuiteName = targetSuiteName + '.' + suiteStack[key].description; + } + targetSuiteName = targetSuiteName.substring(2); + const targetSpecName = targetSuiteName + '#' + desc; + if (notClassArray.includes(targetSpecName) || notClassArray.some(key => targetSpecName.startsWith(key))) { + isFilterNotClass = true; + } + } + return isFilterNotClass; + } + + filterLevelOrSizeOrTestType(level, size, testType, filter) { + let result = false; + if (filter === 0 || filter === '0') { + return result; + } + if (level == null && size == null && testType == null) { + return result; + } + if (level != null) { + let levelFilter = LEVEL[`${level}`]; + result = result || filter === levelFilter; + } + if (size != null) { + let sizeFilter = SIZE[`${size}`]; + result = result || filter === sizeFilter; + } + if (testType != null) { + let testTypeFilter = TESTTYPE[`${testType}`]; + result = result || filter === testTypeFilter; + } + return !result; + } +} +export { ClassFilter, NotClassFilter, SuiteAndItNameFilter, TestTypesFilter, NestFilter }; diff --git a/code/UI/CalendarViewSwitch/oh_modules/.ohpm/@ohos+hypium@1.0.19/oh_modules/@ohos/hypium/src/main/module/config/configService.js b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/@ohos+hypium@1.0.19/oh_modules/@ohos/hypium/src/main/module/config/configService.js new file mode 100644 index 0000000000000000000000000000000000000000..17674d8cf7e2343bcb4a14ad47eb11cd03c15aac --- /dev/null +++ b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/@ohos+hypium@1.0.19/oh_modules/@ohos/hypium/src/main/module/config/configService.js @@ -0,0 +1,299 @@ +/* + * 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 { ClassFilter, NotClassFilter, SuiteAndItNameFilter, TestTypesFilter, NestFilter } from './Filter'; +import { TAG, TESTTYPE, LEVEL, SIZE, KEYSET } from '../../Constant'; +const STRESS_RULE = /^[1-9]\d*$/; + +class ConfigService { + constructor(attr) { + this.id = attr.id; + this.supportAsync = true; // 默认异步处理测试用例 + this.random = false; + this.filterValid = []; + this.filter = 0; + this.flag = false; + this.suite = null; + this.itName = null; + this.testType = null; + this.level = null; + this.size = null; + this.class = null; + this.notClass = null; + this.timeout = null; + // 遇错即停模式配置 + this.breakOnError = false; + // 压力测试配置 + this.stress = null; + this.skipMessage = false; + this.runSkipped = ''; + this.filterXdescribe = []; + } + + init(coreContext) { + this.coreContext = coreContext; + } + + isNormalInteger(str) { + const n = Math.floor(Number(str)); + return n !== Infinity && String(n) === String(str) && n >= 0; + } + + + getStress() { + if (this.stress === undefined || this.stress === '' || this.stress === null) { + return 1; + } + return !this.stress.match(STRESS_RULE) ? 1 : Number.parseInt(this.stress); + } + + basicParamValidCheck(params) { + let size = params.size; + if (size !== undefined && size !== '' && size !== null) { + let sizeArray = ['small', 'medium', 'large']; + if (sizeArray.indexOf(size) === -1) { + this.filterValid.push('size:' + size); + } + } + let level = params.level; + if (level !== undefined && level !== '' && level !== null) { + let levelArray = ['0', '1', '2', '3', '4']; + if (levelArray.indexOf(level) === -1) { + this.filterValid.push('level:' + level); + } + } + let testType = params.testType; + if (testType !== undefined && testType !== '' && testType !== null) { + let testTypeArray = ['function', 'performance', 'power', 'reliability', 'security', + 'global', 'compatibility', 'user', 'standard', 'safety', 'resilience']; + if (testTypeArray.indexOf(testType) === -1) { + this.filterValid.push('testType:' + testType); + } + } + } + + filterParamValidCheck(params) { + let timeout = params.timeout; + if (timeout !== undefined && timeout !== '' && timeout !== null) { + if (!this.isNormalInteger(timeout)) { + this.filterValid.push('timeout:' + timeout); + } + } + + let paramKeys = ['dryRun', 'random', 'breakOnError', 'coverage', 'skipMessage']; + for (const key of paramKeys) { + if (params[key] !== undefined && params[key] !== 'true' && params[key] !== 'false') { + this.filterValid.push(`${key}:${params[key]}`); + } + } + + // 压力测试参数验证,正整数 + if (params.stress !== undefined && params.stress !== '' && params.stress !== null) { + if (!params.stress.match(STRESS_RULE)) { + this.filterValid.push('stress:' + params.stress); + } + } + + let nameRule = /^[A-Za-z]{1}[\w#,.]*$/; + let paramClassKeys = ['class', 'notClass']; + for (const key of paramClassKeys) { + if (params[key] !== undefined && params[key] !== '' && params[key] !== null) { + let classArray = params[key].split(','); + classArray.forEach(item => !item.match(nameRule) ? this.filterValid.push(`${key}:${params[key]}`) : null); + } + } + } + + setConfig(params) { + this.basicParamValidCheck(params); + this.filterParamValidCheck(params); + try { + this.class = params.class; + this.notClass = params.notClass; + this.flag = params.flag || { flag: false }; + this.suite = params.suite; + this.itName = params.itName; + this.filter = params.filter; + this.testType = params.testType; + this.level = params.level; + this.size = params.size; + this.timeout = params.timeout; + this.dryRun = params.dryRun; + this.breakOnError = params.breakOnError; + this.random = params.random === 'true' ? true : false; + this.stress = params.stress; + this.coverage = params.coverage; + this.skipMessage = params.skipMessage; + this.runSkipped = params.runSkipped; + this.filterParam = { + testType: TESTTYPE, + level: LEVEL, + size: SIZE + }; + this.parseParams(); + } catch (err) { + console.info(`${TAG}setConfig error: ${err.message}`); + } + } + + parseParams() { + if (this.filter != null) { + return; + } + let testTypeFilter = 0; + let sizeFilter = 0; + let levelFilter = 0; + if (this.testType != null) { + testTypeFilter = this.testType.split(',') + .map(item => this.filterParam.testType[item] || 0) + .reduce((pre, cur) => pre | cur, 0); + } + if (this.level != null) { + levelFilter = this.level.split(',') + .map(item => this.filterParam.level[item] || 0) + .reduce((pre, cur) => pre | cur, 0); + } + if (this.size != null) { + sizeFilter = this.size.split(',') + .map(item => this.filterParam.size[item] || 0) + .reduce((pre, cur) => pre | cur, 0); + } + this.filter = testTypeFilter | sizeFilter | levelFilter; + console.info(`${TAG}filter params:${this.filter}`); + } + + isCurrentSuite(description) { + if (this.suite !== undefined && this.suite !== '' && this.suite !== null) { + let suiteArray = this.suite.split(','); + return suiteArray.indexOf(description) !== -1; + } + return false; + } + + filterSuite(currentSuiteName) { + let filterArray = []; + if (this.suite !== undefined && this.suite !== '' && this.suite !== null) { + filterArray.push(new SuiteAndItNameFilter(currentSuiteName, '', this.suite)); + } + if (this.class !== undefined && this.class !== '' && this.class !== null) { + filterArray.push(new ClassFilter(currentSuiteName, '', this.class)); + } + if (this.notClass !== undefined && this.notClass !== '' && this.notClass !== null) { + filterArray.push(new NotClassFilter(currentSuiteName, '', this.notClass)); + } + + let result = filterArray.map(item => item.filterSuite()).reduce((pre, cur) => pre || cur, false); + return result; + } + + filterDesc(currentSuiteName, desc, fi, coreContext) { + let filterArray = []; + if (this.itName !== undefined && this.itName !== '' && this.itName !== null) { + filterArray.push(new SuiteAndItNameFilter(currentSuiteName, desc, this.itName)); + } + if (this.class !== undefined && this.class !== '' && this.class !== null) { + filterArray.push(new ClassFilter(currentSuiteName, desc, this.class)); + } + if (this.notClass !== undefined && this.notClass !== '' && this.notClass !== null) { + filterArray.push(new NotClassFilter(currentSuiteName, desc, this.notClass)); + } + if (typeof (this.filter) !== 'undefined' && this.filter !== 0 && fi !== 0) { + filterArray.push(new TestTypesFilter('', '', fi, this.filter)); + } + let result = filterArray.map(item => item.filterIt()).reduce((pre, cur) => pre || cur, false); + return result; + } + + filterWithNest(desc, filter) { + let filterArray = []; + const nestFilter = new NestFilter(); + const targetSuiteArray = this.coreContext.getDefaultService('suite').targetSuiteArray; + const targetSpecArray = this.coreContext.getDefaultService('suite').targetSpecArray; + const suiteStack = this.coreContext.getDefaultService('suite').suitesStack; + let isFilter = nestFilter.filterNestName(targetSuiteArray, targetSpecArray, suiteStack, desc); + const isFullRun = this.coreContext.getDefaultService('suite').fullRun; + if (typeof (this.filter) !== 'undefined' && this.filter !== 0 && filter !== 0) { + filterArray.push(new TestTypesFilter('', '', filter, this.filter)); + return filterArray.map(item => item.filterIt()).reduce((pre, cur) => pre || cur, false); + } + if (isFilter && !isFullRun) { + return true; + } + return nestFilter.filterNotClass(this.notClass, suiteStack, desc); + + } + + isRandom() { + return this.random || false; + } + + isBreakOnError() { + return this.breakOnError !== 'true' ? false : true; + } + + setSupportAsync(value) { + this.supportAsync = value; + } + + isSupportAsync() { + return this.supportAsync; + } + + translateParams(parameters) { + const keySet = new Set(KEYSET); + let targetParams = {}; + for (const key in parameters) { + if (keySet.has(key)) { + var newKey = key.replace('-s ', ''); + targetParams[newKey] = parameters[key]; + } + } + return targetParams; + } + translateParamsToString(parameters) { + const keySet = new Set(KEYSET); + let targetParams = ''; + for (const key in parameters) { + if (keySet.has(key)) { + targetParams += ' ' + key + ' ' + parameters[key]; + } + } + return targetParams.trim(); + } + + execute() { + } + + checkIfSuiteInSkipRun(desc) { + return this.runSkipped.split(',').some(item => { + return item === desc || item.startsWith(desc + '.') || item.startsWith(desc + '#') || desc.startsWith(item + '.') || this.runSkipped === 'skipped'; + }); + } + + checkIfSpecInSkipRun(desc) { + return this.runSkipped.split(',').some(item => { + if (item.includes('#')) { + return item === desc; + } else { + return desc.startsWith(item + '.') || desc.startsWith(item + '#') || this.runSkipped === 'skipped'; + } + } + ); + } +} + +export { + ConfigService +}; diff --git a/code/UI/CalendarViewSwitch/oh_modules/.ohpm/@ohos+hypium@1.0.19/oh_modules/@ohos/hypium/src/main/module/coverage/coverageCollect.js b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/@ohos+hypium@1.0.19/oh_modules/@ohos/hypium/src/main/module/coverage/coverageCollect.js new file mode 100644 index 0000000000000000000000000000000000000000..334a33db9561dd2070c4081457632decf2589b83 --- /dev/null +++ b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/@ohos+hypium@1.0.19/oh_modules/@ohos/hypium/src/main/module/coverage/coverageCollect.js @@ -0,0 +1,76 @@ +/* + * 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 SysTestKit from '../kit/SysTestKit'; +import fs from '@ohos.file.fs'; +import {TAG} from '../../Constant'; + +const jsCoverageFileName = 'js_coverage.json'; + +export async function collectCoverageData() { + if (globalThis.__coverage__ === undefined) { + console.info(`${TAG} globalThis not have coverage`); + return; + } + const strJson = JSON.stringify(globalThis.__coverage__); + let testMode = globalThis.__testMode__; + console.info(`${TAG} coverage data testMode: ${testMode}`); + let savePath = globalThis.__savePath__; + console.info(`${TAG} write coverage data to: ${savePath}`); + let readPath = globalThis.__readPath__; + console.info(`${TAG} read coverage data in: ${readPath}`); + + // run callback mode if local test or (save path and read path ) is not defined + if (!testMode || !isCoveragePathValid(savePath)) { + console.info(`${TAG} run coverage data in call back mode`); + const strLen = strJson.length; + const maxLen = 500; + const maxCount = Math.floor(strLen / maxLen); + const OHOS_REPORT_COVERAGE_DATA = 'OHOS_REPORT_COVERAGE_DATA:'; + for (let count = 0; count <= maxCount; count++) { + console.info(`${OHOS_REPORT_COVERAGE_DATA} ${strJson.substring(count * maxLen, (count + 1) * maxLen)}`); + await SysTestKit.print(`${OHOS_REPORT_COVERAGE_DATA} ${strJson.substring(count * maxLen, (count + 1) * maxLen)}`); + } + return; + } + console.info(`${TAG} run coverage data in save file mode`); + if (fs.accessSync(savePath)) { + fs.unlinkSync(savePath); + } + + let inputPathDir = savePath.substring(0, savePath.length - jsCoverageFileName.length); + if (!fs.accessSync(inputPathDir)) { + console.info(`${TAG} coverage data create dir: ${inputPathDir}`); + fs.mkdirSync(inputPathDir); + } + + let file = fs.openSync(savePath, fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE); + let writeLen = fs.writeSync(file.fd, strJson, {encoding:'utf-8'}); + console.info(`${TAG} write coverage data success: ${writeLen}`); + fs.closeSync(file); + const OHOS_REPORT_COVERAGE_PATH = 'OHOS_REPORT_COVERAGE_PATH:'; + await SysTestKit.print(`${OHOS_REPORT_COVERAGE_PATH} ${readPath}`); + console.info(`${OHOS_REPORT_COVERAGE_PATH} ${readPath}`); +} + +function isCoveragePathValid(inputPath) { + if (!inputPath) { + return false; + } + if (inputPath.indexOf(jsCoverageFileName) === -1) { + return false; + } + return true; +} \ No newline at end of file diff --git a/code/UI/CalendarViewSwitch/oh_modules/.ohpm/@ohos+hypium@1.0.19/oh_modules/@ohos/hypium/src/main/module/kit/SysTestKit.js b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/@ohos+hypium@1.0.19/oh_modules/@ohos/hypium/src/main/module/kit/SysTestKit.js new file mode 100644 index 0000000000000000000000000000000000000000..6e2f256514cff87450f910098b1130943a40e39c --- /dev/null +++ b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/@ohos+hypium@1.0.19/oh_modules/@ohos/hypium/src/main/module/kit/SysTestKit.js @@ -0,0 +1,102 @@ +/* + * 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 {TAG} from '../../Constant'; +import Core from '../../core.js'; + +export default class SysTestKit { + + static delegator = null; + static systemTime = null; + static workerPort = null; + + constructor() { + this.id = 'sysTestKit'; + this.index = 0; + } + + static getDescribeName() { + return Core.getInstance().getDefaultService('suite').getCurrentRunningSuite().description; + } + + static getItName() { + return Core.getInstance().getDefaultService('spec').getCurrentRunningSpec().description; + } + + static getItAttribute() { + return Core.getInstance().getDefaultService('spec').getCurrentRunningSpec().fi; + } + + static actionStart(tag) { + console.info(`${TAG}${JSON.stringify(tag)}`); + var message = '\n' + 'OHOS_REPORT_ACTIONSTART: ' + JSON.stringify(tag) + '\n'; + SysTestKit.print(message); + console.info(`${TAG}${JSON.stringify(tag)} actionStart print success`); + } + + static actionEnd(tag) { + console.info(`${TAG}${JSON.stringify(tag)}`); + var message = '\n' + 'OHOS_REPORT_ACTIONEND: ' + JSON.stringify(tag) + '\n'; + SysTestKit.print(message); + console.info(`${TAG}${JSON.stringify(tag)} actionEnd print success`); + } + + static async existKeyword(keyword, timeout) { + let reg = new RegExp(/^[a-zA-Z0-9]{1,}$/); + if (!reg.test(keyword)) { + throw new Error('keyword must contain more than one string, and only letters and numbers are supported.'); + } + timeout = timeout || 4; + + let searchResult = false; + let cmd = 'hilog -x | grep -i \'' + keyword + '\' | wc -l'; + await executePromise(cmd, timeout).then((data) => { + searchResult = data; + }); + return searchResult; + } + static async print(message) { + if ('printSync' in SysTestKit.delegator) { + console.info(`${TAG}printSync called ...`); + SysTestKit.delegator.printSync(message); + } else { + await SysTestKit.delegator.print(message); + } + } + + static async getRealTime() { + let currentTime = new Date().getTime(); + if (SysTestKit.systemTime !== null && SysTestKit.systemTime !== undefined) { + await SysTestKit.systemTime.getRealTime().then((time) => { + console.info(`${TAG}systemTime.getRealTime success data: ${JSON.stringify(time)}`); + currentTime = time; + }).catch((error) => { + console.error(`${TAG}failed to systemTime.getRealTime because ${JSON.stringify(error)}`); + }); + } + return currentTime; + } +} + +function executePromise(cmd, timeout) { + return new Promise((resolve, reject) => { + SysTestKit.delegator.executeShellCommand(cmd, timeout, + (error, data) => { + console.info(`${TAG}existKeyword CallBack: err : ${JSON.stringify(error)}`); + console.info(`${TAG}existKeyword CallBack: data : ${JSON.stringify(data)}`); + resolve(parseInt(data.stdResult) > 3 ? true : false); + }); + }); +} \ No newline at end of file diff --git a/code/UI/CalendarViewSwitch/oh_modules/.ohpm/@ohos+hypium@1.0.19/oh_modules/@ohos/hypium/src/main/module/mock/ArgumentMatchers.js b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/@ohos+hypium@1.0.19/oh_modules/@ohos/hypium/src/main/module/mock/ArgumentMatchers.js new file mode 100644 index 0000000000000000000000000000000000000000..1e69ac401049589986968a8575ca45a02a299327 --- /dev/null +++ b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/@ohos+hypium@1.0.19/oh_modules/@ohos/hypium/src/main/module/mock/ArgumentMatchers.js @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2022-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 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() { + let regex = arguments[0]; + if (ArgumentMatchers.isRegExp(regex)) { + return regex; + } + throw Error('not a regex'); + } + + static isRegExp(value) { + return Object.prototype.toString.call(value) === '[object RegExp]'; + } + + matcheReturnKey() { + let arg = arguments[0]; + let regex = arguments[1]; + let stubSetKey = arguments[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() { + let key = arguments[0]; + + 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; + } +} + +export default ArgumentMatchers; \ No newline at end of file diff --git a/code/UI/CalendarViewSwitch/oh_modules/.ohpm/@ohos+hypium@1.0.19/oh_modules/@ohos/hypium/src/main/module/mock/ExtendInterface.js b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/@ohos+hypium@1.0.19/oh_modules/@ohos/hypium/src/main/module/mock/ExtendInterface.js new file mode 100644 index 0000000000000000000000000000000000000000..c6a866a6df662ad10a7f6869dcbc2381fa47bcdc --- /dev/null +++ b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/@ohos+hypium@1.0.19/oh_modules/@ohos/hypium/src/main/module/mock/ExtendInterface.js @@ -0,0 +1,57 @@ +/* + * 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() { + this.mocker.clear(); + } +} + +export default ExtendInterface; \ No newline at end of file diff --git a/code/UI/CalendarViewSwitch/oh_modules/.ohpm/@ohos+hypium@1.0.19/oh_modules/@ohos/hypium/src/main/module/mock/MockKit.js b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/@ohos+hypium@1.0.19/oh_modules/@ohos/hypium/src/main/module/mock/MockKit.js new file mode 100644 index 0000000000000000000000000000000000000000..5895666bc89ed4270582b436c82045745d5249b4 --- /dev/null +++ b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/@ohos+hypium@1.0.19/oh_modules/@ohos/hypium/src/main/module/mock/MockKit.js @@ -0,0 +1,266 @@ +/* + * Copyright (c) 2022-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 ExtendInterface from './ExtendInterface'; +import VerificationMode from './VerificationMode'; +import ArgumentMatchers from './ArgumentMatchers'; + +class MockKit { + + 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 = {}; + this.recordCalls = {}; + this.currentSetKey = new Map(); + this.mockObj = null; + this.recordMockedMethod = new Map(); + } + + clearAll() { + this.reset(); + var props = Object.keys(this); + for (var i = 0; i < props.length; i++) { + delete this[props[i]]; + } + + var props = Object.getOwnPropertyNames(this); + for (var i = 0; i < props.length; i++) { + delete this[props[i]]; + } + for (var key in this) { + delete this[key]; + } + } + + 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 = null; + 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(newObj) { + if (newObj.__proto__ === null) { + return []; + } + let properties = Object.getOwnPropertyNames(newObj); + return [...properties, ...getProperty(newObj.__proto__)]; + } + return getProperty(obj); + } + + recordMethodCall(originalMethod, args) { + Function.prototype.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; + 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); + } +} + +export {MockKit, when}; \ No newline at end of file diff --git a/code/UI/CalendarViewSwitch/oh_modules/.ohpm/@ohos+hypium@1.0.19/oh_modules/@ohos/hypium/src/main/module/mock/VerificationMode.js b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/@ohos+hypium@1.0.19/oh_modules/@ohos/hypium/src/main/module/mock/VerificationMode.js new file mode 100644 index 0000000000000000000000000000000000000000..aaf2fdfae00135d3d2055320fc5ea403b44d0bf3 --- /dev/null +++ b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/@ohos+hypium@1.0.19/oh_modules/@ohos/hypium/src/main/module/mock/VerificationMode.js @@ -0,0 +1,49 @@ +/* + * 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 {expect} from '../../interface'; + +class VerificationMode { + constructor(times) { + this.doTimes = times; + } + + times(count) { + expect(count).assertEqual(this.doTimes); + } + + never() { + console.info(this.doTimes); + expect(0).assertEqual(this.doTimes); + } + + once() { + expect(1).assertEqual(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; \ No newline at end of file diff --git a/code/UI/CalendarViewSwitch/oh_modules/.ohpm/@ohos+hypium@1.0.19/oh_modules/@ohos/hypium/src/main/module/report/LogExpectError.js b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/@ohos+hypium@1.0.19/oh_modules/@ohos/hypium/src/main/module/report/LogExpectError.js new file mode 100644 index 0000000000000000000000000000000000000000..5a94cecb4625205797ae886c19ac592f189c2232 --- /dev/null +++ b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/@ohos+hypium@1.0.19/oh_modules/@ohos/hypium/src/main/module/report/LogExpectError.js @@ -0,0 +1,65 @@ +/* + * 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. + */ + +class LogExpectError { + static getErrorMsg(matcherName, actualValue, expect, originMsg) { + if (matcherName === 'assertNull') { + return 'expect not null, actualValue is ' + (actualValue); + } + if (matcherName === 'assertTrue') { + return 'expect not true, actualValue is ' + (actualValue); + } + if (matcherName === 'assertFalse') { + return 'expect not false, actualValue is ' + (actualValue); + } + if (matcherName === 'assertEqual') { + return 'expect not Equal, actualValue is ' + actualValue + ' equals ' + expect; + } + if (matcherName === 'assertContain') { + return 'expect not have, ' + actualValue + ' have ' + expect; + } + if (matcherName === 'assertInstanceOf') { + return 'expect not InstanceOf, ' + actualValue + ' is ' + + Object.prototype.toString.call(actualValue) + expect; + } + if (matcherName === 'assertLarger') { + return 'expect not Larger, ' + + (actualValue) + ' is larger than ' + expect; + } + if (matcherName === 'assertLargerOrEqual') { + return 'expect not LargerOrEqual, ' + (actualValue) + ' larger than ' + expect; + } + if (matcherName === 'assertLess') { + return 'expect not Less, ' + (actualValue) + ' less than ' + expect; + } + if (matcherName === 'assertLessOrEqual') { + return 'expect not LessOrEqual, ' + (actualValue) + ' is less than ' + expect; + } + if (matcherName === 'assertNaN') { + return 'expect not NaN, actualValue is ' + (actualValue); + } + if (matcherName === 'assertNegUnlimited') { + return 'expect not NegUnlimited, actualValue is ' + (actualValue); + } + if (matcherName === 'assertPosUnlimited') { + return 'expect not PosUnlimited, actualValue is ' + (actualValue); + } + if (matcherName === 'assertUndefined') { + return 'expect not Undefined, actualValue is ' + (actualValue); + } + return originMsg; + } +} +export default LogExpectError; \ No newline at end of file diff --git a/code/UI/CalendarViewSwitch/oh_modules/.ohpm/@ohos+hypium@1.0.19/oh_modules/@ohos/hypium/src/main/module/report/OhReport.js b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/@ohos+hypium@1.0.19/oh_modules/@ohos/hypium/src/main/module/report/OhReport.js new file mode 100644 index 0000000000000000000000000000000000000000..653e56be9e88e810f6ab1f3d58049cf08d2ac0b6 --- /dev/null +++ b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/@ohos+hypium@1.0.19/oh_modules/@ohos/hypium/src/main/module/report/OhReport.js @@ -0,0 +1,213 @@ +/* + * 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 SysTestKit from "../kit/SysTestKit"; +import { collectCoverageData } from '../coverage/coverageCollect'; +import { TAG, PrintTag } from '../../Constant'; + +class OhReport { + constructor(attr) { + this.delegator = attr.delegator; + this.abilityDelegatorArguments = attr.abilityDelegatorArguments; + this.id = 'report'; + this.index = 0; + this.duration = 0; + this.currentThreadName = 'mainThread'; + } + + init(coreContext) { + this.coreContext = coreContext; + this.suiteService = this.coreContext.getDefaultService('suite'); + this.specService = this.coreContext.getDefaultService('spec'); + if (SysTestKit.workerPort !== null) { + this.currentThreadName = SysTestKit.workerPort.name; + } + } + + taskStart() { + } + + async taskDone() { + let summary = this.suiteService.getSummary(); + if (this.abilityDelegatorArguments !== null) { + this.taskDoneTime = new Date().getTime(); + const configService = this.coreContext.getDefaultService('config'); + const suiteService = this.coreContext.getDefaultService('suite'); + const specService = this.coreContext.getDefaultService('spec'); + if (configService['coverage'] === 'true') { + await collectCoverageData(); + } + let message = '\n' + `${PrintTag.OHOS_REPORT_RESULT}: stream=Tests run: ` + summary.total + ', Failure: ' + summary.failure; + message += ', Error: ' + summary.error; + message += ', Pass: ' + summary.pass; + message += ', Ignore: ' + summary.ignore; + if (specService.skipSpecNum > 0) { + message += ', SkipSpec: ' + specService.skipSpecNum; + } + message += '\n' + `${PrintTag.OHOS_REPORT_CODE}: ` + (summary.failure > 0 ? -1 : 0) + '\n'; + let isHasError = summary.failure > 0 || summary.error > 0; + let config = this.coreContext.getDefaultService('config'); + if (config.isBreakOnError() && isHasError) { + // 未执行全部说明 + message += '\n' + `${PrintTag.OHOS_REPORT_RESULT}: breakOnError model, Stopping whole test suite if one specific test case failed or error` + '\n'; + } + message += `${PrintTag.OHOS_REPORT_STATUS}: taskconsuming=` + summary.duration + '\n'; + console.info(`${message}`); + await SysTestKit.print(message); + } + if (SysTestKit.workerPort === null || SysTestKit.workerPort === undefined) { + // 主线程执行完成 结束任务。 + console.info(`${TAG}report print success`); + this.delegator.finishTest('your test finished!!!', 0, () => { }); + } else { + // worker线程执行完成将数据发送到主线程中。 + let sendData = { + currentThreadName: this.currentThreadName, + summary: summary + }; + console.info(`${TAG}, send data to mainThread, ${this.currentThreadName}, ${JSON.stringify(sendData)}`); + SysTestKit.workerPort.postMessage(sendData); + } + } + + incorrectFormat() { + if (this.coreContext.getDefaultService('config').filterValid.length !== 0) { + var value = this.coreContext.getDefaultService('config').filterValid; + var message = 'this param ' + value.join(',') + ' is invalid' + '\n'; + this.delegator.finishTest(message, 0, () => { + }); + } + } + + incorrectTestSuiteFormat() { + if (this.coreContext.getDefaultService('config').filterXdescribe.length !== 0) { + let value = this.coreContext.getDefaultService('config').filterXdescribe; + let message = 'xdescribe ' + value.join(',') + ' should not contain it' + '\n'; + this.delegator.finishTest(message, 0, () => { + }); + } + } + async suiteStart() { + if (this.abilityDelegatorArguments !== null) { + let specArr = []; + this.suiteService.getAllChildSuiteNum(this.suiteService.getCurrentRunningSuite(), specArr); + let message = '\n' + `${PrintTag.OHOS_REPORT_SUM}: ` + specArr.length; + this.suiteService.setCurrentRunningSuiteDesc(this.suiteService.getRootSuite(), this.suiteService.getCurrentRunningSuite(), ''); + message += '\n' + `${PrintTag.OHOS_REPORT_STATUS}: class=` + this.suiteService.getCurrentRunningSuiteDesc() + '\n'; + if (this.suiteService.currentRunningSuite.isSkip) { + message += `${PrintTag.OHOS_REPORT_STATUS}: skipReason=` + this.suiteService.currentRunningSuite.skipReason + '\n'; + } + if (SysTestKit.workerPort !== null) { + message += `${PrintTag.OHOS_REPORT_STATUS}: currentWorkerName=` + this.currentThreadName; + } + console.info(`${message}`); + await SysTestKit.print(message); + console.info(`${TAG}${this.suiteService.getCurrentRunningSuite().description} suiteStart print success`); + } + } + + async suiteDone() { + if (this.abilityDelegatorArguments !== null) { + const currentRunningSuite = this.suiteService.getCurrentRunningSuite(); + this.suiteService.setCurrentRunningSuiteDesc(this.suiteService.getRootSuite(), this.suiteService.getCurrentRunningSuite(), ''); + let message = '\n' + `${PrintTag.OHOS_REPORT_STATUS}: class=` + this.suiteService.getCurrentRunningSuiteDesc(); + if (this.suiteService.currentRunningSuite.isSkip && this.suiteService.currentRunningSuite.skipReason !== '') { + message += '\n' + `${PrintTag.OHOS_REPORT_STATUS}: skipReason=` + this.suiteService.currentRunningSuite.skipReason; + } + message += '\n' + `${PrintTag.OHOS_REPORT_STATUS}: suiteconsuming=` + this.suiteService.getCurrentRunningSuite().duration; + if (currentRunningSuite.hookError) { + message += '\n' + `${PrintTag.OHOS_REPORT_STATUS}: ${currentRunningSuite.hookError.message}`; + } + message += '\n'; + if (SysTestKit.workerPort !== null) { + message += `${PrintTag.OHOS_REPORT_STATUS}: currentWorkerName=` + this.currentThreadName; + } + console.info(`${message}`); + await SysTestKit.print(message); + console.info(`${TAG}${this.suiteService.getCurrentRunningSuite().description} suiteDone print success`); + } + } + + async specStart() { + if (this.abilityDelegatorArguments !== null) { + let message = '\n' + `${PrintTag.OHOS_REPORT_STATUS}: class=` + this.suiteService.getCurrentRunningSuiteDesc(); + message += '\n' + `${PrintTag.OHOS_REPORT_STATUS}: current=` + (++this.index); + message += '\n' + `${PrintTag.OHOS_REPORT_STATUS}: id=JS`; + message += '\n' + `${PrintTag.OHOS_REPORT_STATUS}: numtests=` + this.specService.getTestTotal(); + message += '\n' + `${PrintTag.OHOS_REPORT_STATUS}: stream=`; + message += '\n' + `${PrintTag.OHOS_REPORT_STATUS}: test=` + this.specService.currentRunningSpec.description; + message += '\n' + `${PrintTag.OHOS_REPORT_STATUS_CODE}: 1` + '\n'; + if (this.specService.currentRunningSpec.isSkip) { + message += `${PrintTag.OHOS_REPORT_STATUS}: skipReason=` + this.specService.currentRunningSpec.skipReason + '\n'; + } + if (SysTestKit.workerPort !== null) { + message += `${PrintTag.OHOS_REPORT_STATUS}: currentWorkerName=` + this.currentThreadName; + } + console.info(`${message}`); + await SysTestKit.print(message); + console.info(`${TAG}${this.specService.currentRunningSpec.description} specStart start print success`); + } + } + + async specDone() { + if (this.abilityDelegatorArguments !== null) { + let message = '\n' + `${PrintTag.OHOS_REPORT_STATUS}: class=` + this.suiteService.getCurrentRunningSuiteDesc(); + message += '\n' + `${PrintTag.OHOS_REPORT_STATUS}: current=` + (this.index); + message += '\n' + `${PrintTag.OHOS_REPORT_STATUS}: id=JS`; + message += '\n' + `${PrintTag.OHOS_REPORT_STATUS}: numtests=` + this.specService.getTestTotal(); + let messageStack = ''; + let messageCode = ''; + if (this.specService.currentRunningSpec.error) { + messageStack = `${PrintTag.OHOS_REPORT_STATUS}: stack=` + this.specService.currentRunningSpec.error?.stack?.slice(0, -1); + messageCode += `${PrintTag.OHOS_REPORT_STATUS}: stream=`; + messageCode += this.specService.currentRunningSpec.expectMsg !== '' ? + `message: ${this.specService.currentRunningSpec.expectMsg}, Error in ${this.specService.currentRunningSpec.description}, ${this.specService.currentRunningSpec.error?.message}` : + `Error in ${this.specService.currentRunningSpec.description}, ${this.specService.currentRunningSpec.error?.message}`; + messageCode += '\n' + `${PrintTag.OHOS_REPORT_STATUS}: test=` + this.specService.currentRunningSpec.description; + messageCode += '\n' + `${PrintTag.OHOS_REPORT_STATUS_CODE}: -1` + '\n'; + } else if (this.specService.currentRunningSpec) { + if (this.specService.currentRunningSpec.fail) { + messageStack += `${PrintTag.OHOS_REPORT_STATUS}: stack=` + this.specService.currentRunningSpec.fail?.stack?.slice(0, -1); + messageCode += `${PrintTag.OHOS_REPORT_STATUS}: stream=`; + messageCode += this.specService.currentRunningSpec.expectMsg !== '' ? + `message: ${this.specService.currentRunningSpec.expectMsg}, Error in ${this.specService.currentRunningSpec.description}, ${this.specService.currentRunningSpec.fail?.message}` : + `Error in ${this.specService.currentRunningSpec.description}, ${this.specService.currentRunningSpec.fail?.message}`; + messageCode += '\n' + `${PrintTag.OHOS_REPORT_STATUS}: test=` + this.specService.currentRunningSpec.description; + messageCode += '\n' + `${PrintTag.OHOS_REPORT_STATUS_CODE}: -2` + '\n'; + } else { + messageStack += `${PrintTag.OHOS_REPORT_STATUS}: stream=`; + messageCode += `${PrintTag.OHOS_REPORT_STATUS}: test=` + this.specService.currentRunningSpec.description; + messageCode += '\n' + `${PrintTag.OHOS_REPORT_STATUS_CODE}: 0` + '\n'; + messageCode += this.specService.currentRunningSpec.isSkip ? (`${PrintTag.OHOS_REPORT_STATUS}: skipReason=` + this.specService.currentRunningSpec.skipReason + '\n') : ''; + } + } else { + messageCode += '\n'; + } + messageCode += `${PrintTag.OHOS_REPORT_STATUS}: consuming=` + this.specService.currentRunningSpec.duration + '\n'; + if (SysTestKit.workerPort !== null) { + messageCode += `${PrintTag.OHOS_REPORT_STATUS}: currentWorkerName=` + this.currentThreadName; + } + console.info(`${message}`); + console.info(`\n${messageStack}`); + console.info(`\n${messageCode}`); + await SysTestKit.print(message); + await SysTestKit.print(messageStack); + await SysTestKit.print(messageCode); + console.info(`${TAG}${this.specService.currentRunningSpec.description} specDone end print success`); + } + } +} + +export default OhReport; diff --git a/code/UI/CalendarViewSwitch/oh_modules/.ohpm/@ohos+hypium@1.0.19/oh_modules/@ohos/hypium/src/main/module/report/ReportExtend.js b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/@ohos+hypium@1.0.19/oh_modules/@ohos/hypium/src/main/module/report/ReportExtend.js new file mode 100644 index 0000000000000000000000000000000000000000..852fbcd5cbf97e776ebe5177a029df0f516594a5 --- /dev/null +++ b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/@ohos+hypium@1.0.19/oh_modules/@ohos/hypium/src/main/module/report/ReportExtend.js @@ -0,0 +1,137 @@ +/* + * 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. + */ + +class ReportExtend { + constructor(fileModule) { + this.id = 'extend'; + this.fileModule = fileModule; + } + + init(coreContext) { + this.coreContext = coreContext; + this.suiteService = this.coreContext.getDefaultService('suite'); + } + + taskStart() { + + } + + taskDone() { + const report = { + tag: 'testsuites', + name: 'summary_report', + timestamp: new Date().toDateString(), + time: '1', + errors: 0, + failures: 0, + tests: 0, + children: [] + }; + const rootSuite = this.suiteService.rootSuite; + if (rootSuite && rootSuite.childSuites) { + for (let testsuite of rootSuite.childSuites) { + let suiteReport = { + tag: 'testsuite', + name: testsuite['description'], + errors: 0, + tests: 0, + failures: 0, + time: '0.1', + children: [] + }; + let specs = testsuite['specs']; + for (let testcase of specs) { + report.tests++; + suiteReport.tests++; + let caseReport = { + tag: 'testcase', + name: testcase['description'], + status: 'run', + time: '0.0', + classname: testsuite['description'] + }; + if (testcase.error) { + caseReport['result'] = false; + caseReport['children'] = [{ + tag: 'error', + type: '', + message: testcase.error.message + }]; + report.errors++; + suiteReport.errors++; + } else if (testcase.result.failExpects.length > 0) { + caseReport['result'] = false; + let message = ''; + testcase.result.failExpects.forEach(failExpect => { + message += failExpect.message || ('expect ' + failExpect.actualValue + ' ' + failExpect.checkFunc + ' ' + (failExpect.expectValue || '')) + ';'; + }); + caseReport['children'] = [{ + tag: 'failure', + type: '', + message: message + }]; + report.failures++; + suiteReport.failures++; + } else { + caseReport['result'] = true; + } + suiteReport.children.push(caseReport); + } + report.children.push(suiteReport); + } + } + + let reportXml = '\n' + json2xml(report); + this.fileModule.writeText({ + uri: 'internal://app/report.xml', + text: reportXml, + success: function () { + console.info('call success callback success'); + }, + fail: function (data, code) { + console.info('call fail callback success:'); + }, + complete: function () { + console.info('call complete callback success'); + } + }); + } +} + +function json2xml(json) { + let tagName; + let hasChildren = false; + let childrenStr = ''; + let attrStr = ''; + for (let key in json) { + if (key === 'tag') { + tagName = json[key]; + } else if (key === 'children') { + if (json[key].length > 0) { + hasChildren = true; + for (let child of json[key]) { + childrenStr += json2xml(child); + } + } + } else { + attrStr += ` ${key}="${json[key]}"`; + } + } + let xml = `<${tagName}${attrStr}`; + xml += hasChildren ? `>${childrenStr}` : '/>'; + return xml; +} + +export default ReportExtend; diff --git a/code/UI/CalendarViewSwitch/oh_modules/.ohpm/@ohos+hypium@1.0.19/oh_modules/@ohos/hypium/src/main/service.js b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/@ohos+hypium@1.0.19/oh_modules/@ohos/hypium/src/main/service.js new file mode 100644 index 0000000000000000000000000000000000000000..92d46b77959af314f40bd601ede62b9b6d8b9ba2 --- /dev/null +++ b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/@ohos+hypium@1.0.19/oh_modules/@ohos/hypium/src/main/service.js @@ -0,0 +1,1230 @@ +/* + * 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 SysTestKit from './module/kit/SysTestKit'; +import { TAG } from './Constant'; +import LogExpectError from './module/report/LogExpectError'; +import { NestFilter } from './module/config/Filter'; + +class AssertException extends Error { + constructor(message) { + super(); + this.name = 'AssertException'; + this.message = message; + } +} + +function getFuncWithArgsZero(func, timeout, isStressTest) { + return new Promise(async (resolve, reject) => { + let timer = null; + if (!isStressTest) { + timer = setTimeout(() => { + reject(new Error('execute timeout ' + timeout + 'ms')); + }, timeout); + } + try { + await func(); + } catch (err) { + reject(err); + } + timer !== null ? clearTimeout(timer) : null; + resolve(); + }); +} + +function getFuncWithArgsOne(func, timeout, isStressTest) { + return new Promise(async (resolve, reject) => { + let timer = null; + if (!isStressTest) { + timer = setTimeout(() => { + reject(new Error('execute timeout ' + timeout + 'ms')); + }, timeout); + } + + function done() { + timer !== null ? clearTimeout(timer) : null; + resolve(); + } + + try { + await func(done); + } catch (err) { + timer !== null ? clearTimeout(timer) : null; + reject(err); + } + }); +} + +function getFuncWithArgsTwo(func, timeout, paramItem, isStressTest) { + return new Promise(async (resolve, reject) => { + let timer = null; + if (!isStressTest) { + timer = setTimeout(() => { + reject(new Error('execute timeout ' + timeout + 'ms')); + }, timeout); + } + + function done() { + timer !== null ? clearTimeout(timer) : null; + resolve(); + } + + try { + await func(done, paramItem); + } catch (err) { + timer !== null ? clearTimeout(timer) : null; + reject(err); + } + }); +} + +function processFunc(coreContext, func) { + let argNames = ((func || '').toString() + .replace(/((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg, '') + .match(/^(function)?\s*[^\(]*\(\s*([^\)]*)\)/m) || ['', '', ''])[2] + .split(',') // split parameters + .map(item => item.replace(/^\s*(_?)(.+?)\1\s*$/, name => name.split('=')[0].trim())) + .filter(String); + let funcLen = func.length; + let processedFunc; + const config = coreContext.getDefaultService('config'); + config.setSupportAsync(true); + const timeout = + (config.timeout === undefined ? 5000 : config.timeout); + const isStressTest = (coreContext.getServices('dataDriver') !== undefined || config.getStress() > 1); + switch (funcLen) { + case 0: { + processedFunc = function () { + return getFuncWithArgsZero(func, timeout, isStressTest); + }; + break; + } + case 1: { + if (argNames[0] === 'data') { + processedFunc = function (paramItem) { + func(paramItem); + }; + } else { + processedFunc = function () { + return getFuncWithArgsOne(func, timeout, isStressTest); + }; + } + break; + } + default: { + processedFunc = function (paramItem) { + return getFuncWithArgsTwo(func, timeout, paramItem, isStressTest); + }; + break; + } + } + return processedFunc; +} + +function secureRandomNumber() { + return crypto.randomBytes(8).readUInt32LE() / 0xffffffff; +} + +class SuiteService { + constructor(attr) { + this.id = attr.id; + this.rootSuite = new SuiteService.Suite({}); + this.currentRunningSuite = this.rootSuite; + this.suitesStack = [this.rootSuite]; + this.targetSuiteArray = []; + this.targetSpecArray = []; + this.currentRunningSuiteDesc = null; + this.fullRun = false; + this.isSkipSuite = false; + this.suiteSkipReason = null; + } + + describe(desc, func) { + const configService = this.coreContext.getDefaultService('config'); + if (this.suitesStack.some(suite => { return suite.description === desc })) { + console.error(`${TAG} Loop nesting occurs : ${desc}`); + this.suiteSkipReason = ''; + this.isSkipSuite = false; + return; + } + let isFilter = this.analyzeConfigServiceClass(configService.class, desc); + if (configService.filterSuite(desc) && isFilter) { + if (this.currentRunningSuite.description === '' || this.currentRunningSuite.description == null) { + console.info(`${TAG}filter suite : ${desc}`); + this.suiteSkipReason = ''; + this.isSkipSuite = false; + return; + } + } + const suite = new SuiteService.Suite({ description: desc }); + if (this.isSkipSuite) { + suite.isSkip = true; + suite.skipReason = this.suiteSkipReason; + } + this.suiteSkipReason = ''; + this.isSkipSuite = false; + if (typeof this.coreContext.getServices('dataDriver') !== 'undefined' && configService['dryRun'] !== 'true') { + let suiteStress = this.coreContext.getServices('dataDriver').dataDriver.getSuiteStress(desc); + for (let i = 1; i < suiteStress; i++) { + this.currentRunningSuite.childSuites.push(suite); + } + } + this.currentRunningSuite.childSuites.push(suite); + this.currentRunningSuite = suite; + this.suitesStack.push(suite); + func.call(); + this.suitesStack.pop(); + this.currentRunningSuite = this.suitesStack.pop(); + this.suitesStack.push(this.currentRunningSuite); + } + xdescribe(desc, func, reason) { + const configService = this.coreContext.getDefaultService('config'); + if (!configService.skipMessage && configService.runSkipped !== 'all') { + if (configService.runSkipped != null && configService.runSkipped !== '') { + let finalDesc = ''; + this.suitesStack.map(suite => { + finalDesc = finalDesc + '.' + suite.description; + }); + finalDesc = (finalDesc + '.' + desc).substring(2); + console.info(`${TAG} finalDesc ${finalDesc}`); + if (configService.checkIfSuiteInSkipRun(finalDesc)) { + console.info(`${TAG} runSkipped suite: ${desc}`); + } else { + console.info(reason == null ? `${TAG} skip suite: ${desc}` : `${TAG} skip suite: ${desc}, and the reason is ${reason}`); + return; + } + } else { + console.info(reason == null ? `${TAG} skip suite: ${desc}` : `${TAG} skip suite: ${desc}, and the reason is ${reason}`); + return; + } + } + this.isSkipSuite = true; + this.suiteSkipReason = reason; + this.describe(desc, func); + } + + beforeAll(func) { + this.currentRunningSuite.beforeAll.push(processFunc(this.coreContext, func)); + } + + beforeEach(func) { + this.currentRunningSuite.beforeEach.push(processFunc(this.coreContext, func)); + } + + beforeItSpecified(itDescs, func) { + this.currentRunningSuite.beforeItSpecified.set(itDescs, processFunc(this.coreContext, func)); + } + + afterItSpecified(itDescs, func) { + this.currentRunningSuite.afterItSpecified.set(itDescs, processFunc(this.coreContext, func)); + } + + afterAll(func) { + this.currentRunningSuite.afterAll.push(processFunc(this.coreContext, func)); + } + + afterEach(func) { + this.currentRunningSuite.afterEach.push(processFunc(this.coreContext, func)); + } + + getCurrentRunningSuite() { + return this.currentRunningSuite; + } + + setCurrentRunningSuite(suite) { + this.currentRunningSuite = suite; + } + + getRootSuite() { + return this.rootSuite; + } + + getCurrentRunningSuiteDesc() { + return this.currentRunningSuiteDesc; + } + + + setCurrentRunningSuiteDesc(suite, currentSuite, prefix) { + if (suite != null && suite === currentSuite) { + this.currentRunningSuiteDesc = prefix; + } else if (suite != null && suite !== currentSuite) { + suite.childSuites.forEach(it => { + let temp = prefix; + if (it.description != null || it.description !== '') { + temp = prefix === '' ? it.description : prefix + '.' + it.description; + } + this.setCurrentRunningSuiteDesc(it, currentSuite, temp); + } + ); + } + } + analyzeConfigServiceClass(configServiceClass, desc) { + if (configServiceClass == null || configServiceClass === '') { + this.fullRun = true + return false; + } + if (this.targetSuiteArray.length === 0) { + const targetArray = configServiceClass.split(','); + for (let index in targetArray) { + if (targetArray[index].includes('#')) { + this.targetSpecArray.push(targetArray[index]); + } else { + this.targetSuiteArray.push(targetArray[index]); + } + } + + } + return !configServiceClass.includes(desc); + + } + traversalResults(suite, obj, breakOnError) { + if (suite.childSuites.length === 0 && suite.specs.length === 0) { + return obj; + } + if (suite.specs.length > 0) { + for (const itItem of suite.specs) { + obj.total++; + let itInfo = { + currentThreadName: 'mainThread', + description: suite.description + '#' + itItem.description, + result: -3 + }; + if (SysTestKit.workerPort !== null) { + itInfo.currentThreadName = SysTestKit.workerPort.name; + } + obj.itItemList.push(itInfo); + if (breakOnError && (obj.error > 0 || obj.failure > 0)) { // breakOnError模式 + continue; + } + if (itItem.error) { + obj.error++; + itInfo.result = -1; + } else if (itItem.fail) { + obj.failure++; + itInfo.result = -2; + } else if (itItem.pass === true) { + obj.pass++; + itInfo.result = 0; + } + } + } + + obj.duration += suite.duration; + + if (suite.childSuites.length > 0) { + for (const suiteItem of suite.childSuites) { + this.traversalResults(suiteItem, obj, breakOnError); + } + } + } + + async setSuiteResults(suite, error, coreContext) { + if (suite.childSuites.length === 0 && suite.specs.length === 0) { + return obj; + } + if (suite.specs.length > 0) { + const specService = coreContext.getDefaultService('spec'); + for (const specItem of suite.specs) { + specService.setCurrentRunningSpec(specItem); + if (error instanceof AssertException) { + specItem.fail = error; + } else { + specItem.error = error; + } + await coreContext.fireEvents('spec', 'specStart', specItem); + await coreContext.fireEvents('spec', 'specDone', specItem); + } + } + if (suite.childSuites.length > 0) { + for (const suiteItem of suite.childSuites) { + await this.setSuiteResults(suiteItem, error, coreContext); + } + } + } + + getSummary() { + let suiteService = this.coreContext.getDefaultService('suite'); + let rootSuite = suiteService.rootSuite; + const specService = this.coreContext.getDefaultService('spec'); + const configService = this.coreContext.getDefaultService('config'); + let breakOnError = configService.isBreakOnError(); + let isError = specService.getStatus(); + let isBreaKOnError = breakOnError && isError; + // itItemList 保存当前用例执行情况, 发送到主线程用例计算最终结果 + let obj = { total: 0, failure: 0, error: 0, pass: 0, ignore: 0, duration: 0, itItemList: []}; + for (const suiteItem of rootSuite.childSuites) { + this.traversalResults(suiteItem, obj, isBreaKOnError); + } + obj.ignore = obj.total - obj.pass - obj.failure - obj.error; + return obj; + } + + init(coreContext) { + this.coreContext = coreContext; + } + + traversalSuites(suite, obj, configService) { + if (suite.childSuites.length === 0 && suite.specs.length === 0) { + return []; + } + if (suite.specs.length > 0) { + let itArray = []; + for (const itItem of suite['specs']) { + if (!configService.filterDesc(suite.description, itItem.description, itItem.fi, null)) { + itArray.push({ 'itName': itItem.description }); + } + } + obj[suite.description] = itArray; + } + if (suite.childSuites.length > 0) { + let suiteArray = []; + for (const suiteItem of suite.childSuites) { + let suiteObj = {}; + this.traversalSuites(suiteItem, suiteObj, configService); + if (!configService.filterSuite(suiteItem.description)) { + suiteArray.push(suiteObj); + } + } + obj.suites = suiteArray; + } + } + + async dryRun(abilityDelegator) { + console.info(`${TAG} rootSuite : ` + JSON.stringify(this.rootSuite)); + let obj = this.rootSuite; + let prefixStack = []; + let suiteArray = []; + let skipSuiteArray = []; + this.analyzeSuitesArray(prefixStack, suiteArray, skipSuiteArray, obj); + const configService = this.coreContext.getDefaultService('config'); + let result; + if (configService.skipMessage) { + result = { 'suites': suiteArray, 'skipSuites': skipSuiteArray }; + } else { + result = { 'suites': suiteArray }; + } + let strJson = JSON.stringify(result); + let strLen = strJson.length; + let maxLen = 500; + let maxCount = Math.floor(strLen / maxLen); + for (let count = 0; count <= maxCount; count++) { + await SysTestKit.print(strJson.substring(count * maxLen, (count + 1) * maxLen)); + } + console.info(`${TAG}dryRun print success`); + abilityDelegator.finishTest('dry run finished!!!', 0, () => { }); + } + + //将suitesArray的嵌套结构展开成三层结构 + analyzeSuitesArray(prefixStack, suiteArray, skipSuiteArray, obj) { + obj.childSuites.map(suite => { + if (suite.description != null && suite.description !== '') { + let prefix = ''; + if (prefixStack.length > 0) { + prefix = prefixStack.join('.') + '.' + suite.description; + } else { + prefix = suite.description; + } + prefixStack.push(suite.description); + let temp = {}; + temp[prefix] = []; + let skipTemp = {}; + skipTemp[prefix] = []; + suite.specs.map(spec => { + let it = { 'itName': spec.description }; + spec.isSkip ? skipTemp[prefix].push(it) : temp[prefix].push(it); + }); + suiteArray.push(temp); + skipSuiteArray.push(skipTemp); + } + this.analyzeSuitesArray(prefixStack, suiteArray, skipSuiteArray, suite); + prefixStack.pop(); + }); + } + //获取当前测试套下的所有测试用例数量 + getAllChildSuiteNum(suite, specArray) { + if (suite.specs != null) { + suite.specs.forEach(spec => specArray.push(spec)); + } + if (suite.childSuites != null) { + suite.childSuites.forEach(it => this.getAllChildSuiteNum(it, specArray)); + } + } + + execute() { + const configService = this.coreContext.getDefaultService('config'); + if (configService.filterValid.length !== 0) { + this.coreContext.fireEvents('task', 'incorrectFormat'); + return; + } + if (configService.filterXdescribe.length !== 0) { + this.coreContext.fireEvents('task', 'incorrectTestSuiteFormat'); + return; + } + if (configService.isRandom() && this.rootSuite.childSuites.length > 0) { + this.rootSuite.childSuites.sort(function () { + return Math.random().toFixed(1) > 0.5 ? -1 : 1; + }); + this.currentRunningSuite = this.rootSuite.childSuites[0]; + } + if (configService.isSupportAsync()) { + console.info(`${TAG} rootSuite:` + JSON.stringify(this.rootSuite)); + let asyncExecute = async () => { + await this.coreContext.fireEvents('task', 'taskStart'); + await this.rootSuite.asyncRun(this.coreContext); + }; + asyncExecute().then(async () => { + await this.coreContext.fireEvents('task', 'taskDone'); + }); + } else { + console.info('${TAG} rootSuite:' + JSON.stringify(this.rootSuite)); + this.coreContext.fireEvents('task', 'taskStart'); + this.rootSuite.run(this.coreContext); + this.coreContext.fireEvents('task', 'taskDone'); + } + } + + apis() { + const _this = this; + return { + describe: function (desc, func) { + return _this.describe(desc, func); + }, + xdescribe: function (desc, func, reason) { + return _this.xdescribe(desc, func, reason); + }, + beforeItSpecified: function (itDescs, func) { + return _this.beforeItSpecified(itDescs, func); + }, + afterItSpecified: function (itDescs, func) { + return _this.afterItSpecified(itDescs, func); + }, + beforeAll: function (func) { + return _this.beforeAll(func); + }, + beforeEach: function (func) { + return _this.beforeEach(func); + }, + afterAll: function (func) { + return _this.afterAll(func); + }, + afterEach: function (func) { + return _this.afterEach(func); + } + }; + } +} + +SuiteService.Suite = class { + constructor(attrs) { + this.description = attrs.description || ''; + this.childSuites = []; + this.specs = []; + this.beforeAll = []; + this.afterAll = []; + this.beforeItSpecified = new Map(); + this.afterItSpecified = new Map(); + this.beforeEach = []; + this.afterEach = []; + this.duration = 0; + this.hookError = null; + this.isSkip = false; + this.skipReason = ''; + } + + pushSpec(spec) { + this.specs.push(spec); + } + + removeSpec(desc) { + this.specs = this.specs.filter((item, index) => { + return item.description !== desc; + }); + } + + getSpecsNum() { + return this.specs.length; + } + + isRun(coreContext) { + const configService = coreContext.getDefaultService('config'); + const suiteService = coreContext.getDefaultService('suite'); + const specService = coreContext.getDefaultService('spec'); + let breakOnError = configService.isBreakOnError(); + let isError = specService.getStatus(); + return breakOnError && isError; + } + + run(coreContext) { + const suiteService = coreContext.getDefaultService('suite'); + suiteService.setCurrentRunningSuite(this); + if (this.description !== '') { + coreContext.fireEvents('suite', 'suiteStart', this); + } + this.runHookFunc('beforeAll'); + if (this.specs.length > 0) { + const configService = coreContext.getDefaultService('config'); + if (configService.isRandom()) { + this.specs.sort(function () { + return Math.random().toFixed(1) > 0.5 ? -1 : 1; + }); + } + for (let spec in this.specs) { + let isBreakOnError = this.isRun(coreContext); + if (isBreakOnError) { + break; + } + this.runHookFunc('beforeEach'); + spec.run(coreContext); + this.runHookFunc('afterEach'); + } + } + if (this.childSuites.length > 0) { + for (let suite in this.childSuites) { + let isBreakOnError = this.isRun(coreContext); + if (isBreakOnError) { + break; + } + suite.run(coreContext); + suiteService.setCurrentRunningSuite(suite); + } + } + this.runHookFunc('afterAll'); + if (this.description !== '') { + coreContext.fireEvents('suite', 'suiteDone'); + } + } + + async asyncRunSpecs(coreContext) { + const configService = coreContext.getDefaultService('config'); + if (configService.isRandom()) { + this.specs.sort(function () { + return Math.random().toFixed(1) > 0.5 ? -1 : 1; + }); + } + const specService = coreContext.getDefaultService('spec'); + for (let specItem of this.specs) { + specService.setCurrentRunningSpec(specItem); + // 遇错即停模式,发现用例有问题,直接返回,不在执行后面的it + let isBreakOnError = this.isRun(coreContext); + if (isBreakOnError) { + console.info('break description :' + this.description); + break; + } + await coreContext.fireEvents('spec', 'specStart', specItem); + try { + for (const [itNames, hookFunc] of this.beforeItSpecified) { + if ((Object.prototype.toString.call(itNames) === '[object Array]' && itNames.includes(specItem.description)) || + (Object.prototype.toString.call(itNames) === '[object String]' && itNames === specItem.description)) { + await Reflect.apply(hookFunc, null, []); + } + break; + } + await this.runAsyncHookFunc('beforeEach'); + await specItem.asyncRun(coreContext); + for (const [itNames, hookFunc] of this.afterItSpecified) { + if ((Object.prototype.toString.call(itNames) === '[object Array]' && itNames.includes(specItem.description)) || + (Object.prototype.toString.call(itNames) === '[object String]' && itNames === specItem.description)) { + await Reflect.apply(hookFunc, null, []); + } + break; + } + await this.runAsyncHookFunc('afterEach'); + } catch (e) { + console.error(`${TAG}stack:${e?.stack}`); + console.error(`${TAG}stack end`); + if (e instanceof AssertException) { + specItem.fail = e; + } else { + specItem.error = e; + } + specService.setStatus(true); + } + specItem.setResult(); + await coreContext.fireEvents('spec', 'specDone', specItem); + specService.setCurrentRunningSpec(null); + } + } + + async asyncRunChildSuites(coreContext) { + for (let i = 0; i < this.childSuites.length; i++) { + // 遇错即停模式, 发现用例有问题,直接返回,不在执行后面的description + let isBreakOnError = this.isRun(coreContext); + if (isBreakOnError) { + console.info(`${TAG}break description : ${this.description}`); + break; + } + await this.childSuites[i].asyncRun(coreContext); + } + } + + async asyncRun(coreContext) { + const suiteService = coreContext.getDefaultService('suite'); + const specService = coreContext.getDefaultService('spec'); + + suiteService.setCurrentRunningSuite(this); + suiteService.suitesStack.push(this); + if (this.description !== '') { + await coreContext.fireEvents('suite', 'suiteStart', this); + } + + try { + await this.runAsyncHookFunc('beforeAll'); + } catch (error) { + console.error(`${TAG}${error?.stack}`); + this.hookError = error; + } + + if (this.hookError !== null) { + specService.setStatus(true); + await suiteService.setSuiteResults(this, this.hookError, coreContext); + } + + if (this.specs.length > 0 && this.hookError === null) { + await this.asyncRunSpecs(coreContext); + } + + if (this.childSuites.length > 0 && this.hookError === null) { + await this.asyncRunChildSuites(coreContext); + } + + try { + await this.runAsyncHookFunc('afterAll'); + } catch (error) { + console.error(`${TAG}${error?.stack}`); + this.hookError = error; + specService.setStatus(true); + } + + if (this.description !== '') { + await coreContext.fireEvents('suite', 'suiteDone'); + let childSuite = suiteService.suitesStack.pop(); + let currentRunningSuite = suiteService.suitesStack.pop(); + suiteService.setCurrentRunningSuite(currentRunningSuite); + suiteService.suitesStack.push(currentRunningSuite); + } + } + + runHookFunc(hookName) { + if (this[hookName] && this[hookName].length > 0) { + this[hookName].forEach(func => { + try { + func(); + } catch (e) { + console.error(`${TAG}${e.stack}`); + } + }); + } + } + + async runAsyncHookFunc(hookName) { + for (const hookItem of this[hookName]) { + try { + await hookItem(); + } catch (error) { + error['message'] += `, error in ${hookName} function`; + throw error; + } + + } + } +}; + +class SpecService { + constructor(attr) { + this.id = attr.id; + this.totalTest = 0; + this.hasError = false; + this.skipSpecNum = 0; + this.isSkipSpec = false; + this.specSkipReason = ''; + } + + init(coreContext) { + this.coreContext = coreContext; + } + + setCurrentRunningSpec(spec) { + this.currentRunningSpec = spec; + } + + setStatus(obj) { + this.hasError = obj; + } + + getStatus() { + return this.hasError; + } + + getTestTotal() { + return this.totalTest; + } + + getCurrentRunningSpec() { + return this.currentRunningSpec; + } + + + getSkipSpecNum() { + return this.skipSpecNum; + } + + initSpecService() { + this.isSkipSpec = false; + this.specSkipReason = ''; + } + + it(desc, filter, func) { + const suiteService = this.coreContext.getDefaultService('suite'); + const configService = this.coreContext.getDefaultService('config'); + let isFilter = new NestFilter().filterNestName(suiteService.targetSuiteArray, suiteService.targetSpecArray, suiteService.suitesStack, desc); + if (configService.filterWithNest(desc, filter)) { + console.info(`${TAG}filter it :${desc}`); + this.initSpecService(); + return; + } + if (configService.filterDesc(suiteService.currentRunningSuite.description, desc, filter, this.coreContext) && isFilter && !suiteService.fullRun) { + console.info(`${TAG}filter it :${desc}`); + this.initSpecService(); + } else { + let processedFunc = processFunc(this.coreContext, func); + const spec = new SpecService.Spec({ description: desc, fi: filter, fn: processedFunc }); + if (this.isSkipSpec) { + spec.isSkip = true; + spec.skipReason = this.specSkipReason; + } + this.initSpecService(); + if (configService.runSkipped === 'skipped' && !spec.isSkip) { + console.info(`${TAG} runSkipped is skipped , just run xit, don't run it: ${spec.description}`); + return; + } + if (suiteService.getCurrentRunningSuite().isSkip && !spec.isSkip) { + configService.filterXdescribe.push(suiteService.getCurrentRunningSuite().description); + } + if (typeof this.coreContext.getServices('dataDriver') !== 'undefined' && configService['dryRun'] !== 'true') { + let specStress = this.coreContext.getServices('dataDriver').dataDriver.getSpecStress(desc); + for (let i = 1; i < specStress; i++) { + this.totalTest++; + suiteService.getCurrentRunningSuite().pushSpec(spec); + } + } + // dryRun 状态下不统计压力测试重复数据 + if (configService['dryRun'] !== 'true') { + let stress = configService.getStress(); // 命令配置压力测试 + console.info(`${TAG}stress length : ${stress}`); + for (let i = 1; i < stress; i++) { + this.totalTest++; + suiteService.getCurrentRunningSuite().pushSpec(spec); + } + } + this.totalTest++; + suiteService.getCurrentRunningSuite().pushSpec(spec); + } + } + + xit(desc, filter, func, reason) { + const configService = this.coreContext.getDefaultService('config'); + const suiteService = this.coreContext.getDefaultService('suite'); + if (!configService.skipMessage && configService.runSkipped !== 'all') { + if (configService.runSkipped != null && configService.runSkipped !== '') { + let finalDesc = ''; + suiteService.suitesStack.map(suite => { + finalDesc = finalDesc + '.' + suite.description; + }); + finalDesc = (finalDesc + '#' + desc).substring(2); + if (configService.checkIfSpecInSkipRun(finalDesc)) { + console.info(`${TAG} runSkipped spec: ${desc}`); + } else { + console.info(reason == null ? `${TAG} skip spec: ${desc}` : `${TAG} skip spec: ${desc}, and the reason is ${reason}`); + return; + } + } else { + console.info(reason == null ? `${TAG} skip spec: ${desc}` : `${TAG} skip spec: ${desc}, and the reason is ${reason}`); + return; + } + } + this.skipSpecNum++; + this.isSkipSpec = true; + this.specSkipReason = reason; + this.it(desc, filter, func); + } + + apis() { + const _this = this; + return { + it: function (desc, filter, func) { + return _this.it(desc, filter, func); + }, + xit: function (desc, filter, func, reason) { + return _this.xit(desc, filter, func, reason); + } + }; + } +} + +SpecService.Spec = class { + constructor(attrs) { + this.description = attrs.description || ''; + this.fi = attrs.fi; + this.fn = attrs.fn || function () { + }; + this.fail = undefined; + this.error = undefined; + this.duration = 0; + this.startTime = 0; + this.isExecuted = false; // 当前用例是否执行 + this.isSkip = false; + this.skipReason = ''; + this.expectMsg = ''; + } + + setResult() { + if (this.fail) { + this.pass = false; + } else { + this.pass = true; + } + } + + run(coreContext) { + const specService = coreContext.getDefaultService('spec'); + specService.setCurrentRunningSpec(this); + coreContext.fireEvents('spec', 'specStart', this); + this.isExecuted = true; + try { + let dataDriver = coreContext.getServices('dataDriver'); + if (typeof dataDriver === 'undefined') { + this.fn(); + } else { + let suiteParams = dataDriver.dataDriver.getSuiteParams(); + let specParams = dataDriver.dataDriver.getSpecParams(); + console.info(`${TAG}[suite params] ${JSON.stringify(suiteParams)}`); + console.info(`${TAG}[spec params] ${JSON.stringify(specParams)}`); + if (this.fn.length === 0) { + this.fn(); + } else if (specParams.length === 0) { + this.fn(suiteParams); + } else { + specParams.forEach(paramItem => this.fn(Object.assign({}, paramItem, suiteParams))); + } + } + this.setResult(); + } catch (e) { + this.error = e; + specService.setStatus(true); + } + coreContext.fireEvents('spec', 'specDone', this); + } + + async asyncRun(coreContext) { + const dataDriver = coreContext.getServices('dataDriver'); + if (typeof dataDriver === 'undefined') { + await this.fn(); + } else { + const suiteParams = dataDriver.dataDriver.getSuiteParams(); + const specParams = dataDriver.dataDriver.getSpecParams(); + console.info(`[suite params] ${JSON.stringify(suiteParams)}`); + console.info(`[spec params] ${JSON.stringify(specParams)}`); + if (this.fn.length === 0) { + await this.fn(); + } else if (specParams.length === 0) { + await this.fn(suiteParams); + } else { + for (const paramItem of specParams) { + await this.fn(Object.assign({}, paramItem, suiteParams)); + } + } + } + + this.isExecuted = true; + } + + filterCheck(coreContext) { + const specService = coreContext.getDefaultService('spec'); + specService.setCurrentRunningSpec(this); + return true; + } +}; + +class ExpectService { + constructor(attr) { + this.id = attr.id; + this.matchers = {}; + this.customMatchers = []; + } + + expect(actualValue) { + return this.wrapMatchers(actualValue); + } + + init(coreContext) { + this.coreContext = coreContext; + this.addMatchers(this.basicMatchers()); + } + + addMatchers(matchers) { + for (const matcherName in matchers) { + if (Object.prototype.hasOwnProperty.call(matchers, matcherName)) { + this.matchers[matcherName] = matchers[matcherName]; + } + } + } + + removeMatchers(customAssertionName) { + if (customAssertionName === 'all') { + for (const matcherName in this.matchers) { + this.matchers[matcherName] = this.customMatchers.includes(matcherName) ? (() => {throw new Error(`${matcherName} is unregistered`)}) : undefined; + } + }else { + this.matchers[customAssertionName] = () => { + throw new Error(`${customAssertionName} is unregistered`) + }; + } + } + + basicMatchers() { + return { + assertTrue: function (actualValue) { + return { + pass: (actualValue) === true, + message: 'expect true, actualValue is ' + actualValue + }; + }, + assertEqual: function (actualValue, args) { + let msg = 'expect ' + actualValue + ' equals ' + args[0]; + if (actualValue == args[0]) { // 数值相同,提示数据类型 + const aClassName = Object.prototype.toString.call(actualValue); + const bClassName = Object.prototype.toString.call(args[0]); + msg = 'expect ' + actualValue + aClassName + ' equals ' + args[0] + bClassName + 'strict mode inspect type'; + } + return { + pass: (actualValue) === args[0], + expectValue: args[0], + message: msg + }; + }, + assertThrow: function (actual, args) { + const result = { + pass: false + }; + if (typeof actual !== 'function') { + result.message = 'toThrow\'s Actual should be a Function'; + } else { + let hasThrow = false; + let throwError; + try { + actual(); + } catch (e) { + hasThrow = true; + throwError = e; + } + if (!hasThrow) { + result.message = 'function did not throw an exception'; + } else if (throwError && throwError.message === args[0]) { + result.pass = true; + } else { + result.message = `expect to throw ${args[0]} , actual throw ${throwError.message}`; + } + } + return result; + } + }; + } + + initWrapMatchers(currentRunningSpec) { + return { + // 翻转标识 + isNot: false, + // 翻转方法 + not: function () { + this.isNot = true; + return this; + }, + message: function (msg) { + currentRunningSpec.expectMsg = msg; + console.info(`${TAG} msg: ${msg}`); + return this; + } + }; + + } + wrapMatchers(actualValue) { + const _this = this; + const specService = _this.coreContext.getDefaultService('spec'); + const currentRunningSpec = specService.getCurrentRunningSpec(); + const wrappedMatchers = this.initWrapMatchers(currentRunningSpec); + const currentRunningSuite = _this.coreContext.getDefaultService('suite').getCurrentRunningSuite(); + for (const matcherName in this.matchers) { + let result = Object.prototype.hasOwnProperty.call(this.matchers, matcherName); + if (!result) { + continue; + } + if (matcherName.search('assertPromise') == 0) { + wrappedMatchers[matcherName] = async function () { + await _this.matchers[matcherName](actualValue, arguments).then(function (result) { + if (wrappedMatchers.isNot) { + result.pass = !result.pass; + } + result.actualValue = actualValue; + result.checkFunc = matcherName; + if (!result.pass) { + const assertError = new AssertException(result.message); + currentRunningSpec ? currentRunningSpec.fail = assertError : currentRunningSuite.hookError = assertError; + throw assertError; + } + }); + }; + } else { + wrappedMatchers[matcherName] = function () { + const result = _this.customMatchers.includes(matcherName) ? _this.matchers[matcherName](actualValue, arguments[0]) : _this.matchers[matcherName](actualValue, arguments); + if (wrappedMatchers.isNot) { + result.pass = !result.pass; + result.message = LogExpectError.getErrorMsg(matcherName, actualValue, arguments[0], result.message); + } + result.actualValue = actualValue; + result.checkFunc = matcherName; + if (!result.pass) { + const assertError = new AssertException(result.message); + currentRunningSpec ? currentRunningSpec.fail = assertError : currentRunningSuite.hookError = assertError; + throw assertError; + } + }; + } + } + return wrappedMatchers; + } + + apis() { + const _this = this; + return { + expect: function (actualValue) { + return _this.expect(actualValue); + } + }; + } +} + +class ReportService { + constructor(attr) { + this.id = attr.id; + } + + init(coreContext) { + this.coreContext = coreContext; + this.specService = this.coreContext.getDefaultService('spec'); + this.suiteService = this.coreContext.getDefaultService('suite'); + this.duration = 0; + } + + taskStart() { + console.info(`${TAG}[start] start run suites`); + } + + async suiteStart() { + console.info(`${TAG}[suite start]${this.suiteService.getCurrentRunningSuite().description}`); + } + + async specStart() { + console.info(`${TAG}start running case '${this.specService.currentRunningSpec.description}'`); + this.index = this.index + 1; + let spec = this.specService.currentRunningSpec; + spec.startTime = await SysTestKit.getRealTime(); + } + + async specDone() { + let msg = ''; + let spec = this.specService.currentRunningSpec; + let suite = this.suiteService.currentRunningSuite; + spec.duration = await SysTestKit.getRealTime() - spec.startTime; + suite.duration += spec.duration; + if (spec.error) { + this.formatPrint('error', spec.description + ' ; consuming ' + spec.duration + 'ms'); + this.formatPrint('errorDetail', spec.error); + } else if (spec.fail) { + this.formatPrint('fail', spec.description + ' ; consuming ' + spec.duration + 'ms'); + this.formatPrint('failDetail', spec.fail?.message); + } else { + this.formatPrint('pass', spec.description + ' ; consuming ' + spec.duration + 'ms'); + } + this.formatPrint(this.specService.currentRunningSpec.error, msg); + } + + suiteDone() { + let suite = this.suiteService.currentRunningSuite; + let message = suite.hookError ? `, ${suite.hookError?.message}` : ''; + console.info(`[suite end] ${suite.description} consuming ${suite.duration} ms${message}`); + } + + taskDone() { + let msg = ''; + let summary = this.suiteService.getSummary(); + msg = 'total cases:' + summary.total + ';failure ' + summary.failure + ',' + 'error ' + summary.error; + msg += ',pass ' + summary.pass + '; consuming ' + summary.duration + 'ms'; + console.info(`${TAG}${msg}`); + console.info(`${TAG}[end] run suites end`); + } + + incorrectFormat() { + if (this.coreContext.getDefaultService('config').filterValid.length !== 0) { + this.coreContext.getDefaultService('config').filterValid.forEach(function (item) { + console.info(`${TAG}this param ${item} is invalid`); + }); + } + } + + incorrectTestSuiteFormat() { + if (this.coreContext.getDefaultService('config').filterXdescribe.length !== 0) { + this.coreContext.getDefaultService('config').filterXdescribe.forEach(function (item) { + console.info(`${TAG}xdescribe: ${item} should not contain it`); + }) + } + } + + formatPrint(type, msg) { + switch (type) { + case 'pass': + console.info(`${TAG}[pass]${msg}`); + break; + case 'fail': + console.info(`${TAG}[fail]${msg}`); + break; + case 'failDetail': + console.info(`${TAG}[failDetail]${msg}`); + break; + case 'error': + console.info(`${TAG}[error]${msg}`); + break; + case 'errorDetail': + console.info(`${TAG}[errorDetail]${msg}`); + break; + } + } + + sleep(numberMillis) { + var now = new Date(); + var exitTime = now.getTime() + numberMillis; + while (true) { + now = new Date(); + if (now.getTime() > exitTime) { + return; + } + } + } +} + +export { + SuiteService, + SpecService, + ExpectService, + ReportService +}; diff --git a/code/UI/CalendarViewSwitch/oh_modules/.ohpm/@ohos+hypium@1.0.19/oh_modules/@ohos/hypium/src/main/testrunner/OpenHarmonyTestRunner.ts b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/@ohos+hypium@1.0.19/oh_modules/@ohos/hypium/src/main/testrunner/OpenHarmonyTestRunner.ts new file mode 100644 index 0000000000000000000000000000000000000000..e6f4c1b12dd69714ed5a4524671abca1fbcaa58c --- /dev/null +++ b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/@ohos+hypium@1.0.19/oh_modules/@ohos/hypium/src/main/testrunner/OpenHarmonyTestRunner.ts @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2023-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 { abilityDelegatorRegistry, TestRunner } from '@kit.TestKit'; +import { BusinessError } from '@kit.BasicServicesKit'; +import { hilog } from '@kit.PerformanceAnalysisKit'; +import { resourceManager } from '@kit.LocalizationKit'; +import { util } from '@kit.ArkTS'; +import { Hypium } from '@ohos/hypium'; +import testsuite from '../test/List.test'; + +let abilityDelegator: abilityDelegatorRegistry.AbilityDelegator; +let abilityDelegatorArguments: abilityDelegatorRegistry.AbilityDelegatorArgs; +let jsonPath: string = 'mock/mock-config.json'; +let domain: number = 0x0000; //日志标识,0x0000作为测试框架的业务标识 +let tag: string = 'testTag'; //日志标识字符串,作为tag标识当前runner类下的测试行为 + +export default class OpenHarmonyTestRunner implements TestRunner { + constructor() { + } + + onPrepare() { + hilog.info(domain, tag, '%{public}s', 'OpenHarmonyTestRunner OnPrepare'); + } + + async onRun() { + hilog.info(domain, tag, '%{public}s', 'OpenHarmonyTestRunner onRun run'); + abilityDelegatorArguments = abilityDelegatorRegistry.getArguments(); + abilityDelegator = abilityDelegatorRegistry.getAbilityDelegator(); + let moduleName = abilityDelegatorArguments.parameters['-m']; + let context = abilityDelegator.getAppContext().getApplicationContext().createModuleContext(moduleName); + let mResourceManager = context.resourceManager; + await checkMock(abilityDelegator, mResourceManager); + hilog.info(domain, tag, '%{public}s', 'start run testcase!!!'); + Hypium.hypiumTest(abilityDelegator, abilityDelegatorArguments, testsuite); + hilog.info(domain, tag, '%{public}s', 'OpenHarmonyTestRunner onRun end'); + } +} + +async function checkMock(abilityDelegator: abilityDelegatorRegistry.AbilityDelegator, resourceManager: resourceManager.ResourceManager) { + let rawFile: Uint8Array; + try { + rawFile = resourceManager.getRawFileContentSync(jsonPath); + hilog.info(domain, tag, 'MockList file exists'); + let mockStr: string = util.TextDecoder.create("utf-8", { ignoreBOM: true }).decodeWithStream(rawFile); + let mockMap: Record = getMockList(mockStr); + try { + abilityDelegator.setMockList(mockMap); + } catch (error) { + let code = (error as BusinessError).code; + let message = (error as BusinessError).message; + hilog.error(domain, tag, `abilityDelegator.setMockList failed, error code: ${code}, message: ${message}.`); + } + } catch (error) { + let code = (error as BusinessError).code; + let message = (error as BusinessError).message; + hilog.error(domain, tag, `ResourceManager:callback getRawFileContent failed, error code: ${code}, message: ${message}.`); + } +} + +function getMockList(jsonStr: string) { + let jsonObj: Record = JSON.parse(jsonStr); + let map: Map = new Map(Object.entries(jsonObj)); + let mockList: Record = {}; + map.forEach((value: object, key: string) => { + let realValue: string = value['source'].toString(); + mockList[key] = realValue; + }); + hilog.info(domain, tag, '%{public}s', 'mock-json value:' + JSON.stringify(mockList) ?? ''); + return mockList; +} \ No newline at end of file diff --git a/code/UI/CalendarViewSwitch/oh_modules/.ohpm/oh_modules/@ohos/hypium/BuildProfile.ets b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/oh_modules/@ohos/hypium/BuildProfile.ets new file mode 100644 index 0000000000000000000000000000000000000000..b054e98af7ad6092740cef28d7dcf6207e514dcb --- /dev/null +++ b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/oh_modules/@ohos/hypium/BuildProfile.ets @@ -0,0 +1,5 @@ +export default class BuildProfile { + static readonly HAR_VERSION = '1.0.19'; + static readonly BUILD_MODE_NAME = 'debug'; + static readonly DEBUG = true; +} \ No newline at end of file diff --git a/code/UI/CalendarViewSwitch/oh_modules/.ohpm/oh_modules/@ohos/hypium/CHANGELOG.md b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/oh_modules/@ohos/hypium/CHANGELOG.md new file mode 100644 index 0000000000000000000000000000000000000000..309d707c3d19f506f5582509edf1d0db9011a5b1 --- /dev/null +++ b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/oh_modules/@ohos/hypium/CHANGELOG.md @@ -0,0 +1,24 @@ +## 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.18-rc.1 +规范日志格式 +## 1.0.19 +- 规范日志格式 +- 代码规范整改 \ No newline at end of file diff --git a/code/UI/CalendarViewSwitch/oh_modules/.ohpm/oh_modules/@ohos/hypium/LICENSE b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/oh_modules/@ohos/hypium/LICENSE new file mode 100644 index 0000000000000000000000000000000000000000..4947287f7b5ccb5d1e8b7b2d3aa5d89f322c160d --- /dev/null +++ b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/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/code/UI/CalendarViewSwitch/oh_modules/.ohpm/oh_modules/@ohos/hypium/README.md b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/oh_modules/@ohos/hypium/README.md new file mode 100644 index 0000000000000000000000000000000000000000..6d795105533a9b3b9949e91d2c3dd14e8f867433 --- /dev/null +++ b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/oh_modules/@ohos/hypium/README.md @@ -0,0 +1,219 @@ +
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后缀 +``` + +## 安装使用 +*** +- 在DevEco Studio内使用Hypium +- 工程级package.json内配置: +```json +"dependencies": { + "@ohos/hypium": "1.0.19" +} +``` +注: +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/code/UI/CalendarViewSwitch/oh_modules/.ohpm/oh_modules/@ohos/hypium/build-profile.json5 b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/oh_modules/@ohos/hypium/build-profile.json5 new file mode 100644 index 0000000000000000000000000000000000000000..312d38eb08629793b3484c7373213f22840c8d82 --- /dev/null +++ b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/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/code/UI/CalendarViewSwitch/oh_modules/.ohpm/oh_modules/@ohos/hypium/hvigorfile.ts b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/oh_modules/@ohos/hypium/hvigorfile.ts new file mode 100644 index 0000000000000000000000000000000000000000..42187071482d292588ad40babeda74f7b8d97a23 --- /dev/null +++ b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/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/code/UI/CalendarViewSwitch/oh_modules/.ohpm/oh_modules/@ohos/hypium/index.d.ts b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/oh_modules/@ohos/hypium/index.d.ts new file mode 100644 index 0000000000000000000000000000000000000000..7272b5fa839a2cd510d0c70d517bb6800133dba2 --- /dev/null +++ b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/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/code/UI/CalendarViewSwitch/oh_modules/.ohpm/oh_modules/@ohos/hypium/index.ets b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/oh_modules/@ohos/hypium/index.ets new file mode 100644 index 0000000000000000000000000000000000000000..02a237f1c999d1f48b3974b6076d5dae9213245a --- /dev/null +++ b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/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): (f?: 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/code/UI/CalendarViewSwitch/oh_modules/.ohpm/oh_modules/@ohos/hypium/index.js b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/oh_modules/@ohos/hypium/index.js new file mode 100644 index 0000000000000000000000000000000000000000..02d06d9d1b4b478aa2aec70ba3a73a5e123c98db --- /dev/null +++ b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/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/code/UI/CalendarViewSwitch/oh_modules/.ohpm/oh_modules/@ohos/hypium/index.ts b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/oh_modules/@ohos/hypium/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..b7082ebc98214b58d41e8681791809f1aee48f12 --- /dev/null +++ b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/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/code/UI/CalendarViewSwitch/oh_modules/.ohpm/oh_modules/@ohos/hypium/oh-package.json5 b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/oh_modules/@ohos/hypium/oh-package.json5 new file mode 100644 index 0000000000000000000000000000000000000000..d91344eda60f35477a5caf4bf5c116ffac2e53db --- /dev/null +++ b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/oh_modules/@ohos/hypium/oh-package.json5 @@ -0,0 +1 @@ +{"name":"@ohos/hypium","version":"1.0.19","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":{}} diff --git a/code/UI/CalendarViewSwitch/oh_modules/.ohpm/oh_modules/@ohos/hypium/src/main/Constant.js b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/oh_modules/@ohos/hypium/src/main/Constant.js new file mode 100644 index 0000000000000000000000000000000000000000..f470d69cd9a3302b19d45c147ca7d7c1dd8a3b18 --- /dev/null +++ b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/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/code/UI/CalendarViewSwitch/oh_modules/.ohpm/oh_modules/@ohos/hypium/src/main/core.js b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/oh_modules/@ohos/hypium/src/main/core.js new file mode 100644 index 0000000000000000000000000000000000000000..cfcb5f17287208f5e6869b4248faf6c9093002d9 --- /dev/null +++ b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/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/code/UI/CalendarViewSwitch/oh_modules/.ohpm/oh_modules/@ohos/hypium/src/main/event.js b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/oh_modules/@ohos/hypium/src/main/event.js new file mode 100644 index 0000000000000000000000000000000000000000..3be0211f01646c9c269c2425cbee82c87ac6d9ea --- /dev/null +++ b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/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/code/UI/CalendarViewSwitch/oh_modules/.ohpm/oh_modules/@ohos/hypium/src/main/interface.js b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/oh_modules/@ohos/hypium/src/main/interface.js new file mode 100644 index 0000000000000000000000000000000000000000..1bf43509ac3f70f1275e1da79388e1511e72a3f9 --- /dev/null +++ b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/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/code/UI/CalendarViewSwitch/oh_modules/.ohpm/oh_modules/@ohos/hypium/src/main/module.json b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/oh_modules/@ohos/hypium/src/main/module.json new file mode 100644 index 0000000000000000000000000000000000000000..b0e022bd13205c4c3310480d6732db4707193b3a --- /dev/null +++ b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/oh_modules/@ohos/hypium/src/main/module.json @@ -0,0 +1,26 @@ +{ + "app": { + "bundleName": "com.ohos.myapplication", + "debug": true, + "versionCode": 1000000, + "versionName": "1.0.0", + "minAPIVersion": 40100011, + "targetAPIVersion": 40100011, + "apiReleaseType": "Beta1", + "compileSdkVersion": "4.1.0.55", + "compileSdkType": "HarmonyOS", + "bundleType": "app" + }, + "module": { + "name": "hypium", + "type": "har", + "deviceTypes": [ + "default", + "tablet", + "tv", + "wearable", + "car" + ], + "installationFree": false + } +} diff --git a/code/UI/CalendarViewSwitch/oh_modules/.ohpm/oh_modules/@ohos/hypium/src/main/module/assert/ExpectExtend.js b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/oh_modules/@ohos/hypium/src/main/module/assert/ExpectExtend.js new file mode 100644 index 0000000000000000000000000000000000000000..d10d15e6f9955c6d04610101f8766c951ee1a35d --- /dev/null +++ b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/oh_modules/@ohos/hypium/src/main/module/assert/ExpectExtend.js @@ -0,0 +1,85 @@ +/* + * 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 assertNull from './assertNull'; +import assertClose from './assertClose'; +import assertContain from './assertContain'; +import assertLess from './assertLess'; +import assertLarger from './assertLarger'; +import assertFail from './assertFail'; +import assertUndefined from './assertUndefined'; +import assertFalse from './assertFalse'; +import assertInstanceOf from './assertInstanceOf'; +import assertThrowError from './assertThrowError'; +import assertLargerOrEqual from './assertLargerOrEqual' +import assertLessOrEqual from './assertLessOrEqual' +import assertNaN from './assertNaN' +import assertNegUnlimited from './assertNegUnlimited' +import assertPosUnlimited from './assertPosUnlimited' +import assertDeepEquals from './deepEquals/assertDeepEquals' +import assertPromiseIsPending from './assertPromiseIsPending'; +import assertPromiseIsRejected from './assertPromiseIsRejected'; +import assertPromiseIsRejectedWith from './assertPromiseIsRejectedWith'; +import assertPromiseIsRejectedWithError from './assertPromiseIsRejectedWithError'; +import assertPromiseIsResolved from './assertPromiseIsResolved'; +import assertPromiseIsResolvedWith from './assertPromiseIsResolvedWith'; +class ExpectExtend { + constructor(attr) { + this.id = attr.id; + this.matchers = {}; + } + + extendsMatchers() { + this.matchers.assertNull = assertNull; + this.matchers.assertClose = assertClose; + this.matchers.assertContain = assertContain; + this.matchers.assertLess = assertLess; + this.matchers.assertLarger = assertLarger; + this.matchers.assertFail = assertFail; + this.matchers.assertUndefined = assertUndefined; + this.matchers.assertFalse = assertFalse; + this.matchers.assertInstanceOf = assertInstanceOf; + this.matchers.assertThrowError = assertThrowError; + this.matchers.assertLargerOrEqual = assertLargerOrEqual; + this.matchers.assertLessOrEqual = assertLessOrEqual; + this.matchers.assertNaN = assertNaN; + this.matchers.assertNegUnlimited = assertNegUnlimited; + this.matchers.assertPosUnlimited = assertPosUnlimited; + this.matchers.assertDeepEquals = assertDeepEquals; + this.matchers.assertPromiseIsPending = assertPromiseIsPending; + this.matchers.assertPromiseIsRejected = assertPromiseIsRejected; + this.matchers.assertPromiseIsRejectedWith = assertPromiseIsRejectedWith; + this.matchers.assertPromiseIsRejectedWithError = assertPromiseIsRejectedWithError; + this.matchers.assertPromiseIsResolved = assertPromiseIsResolved; + this.matchers.assertPromiseIsResolvedWith = assertPromiseIsResolvedWith; + } + + init(coreContext) { + this.coreContext = coreContext; + this.extendsMatchers(); + const expectService = this.coreContext.getDefaultService('expect'); + expectService.addMatchers(this.matchers); + } + + apis() { + return { + 'expect': function (actualValue) { + return this.coreContext.getDefaultService('expect').expect(actualValue); + } + }; + } +} + +export default ExpectExtend; diff --git a/code/UI/CalendarViewSwitch/oh_modules/.ohpm/oh_modules/@ohos/hypium/src/main/module/assert/assertClose.js b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/oh_modules/@ohos/hypium/src/main/module/assert/assertClose.js new file mode 100644 index 0000000000000000000000000000000000000000..7e692bd25f1c026640978a042a9c9f64b0e8d5d3 --- /dev/null +++ b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/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/code/UI/CalendarViewSwitch/oh_modules/.ohpm/oh_modules/@ohos/hypium/src/main/module/assert/assertContain.js b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/oh_modules/@ohos/hypium/src/main/module/assert/assertContain.js new file mode 100644 index 0000000000000000000000000000000000000000..7fba0d9755503e5e926f6c1a4e425e0d1cf47570 --- /dev/null +++ b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/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/code/UI/CalendarViewSwitch/oh_modules/.ohpm/oh_modules/@ohos/hypium/src/main/module/assert/assertFail.js b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/oh_modules/@ohos/hypium/src/main/module/assert/assertFail.js new file mode 100644 index 0000000000000000000000000000000000000000..8ab4ac5caef712c75c4eac49dfbbb91d33669d9a --- /dev/null +++ b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/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/code/UI/CalendarViewSwitch/oh_modules/.ohpm/oh_modules/@ohos/hypium/src/main/module/assert/assertFalse.js b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/oh_modules/@ohos/hypium/src/main/module/assert/assertFalse.js new file mode 100644 index 0000000000000000000000000000000000000000..c5008e94f4b2ce13ed35b604811793c76b542347 --- /dev/null +++ b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/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/code/UI/CalendarViewSwitch/oh_modules/.ohpm/oh_modules/@ohos/hypium/src/main/module/assert/assertInstanceOf.js b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/oh_modules/@ohos/hypium/src/main/module/assert/assertInstanceOf.js new file mode 100644 index 0000000000000000000000000000000000000000..1e11b93f7251c67f5455c5007cd7be268aa53b32 --- /dev/null +++ b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/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/code/UI/CalendarViewSwitch/oh_modules/.ohpm/oh_modules/@ohos/hypium/src/main/module/assert/assertLarger.js b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/oh_modules/@ohos/hypium/src/main/module/assert/assertLarger.js new file mode 100644 index 0000000000000000000000000000000000000000..a74f4a8cedaf3add9c2dc2d3799081a83198732f --- /dev/null +++ b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/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/code/UI/CalendarViewSwitch/oh_modules/.ohpm/oh_modules/@ohos/hypium/src/main/module/assert/assertLargerOrEqual.js b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/oh_modules/@ohos/hypium/src/main/module/assert/assertLargerOrEqual.js new file mode 100644 index 0000000000000000000000000000000000000000..e847e6c217364b7f69c173c66fb98d10efc45ef1 --- /dev/null +++ b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/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/code/UI/CalendarViewSwitch/oh_modules/.ohpm/oh_modules/@ohos/hypium/src/main/module/assert/assertLess.js b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/oh_modules/@ohos/hypium/src/main/module/assert/assertLess.js new file mode 100644 index 0000000000000000000000000000000000000000..17e84b0abaeb20804048a5a15c19e0603634846d --- /dev/null +++ b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/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/code/UI/CalendarViewSwitch/oh_modules/.ohpm/oh_modules/@ohos/hypium/src/main/module/assert/assertLessOrEqual.js b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/oh_modules/@ohos/hypium/src/main/module/assert/assertLessOrEqual.js new file mode 100644 index 0000000000000000000000000000000000000000..f754f97ffa9d24e7852efe2423a1dd35d448af82 --- /dev/null +++ b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/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/code/UI/CalendarViewSwitch/oh_modules/.ohpm/oh_modules/@ohos/hypium/src/main/module/assert/assertNaN.js b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/oh_modules/@ohos/hypium/src/main/module/assert/assertNaN.js new file mode 100644 index 0000000000000000000000000000000000000000..8d45d6a93b86c5ed325a68b32ff014835993a58e --- /dev/null +++ b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/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/code/UI/CalendarViewSwitch/oh_modules/.ohpm/oh_modules/@ohos/hypium/src/main/module/assert/assertNegUnlimited.js b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/oh_modules/@ohos/hypium/src/main/module/assert/assertNegUnlimited.js new file mode 100644 index 0000000000000000000000000000000000000000..ceac555afc826e057970e6cfe9c73b322c672aa2 --- /dev/null +++ b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/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/code/UI/CalendarViewSwitch/oh_modules/.ohpm/oh_modules/@ohos/hypium/src/main/module/assert/assertNull.js b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/oh_modules/@ohos/hypium/src/main/module/assert/assertNull.js new file mode 100644 index 0000000000000000000000000000000000000000..53a7bad827323a98d3302a4e7eea679551b459c5 --- /dev/null +++ b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/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/code/UI/CalendarViewSwitch/oh_modules/.ohpm/oh_modules/@ohos/hypium/src/main/module/assert/assertPosUnlimited.js b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/oh_modules/@ohos/hypium/src/main/module/assert/assertPosUnlimited.js new file mode 100644 index 0000000000000000000000000000000000000000..6e68c0e2b6c499f4dc3dd56c13e9ea1073a3c54c --- /dev/null +++ b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/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; diff --git a/code/UI/CalendarViewSwitch/oh_modules/.ohpm/oh_modules/@ohos/hypium/src/main/module/assert/assertPromiseIsPending.js b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/oh_modules/@ohos/hypium/src/main/module/assert/assertPromiseIsPending.js new file mode 100644 index 0000000000000000000000000000000000000000..7e2ca2ce14d50c39554fc1157d6d4eb9329d5c39 --- /dev/null +++ b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/oh_modules/@ohos/hypium/src/main/module/assert/assertPromiseIsPending.js @@ -0,0 +1,42 @@ +/* + * 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 isPromiseLike from './isPromiseLike'; + +function assertPromiseIsPending(actualPromise) { + if (!isPromiseLike(actualPromise)) { + return Promise.reject().then(function () { + }, function () { + return {pass: false, message: 'Expected not be called on a promise.'}; + }); + } + const helper = {}; + return Promise.race([actualPromise, Promise.resolve(helper)]).then( + function (got) { + return helper === got ? {pass: true, message: 'actualValue is isPending'} + : { + pass: false, + message: 'expect isPending, actualValue is resolve' + }; + }, + function () { + return { + pass: false + , message: 'expect isPending, actualValue is reject' + }; + }); +} + +export default assertPromiseIsPending; \ No newline at end of file diff --git a/code/UI/CalendarViewSwitch/oh_modules/.ohpm/oh_modules/@ohos/hypium/src/main/module/assert/assertPromiseIsRejected.js b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/oh_modules/@ohos/hypium/src/main/module/assert/assertPromiseIsRejected.js new file mode 100644 index 0000000000000000000000000000000000000000..380075a369a84d6856e7f2db366f704e04302a8d --- /dev/null +++ b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/oh_modules/@ohos/hypium/src/main/module/assert/assertPromiseIsRejected.js @@ -0,0 +1,38 @@ +/* + * 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 isPromiseLike from './isPromiseLike'; + +function assertPromiseIsRejected(actualPromise) { + if (!isPromiseLike(actualPromise)) { + return Promise.reject().then(function () { + }, function () { + return {pass: false, message: 'Expected not be called on a promise.'}; + }); + } + return actualPromise.then( + function (got) { + return { + pass: false, + message: 'expect isRejected, but actualValue is resolve' + }; + }, + function () { + return {pass: true, message: 'actualValue is isRejected'}; + } + ); +} + +export default assertPromiseIsRejected; \ No newline at end of file diff --git a/code/UI/CalendarViewSwitch/oh_modules/.ohpm/oh_modules/@ohos/hypium/src/main/module/assert/assertPromiseIsRejectedWith.js b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/oh_modules/@ohos/hypium/src/main/module/assert/assertPromiseIsRejectedWith.js new file mode 100644 index 0000000000000000000000000000000000000000..8179589d5580f9c305d2200b4b197d64ac9d53ae --- /dev/null +++ b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/oh_modules/@ohos/hypium/src/main/module/assert/assertPromiseIsRejectedWith.js @@ -0,0 +1,55 @@ +/* + * 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 isPromiseLike from './isPromiseLike'; + +function assertPromiseIsRejectedWith(actualPromise, expectedValue) { + + if (!isPromiseLike(actualPromise)) { + return Promise.reject().then(function () { + }, function () { + return {pass: false, message: 'Expected not be called on a promise.'}; + }); + } + + function tips(passed) { + return ('Expected a promise ' + (passed ? 'not ' : '') + + 'to be rejected with ' + JSON.stringify(expectedValue[0])); + } + + return actualPromise.then( + function (got) { + return { + pass: false, + message: tips(false) + ' but actualValue is resolve' + }; + }, + function (actualValue) { + if (JSON.stringify(actualValue) == JSON.stringify(expectedValue[0])) { + return { + pass: true, + message: 'actualValue was rejected with ' + JSON.stringify(actualValue) + '.' + }; + } else { + return { + pass: false, + message: tips(false) + ' but it was rejected with ' + JSON.stringify(actualValue) + '.' + }; + } + } + ); +} + +export default assertPromiseIsRejectedWith; \ No newline at end of file diff --git a/code/UI/CalendarViewSwitch/oh_modules/.ohpm/oh_modules/@ohos/hypium/src/main/module/assert/assertPromiseIsRejectedWithError.js b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/oh_modules/@ohos/hypium/src/main/module/assert/assertPromiseIsRejectedWithError.js new file mode 100644 index 0000000000000000000000000000000000000000..291af8e5032b7bcd9bcb3e996a39a4fa8ba23157 --- /dev/null +++ b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/oh_modules/@ohos/hypium/src/main/module/assert/assertPromiseIsRejectedWithError.js @@ -0,0 +1,76 @@ +/* + * 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 isPromiseLike from './isPromiseLike'; + +function assertPromiseIsRejectedWithError(actualPromise, expectedValue) { + if (!isPromiseLike(actualPromise)) { + return Promise.reject().then(function () { + }, function () { + return {pass: false, message: 'Expected not be called on a promise.'}; + }); + } + return actualPromise.then( + function (got) { + return { + pass: false, + message: 'Expected a promise to be rejected but actualValue is resolve' + }; + }, + function (actualValue) { + return matchError(actualValue, expectedValue); + } + ); + +} + +function matchError(actualValue, expectedValue) { + if (expectedValue.length == 1 && typeof expectedValue[0] === 'function') { + if (expectedValue[0].name === actualValue.__proto__.name) { + return {pass: true, message: 'actual error type is ' + actualValue.name + '.'}; + } + return {pass: false, message: `except error type is ${expectedValue[0].name},but actual is ${actualValue.name}.`}; + } + + if (expectedValue.length == 1 && typeof expectedValue[0] === 'string') { + if (expectedValue[0] === actualValue.message) { + return {pass: true, message: `actual error message is ${actualValue.message}.`}; + } + return {pass: false, message: `except error message ${expectedValue[0]},but actual is ${actualValue.message}.`}; + } + + if (expectedValue.length == 1) { + return {pass: false, message: 'When only one parameter, it should be error type or error message.'}; + } + + if (expectedValue.length == 2 && typeof expectedValue[0] === 'function' && expectedValue[0].name === actualValue.name) { + if (typeof expectedValue[1] === 'string' && actualValue.message === expectedValue[1]) { + return {pass: true, message: 'actual error message is ' + actualValue.message + '.'}; + } + return {pass: false, message: `except error message is ${expectedValue[1]},but actual is ${actualValue.message}.`}; + } + + if (expectedValue.length == 2 && typeof expectedValue[0] === 'function' && expectedValue[0].name !== actualValue.name) { + if (typeof expectedValue[1] === 'string' && actualValue.message === expectedValue[1]) { + return {pass: false, message: `except error type is ${expectedValue[0].name},but actual is ${actualValue.name}.`}; + } + return {pass: false, message: 'except error type and message are incorrect.'}; + } + if (expectedValue.length > 2) { + return {pass: false, message: 'Up to two parameters are supported.'}; + } +} + +export default assertPromiseIsRejectedWithError; \ No newline at end of file diff --git a/code/UI/CalendarViewSwitch/oh_modules/.ohpm/oh_modules/@ohos/hypium/src/main/module/assert/assertPromiseIsResolved.js b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/oh_modules/@ohos/hypium/src/main/module/assert/assertPromiseIsResolved.js new file mode 100644 index 0000000000000000000000000000000000000000..86f559c32873f27b95d635452d760029de0ed657 --- /dev/null +++ b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/oh_modules/@ohos/hypium/src/main/module/assert/assertPromiseIsResolved.js @@ -0,0 +1,40 @@ +/* + * 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 isPromiseLike from './isPromiseLike'; + +function assertPromiseIsResolved(actualPromise) { + if (!isPromiseLike(actualPromise)) { + return Promise.reject().then(function () { + }, function () { + return {pass: false, message: 'Expected not be called on a promise.'}; + }); + } + + return actualPromise.then( + function (got) { + return {pass: true, message: 'actualValue is isResolved'}; + }, + function (rej) { + return { + pass: false, + message: 'Expected a promise to be resolved but it was ' + + 'rejected with ' + JSON.stringify(rej) + '.' + }; + } + ); +} + +export default assertPromiseIsResolved; \ No newline at end of file diff --git a/code/UI/CalendarViewSwitch/oh_modules/.ohpm/oh_modules/@ohos/hypium/src/main/module/assert/assertPromiseIsResolvedWith.js b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/oh_modules/@ohos/hypium/src/main/module/assert/assertPromiseIsResolvedWith.js new file mode 100644 index 0000000000000000000000000000000000000000..c6f0ef68fde5b04a589a9fa3c6e2ab2b39acf4d3 --- /dev/null +++ b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/oh_modules/@ohos/hypium/src/main/module/assert/assertPromiseIsResolvedWith.js @@ -0,0 +1,55 @@ +/* + * 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 isPromiseLike from './isPromiseLike'; + +function assertPromiseIsResolvedWith(actualPromise, expectedValue) { + if (!isPromiseLike(actualPromise)) { + return Promise.reject().then(function () { + }, function () { + return {pass: false, message: 'Expected not be called on a promise.'}; + }); + } + + function tips(passed) { + return ( + 'Expected a promise ' + (passed ? 'not ' : '') + + 'to be resolved with ' + JSON.stringify(expectedValue[0])); + } + + return actualPromise.then( + function (got) { + if (JSON.stringify(got) == JSON.stringify(expectedValue[0])) { + return { + pass: true, + message: 'actualValue was resolved with ' + JSON.stringify(got) + '.' + }; + } + return { + pass: false, + message: tips(false) + ' but it was resolved with ' + + JSON.stringify(got) + '.' + }; + }, + function (rej) { + return { + pass: false, + message: tips(false) + ' but it was rejected with ' + JSON.stringify(rej) + '.' + }; + } + ); +} + +export default assertPromiseIsResolvedWith; \ No newline at end of file diff --git a/code/UI/CalendarViewSwitch/oh_modules/.ohpm/oh_modules/@ohos/hypium/src/main/module/assert/assertThrowError.js b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/oh_modules/@ohos/hypium/src/main/module/assert/assertThrowError.js new file mode 100644 index 0000000000000000000000000000000000000000..c4544a7f825bcecd1a07d5e98dd9a7b99d237278 --- /dev/null +++ b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/oh_modules/@ohos/hypium/src/main/module/assert/assertThrowError.js @@ -0,0 +1,48 @@ +/* + * 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. + */ + +function assertThrowError(actualValue, expected) { + let result = false; + let message = ''; + let err; + if (typeof actualValue !== 'function') { + throw new Error('actualValue is not a function'); + } + try { + actualValue(); + return { + pass: result, + message: ' An error is not thrown while it is expected!' + }; + } catch (e) { + err = e; + } + if (err instanceof Error) { + let type = typeof expected[0]; + if (type === 'function') { + result = err.constructor.name === expected[0].name; + message = 'expected throw failed , ' + err.constructor.name + ' is not ' + expected[0].name; + }else if(type === 'string'){ + result = err.message.includes(expected[0]); + message = 'expected throw failed , ' + err.message + ' is not ' + expected[0]; + } + } + return { + pass: result, + message: message + }; +} + +export default assertThrowError; diff --git a/code/UI/CalendarViewSwitch/oh_modules/.ohpm/oh_modules/@ohos/hypium/src/main/module/assert/assertUndefined.js b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/oh_modules/@ohos/hypium/src/main/module/assert/assertUndefined.js new file mode 100644 index 0000000000000000000000000000000000000000..61f092d715dd1630297518b59ff13ef0940991e1 --- /dev/null +++ b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/oh_modules/@ohos/hypium/src/main/module/assert/assertUndefined.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 assertUndefined(actualValue) { + return { + pass: undefined === (actualValue), + message: 'expect Undefined, actualValue is ' + (actualValue) + }; +} + +export default assertUndefined; diff --git a/code/UI/CalendarViewSwitch/oh_modules/.ohpm/oh_modules/@ohos/hypium/src/main/module/assert/deepEquals/DeepTypeUtils.js b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/oh_modules/@ohos/hypium/src/main/module/assert/deepEquals/DeepTypeUtils.js new file mode 100644 index 0000000000000000000000000000000000000000..916824d9cb77a75d1fb35bc3500d7598bfc73e80 --- /dev/null +++ b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/oh_modules/@ohos/hypium/src/main/module/assert/deepEquals/DeepTypeUtils.js @@ -0,0 +1,139 @@ +/* + * Copyright (c) 2022-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 DeepTypeUtils { + static getType(value) { + return Object.prototype.toString.apply(value); + } + static isA(typeName, value) { + return this.getType(value) === '[object ' + typeName + ']'; + } + static isAsymmetricEqualityTester(obj) { + return obj ? this.isA('Function', obj.asymmetricMatch) : false; + } + + /** + * 是否是function + * @param value + */ + static isFunction(value) { + return this.isA('Function', value); + } + + /** + * 是否是undefined + * @param obj + */ + static isUndefined(obj) { + return obj === void 0; + } + + /** + * 是否是Node + * @param obj + */ + static isDomNode(obj) { + return obj !== null && + typeof obj === 'object' && + typeof obj.nodeType === 'number' && + typeof obj.nodeName === 'string'; + } + + /** + * 是否是promise对象 + * @param obj + */ + static isPromise(obj) { + return !!obj && obj.constructor === Promise; + }; + /** + * 是否是map对象 + * @param obj + */ + static isMap(obj) { + return ( + obj !== null && + typeof obj !== 'undefined' && + obj.constructor === Map + ); + } + + /** + * 是否是set对象 + * @param obj 对象 + */ + static isSet(obj) { + return ( + obj !== null && + typeof obj !== 'undefined' && + obj.constructor === Set + ); + } + + /** + * 对象是否有key属性 + * @param obj 对象 + * @param key 对象属性名称 + */ + static has(obj, key) { + return Object.prototype.hasOwnProperty.call(obj, key); + } + + /** + * 获取对象的自有属性 + * @param obj 对象 + * @param isArray 是否是数组,[object Array] + */ + static keys(obj, isArray) { + const extraKeys = []; + // 获取对象所有属性 + const allKeys = this.getAllKeys(obj); + if (!isArray) { + return allKeys; + } + if (allKeys.length === 0) { + return allKeys; + } + for (const k of allKeys) { + if (typeof k === 'symbol' || !/^[0-9]+$/.test(k)) { + extraKeys.push(k); + } + } + return extraKeys; + } + + /** + * 获取obj对象的所有属性 + * @param obj obj对象 + */ + static getAllKeys(obj) { + const keys = []; + for (let key in obj) { + if (this.has(obj, key)) { + keys.push(key); + } + } + const symbols = Object.getOwnPropertySymbols(obj); + for (const sym of symbols) { + // obj.propertyIsEnumerable(sym) + if (Object.prototype.propertyIsEnumerable.call(obj, sym)) { + keys.push(sym); + } + } + return keys; + } + +} +export default DeepTypeUtils; \ No newline at end of file diff --git a/code/UI/CalendarViewSwitch/oh_modules/.ohpm/oh_modules/@ohos/hypium/src/main/module/assert/deepEquals/assertDeepEquals.js b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/oh_modules/@ohos/hypium/src/main/module/assert/deepEquals/assertDeepEquals.js new file mode 100644 index 0000000000000000000000000000000000000000..60de33f7e1afdcfaf205c8c56484ef33dfda8160 --- /dev/null +++ b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/oh_modules/@ohos/hypium/src/main/module/assert/deepEquals/assertDeepEquals.js @@ -0,0 +1,341 @@ +/* + * Copyright (c) 2022-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 DeepTypeUtils from './DeepTypeUtils'; +function assertDeepEquals(actualValue, expected) { + let result = eq(actualValue, expected[0]) + let msg = logMsg(actualValue, expected[0]); + return { + pass: result, + message: msg + }; +} + +/** + * 获取失败显示日志 + * @param actualValue 实际对象 + * @param expected 期待比较对象 + */ +function logMsg(actualValue, expected) { + // 获取a的对象名称 + const aClassName = Object.prototype.toString.call(actualValue); + const bClassName = Object.prototype.toString.call(expected); + let actualMsg; + let expectMsg; + if (aClassName == '[object Function]') { + actualMsg = 'actualValue Function'; + } else if (aClassName == '[object Promise]') { + actualMsg = 'actualValue Promise'; + } else if (aClassName == '[object Set]' || aClassName == '[object Map]') { + actualMsg = JSON.stringify(Array.from(actualValue)); + } else if (aClassName == '[object RegExp]') { + actualMsg = JSON.stringify(actualValue.source.replace('\\','')); + } else if (aClassName == '[object BigInt]') { + actualMsg = actualValue; + } else if (aClassName == '[object Error]') { + actualMsg = actualValue.message; + } else if (aClassName == '[object ArrayBuffer]') { + actualMsg = actualValue.byteLength; + } + else { + actualMsg = JSON.stringify(actualValue); + } + if (bClassName == '[object Function]') { + expectMsg = 'expected Function'; + } else if (bClassName == '[object Promise]') { + expectMsg = 'expected Promise'; + } else if (bClassName == '[object Set]' || bClassName == '[object Map]') { + expectMsg = JSON.stringify(Array.from(expected)); + } else if (bClassName == '[object RegExp]') { + expectMsg = JSON.stringify(expected.source.replace('\\','')); + } else if (bClassName == '[object BigInt]') { + expectMsg = expected; + } else if (bClassName == '[object Error]') { + expectMsg = expected.message; + } else if (bClassName == '[object ArrayBuffer]') { + expectMsg = expected.byteLength; + } + else { + expectMsg = JSON.stringify(expected); + } + return actualMsg + ' is not deep equal ' + expectMsg; +} + +function eq(a, b) { + let result = true; + + if (a === b) { + result = a !== 0 || 1 / a === 1 / b; + return result; + } + + if (a === null || b === null) { + result = a === b; + return result; + } + // 获取a的对象名称 + const aClassName = Object.prototype.toString.call(a); + const bClassName = Object.prototype.toString.call(b); + // 不同类型不同对象 + if (aClassName !== bClassName) { + return false; + } + if (aClassName === '[object String]' || aClassName === '[object Number]' || aClassName === '[object Date]' || + aClassName === '[object Boolean]' || aClassName === '[object ArrayBuffer]' || + aClassName === '[object RegExp]' || aClassName === '[object Error]') { + result = isEqualSampleObj(a, b); + return result; + } + + if (typeof a !== 'object' || typeof b !== 'object') { + return false; + } + + if (DeepTypeUtils.isDomNode(a) || DeepTypeUtils.isPromise(a) || DeepTypeUtils.isFunction(a)) { + result = isEqualNodeOrPromiseOrFunction(a, b); + return result; + } + + if (aClassName === '[object Array]' || aClassName === '[object Map]' || aClassName === '[object Set]') { + result = isEqualCollection(a, b); + return result; + } + + result = isEqualObj(a, b); + return result; +} + +function isEqualNodeOrPromiseOrFunction(a, b) { + let equalNodeOrPromiseOrFunction = true; + if (DeepTypeUtils.isDomNode(a) && DeepTypeUtils.isDomNode(b)) { + const aIsDomNode = DeepTypeUtils.isDomNode(a); + const bIsDomNode = DeepTypeUtils.isDomNode(b); + if (aIsDomNode && bIsDomNode) { + // At first try to use DOM3 method isEqualNode + equalNodeOrPromiseOrFunction = a.isEqualNode(b); + return equalNodeOrPromiseOrFunction; + } + if (aIsDomNode || bIsDomNode) { + equalNodeOrPromiseOrFunction = false; + return false; + } + } + + if (DeepTypeUtils.isPromise(a) && DeepTypeUtils.isPromise(b)) { + const aIsPromise = DeepTypeUtils.isPromise(a); + const bIsPromise = DeepTypeUtils.isPromise(b); + // 俩个Promise对象 + if (aIsPromise && bIsPromise) { + equalNodeOrPromiseOrFunction = a === b; + return a === b; + } + } + if (DeepTypeUtils.isFunction(a) && DeepTypeUtils.isFunction(b)) { + // 俩个函数对象 + const aCtor = a.constructor, + bCtor = b.constructor; + if ( + aCtor !== bCtor && + DeepTypeUtils.isFunction(aCtor) && + DeepTypeUtils.isFunction(bCtor) && + a instanceof aCtor && + b instanceof bCtor && + !(aCtor instanceof aCtor && bCtor instanceof bCtor) + ) { + equalNodeOrPromiseOrFunction = false; + return false; + } + } + return equalNodeOrPromiseOrFunction; +} + +function isEqualCollection(a, b) { + let equalCollection = true; + // 获取a的对象名称 + const aClassName = Object.prototype.toString.call(a); + const bClassName = Object.prototype.toString.call(b); + // 都是数组 + if (aClassName === '[object Array]') { + equalCollection = isEqualArray(a, b); + return equalCollection; + } + + // 都是Map + if (DeepTypeUtils.isMap(a) && DeepTypeUtils.isMap(b)) { + equalCollection = isEqualMap(a, b); + return equalCollection; + } + + // 都是Set + if (DeepTypeUtils.isSet(a) && DeepTypeUtils.isSet(b)) { + equalCollection = isEqualSet(a, b); + return equalCollection; + } + + return true; +} + +function isEqualSampleObj(a, b) { + let equalSampleObj = true; + const aClassName = Object.prototype.toString.call(a); + const bClassName = Object.prototype.toString.call(b); + // 俩个string对象 + if (aClassName === '[object String]') { + equalSampleObj = a === String(b); + return equalSampleObj; + } + // 俩个Number对象 + if (aClassName === '[object Number]') { + equalSampleObj = a !== +a ? b !== +b : a === 0 && b === 0 ? 1 / a === 1 / b : a === +b; + return equalSampleObj; + } + + // 俩个Date对象/ boolean对象 + if (aClassName === '[object Date]' || aClassName === '[object Boolean]') { + equalSampleObj = +a === +b; + return equalSampleObj; + } + + // 俩个ArrayBuffer + if (aClassName === '[object ArrayBuffer]') { + equalSampleObj = eq(new Uint8Array(a), new Uint8Array(b)); + return equalSampleObj; + } + + // 正则表达式 + if (aClassName === '[object RegExp]') { + return ( + a.source === b.source && + a.global === b.global && + a.multiline === b.multiline && + a.ignoreCase === b.ignoreCase + ); + } + + if (a instanceof Error && b instanceof Error) { + equalSampleObj = a.message === b.message; + return equalSampleObj; + } + + return equalSampleObj; +} + +function isEqualObj(a, b) { + let equalObj = true; + const aClassName = Object.prototype.toString.call(a); + const bClassName = Object.prototype.toString.call(b); + const aKeys = DeepTypeUtils.keys(a, aClassName === '[object Array]'); + let size = aKeys.length; + + // 俩个对象属性长度不一致, 俩对象不相同 + if (DeepTypeUtils.keys(b, bClassName === '[object Array]').length !== size) { + return false; + } + + // 俩对象属性数量相同, 递归比较每个属性值得值 + for (const key of aKeys) { + // b 没有 key 属性 + if (!DeepTypeUtils.has(b, key)) { + equalObj = false; + continue; + } + if (!eq(a[key], b[key])) { + equalObj = false; + } + } + return equalObj; +} + +function isEqualArray(a, b) { + let equalArray = true; + const aLength = a.length; + const bLength = b.length; + if (aLength !== bLength) { + // 数组长度不同,不是同一个对象 + return false; + } + for (let i = 0; i < aLength || i < bLength; i++) { + // 递归每一个元素是否相同 + equalArray = eq(i < aLength ? a[i] : void 0, i < bLength ? b[i] : void 0) && equalArray; + } + return equalArray; +} + +function isEqualMap(a, b) { + let equalMap = true; + if (a.size !== b.size) { + return false; + } + const keysA = []; + const keysB = []; + a.forEach(function(valueA, keyA) { + keysA.push(keyA); + }); + b.forEach(function(valueB, keyB) { + keysB.push(keyB); + }); + const mapKeys = [keysA, keysB]; + const cmpKeys = [keysB, keysA]; + for (let i = 0; equalMap && i < mapKeys.length; i++) { + const mapIter = mapKeys[i]; + const cmpIter = cmpKeys[i]; + + for (let j = 0; equalMap && j < mapIter.length; j++) { + const mapKey = mapIter[j]; + const cmpKey = cmpIter[j]; + const mapValueA = a.get(mapKey); + let mapValueB; + if (eq(mapKey, cmpKey)) { + mapValueB = b.get(cmpKey); + } else { + mapValueB = b.get(mapKey); + } + equalMap = eq(mapValueA, mapValueB); + } + } + return equalMap; +} + +function isEqualSet(a, b) { + let equalSet = true; + if (a.size !== b.size) { + return false; + } + const valuesA = []; + a.forEach(function(valueA) { + valuesA.push(valueA); + }); + const valuesB = []; + b.forEach(function(valueB) { + valuesB.push(valueB); + }); + const setPairs = [[valuesA, valuesB], [valuesB, valuesA]]; + for (let i = 0; equalSet && i < setPairs.length; i++) { + const baseValues = setPairs[i][0]; + const otherValues = setPairs[i][1]; + for (const baseValue of baseValues) { + let found = false; + for (let j = 0; !found && j < otherValues.length; j++) { + const otherValue = otherValues[j]; + // 深度比较对象 + found = eq(baseValue, otherValue); + } + equalSet = equalSet && found; + } + } + return equalSet; +} + +export default assertDeepEquals; diff --git a/code/UI/CalendarViewSwitch/oh_modules/.ohpm/oh_modules/@ohos/hypium/src/main/module/assert/isPromiseLike.js b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/oh_modules/@ohos/hypium/src/main/module/assert/isPromiseLike.js new file mode 100644 index 0000000000000000000000000000000000000000..015ab19a2a0c4872d7cb490b61f8e1dd6a8ac90b --- /dev/null +++ b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/oh_modules/@ohos/hypium/src/main/module/assert/isPromiseLike.js @@ -0,0 +1,32 @@ +/* + * 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 isPromiseLike(obj) { + return !!obj && isFunction_(obj.then); +} + +function isFunction_(value) { + return isA_('Function', value); +} + +function isA_(typeName, value) { + return getType_(value) === '[object ' + typeName + ']'; +} + +function getType_(value) { + return Object.prototype.toString.apply(value); +} + +export default isPromiseLike; diff --git a/code/UI/CalendarViewSwitch/oh_modules/.ohpm/oh_modules/@ohos/hypium/src/main/module/config/DataDriver.js b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/oh_modules/@ohos/hypium/src/main/module/config/DataDriver.js new file mode 100644 index 0000000000000000000000000000000000000000..639dffc9cdb912f1f33a6ccb61868c9ed7c695bf --- /dev/null +++ b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/oh_modules/@ohos/hypium/src/main/module/config/DataDriver.js @@ -0,0 +1,116 @@ +/* + * 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. + */ + +const SUITES_KEY = 'suites'; +const SPECS_KEY = 'items'; +const DESCRIBE_KEY = 'describe'; +const IT_KEY = 'it'; +const PARAMS_KEY = 'params'; +const STRESS_KEY = 'stress'; + +class ObjectUtils { + static get(object, name, defaultValue) { + let result = defaultValue; + for (const key in object) { + if (key === name) { + return object[key]; + } + } + return result; + } + + static has(object, key) { + return Object.prototype.hasOwnProperty.call(object, key); + } +} + +class DataDriver { + constructor(attr) { + this.id = 'dataDriver'; + this.data = attr.data || {}; + } + + init(coreContext) { + this.coreContext = coreContext; + this.suiteService = this.coreContext.getDefaultService('suite'); + this.specService = this.coreContext.getDefaultService('spec'); + } + + getSpecParams() { + let specParams = []; + let suiteDesc = this.suiteService.getCurrentRunningSuite().description; + let specDesc = this.specService.getCurrentRunningSpec().description; + let suites = ObjectUtils.get(this.data, SUITES_KEY, []); + for (const suiteItem of suites) { + let describeValue = ObjectUtils.get(suiteItem, DESCRIBE_KEY, ''); + if (ObjectUtils.has(suiteItem, DESCRIBE_KEY) && (typeof describeValue === 'object') && describeValue.constructor === Array && describeValue.includes(suiteDesc)) { + let specs = ObjectUtils.get(suiteItem, SPECS_KEY, []); + for (const specItem of specs) { + if (ObjectUtils.has(specItem, IT_KEY) && ObjectUtils.get(specItem, IT_KEY) === specDesc) { + return ObjectUtils.get(specItem, PARAMS_KEY, specParams); + } + } + } + } + return specParams; + } + + getSuiteParams() { + let suiteParams = {}; + let suiteDesc = this.suiteService.getCurrentRunningSuite().description; + let suites = ObjectUtils.get(this.data, SUITES_KEY, []); + for (const suiteItem of suites) { + let describeValue = ObjectUtils.get(suiteItem, DESCRIBE_KEY, []); + if (ObjectUtils.has(suiteItem, DESCRIBE_KEY) && (typeof describeValue === 'object') && describeValue.constructor === Array && describeValue.includes(suiteDesc)) { + suiteParams = Object.assign({}, suiteParams, ObjectUtils.get(suiteItem, PARAMS_KEY, suiteParams)); + } + } + return suiteParams; + } + + getSpecStress(specDesc) { + let stress = 1; + let suiteDesc = this.suiteService.getCurrentRunningSuite().description; + let suites = ObjectUtils.get(this.data, SUITES_KEY, []); + for (const suiteItem of suites) { + let describeValue = ObjectUtils.get(suiteItem, DESCRIBE_KEY, ''); + if (ObjectUtils.has(suiteItem, DESCRIBE_KEY) && (typeof describeValue === 'object') && describeValue.constructor === Array && describeValue.includes(suiteDesc)) { + let specs = ObjectUtils.get(suiteItem, SPECS_KEY, []); + for (const specItem of specs) { + if (ObjectUtils.has(specItem, IT_KEY) && ObjectUtils.get(specItem, IT_KEY) === specDesc) { + let tempStress = ObjectUtils.get(specItem, STRESS_KEY, stress); + return (Number.isInteger(tempStress) && tempStress >= 1) ? tempStress : stress; + } + } + } + } + return stress; + } + + getSuiteStress(suiteDesc) { + let stress = 1; + let suites = ObjectUtils.get(this.data, SUITES_KEY, []); + for (const suiteItem of suites) { + let describeValue = ObjectUtils.get(suiteItem, DESCRIBE_KEY, []); + if (ObjectUtils.has(suiteItem, DESCRIBE_KEY) && (typeof describeValue === 'object') && describeValue.constructor === Array && describeValue.includes(suiteDesc)) { + let tempStress = ObjectUtils.get(suiteItem, STRESS_KEY, stress); + return (Number.isInteger(tempStress) && tempStress >= 1) ? tempStress : stress; + } + } + return stress; + } +} + +export default DataDriver; diff --git a/code/UI/CalendarViewSwitch/oh_modules/.ohpm/oh_modules/@ohos/hypium/src/main/module/config/Filter.js b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/oh_modules/@ohos/hypium/src/main/module/config/Filter.js new file mode 100644 index 0000000000000000000000000000000000000000..b07e6c681bfff618cc9f5ca92ec85d1d9880202d --- /dev/null +++ b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/oh_modules/@ohos/hypium/src/main/module/config/Filter.js @@ -0,0 +1,143 @@ +/* + * 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 { LEVEL, SIZE, TESTTYPE } from '../../Constant'; + +class ClassFilter { + constructor(suiteName, itName, params) { + this.suiteName = suiteName; + this.itName = itName; + this.params = params; + } + + filterSuite() { + return !this.params.split(',').map(item => item.split('#')[0]).map(item => item == this.suiteName).reduce((pre, cur) => pre || cur, false); + } + + filterIt() { + let classArray = this.params.split(',') || []; + let suiteFilterResult = classArray.filter(item => !item.includes('#')).map(item => item == this.suiteName).reduce((pre, cur) => pre || cur, false); + let itFilterResult = classArray.filter(item => item.includes('#')).map(item => item == (this.suiteName + '#' + this.itName)).reduce((pre, cur) => pre || cur, false); + return !(suiteFilterResult || itFilterResult); + } +} + +class NotClassFilter { + constructor(suiteName, itName, params) { + this.suiteName = suiteName; + this.itName = itName; + this.params = params; + } + + filterSuite() { + return this.params.split(',').map(item => item == this.suiteName).reduce((pre, cur) => pre || cur, false); + } + + filterIt() { + return this.params.split(',').some(item => item == (this.suiteName + '#' + this.itName)); + } +} + +class SuiteAndItNameFilter { + constructor(suiteName, itName, params) { + this.suiteName = suiteName; + this.itName = itName; + this.params = params; + } + + filterSuite() { + return !this.params.split(',').map(item => item == this.suiteName).reduce((pre, cur) => pre || cur, false); + } + + filterIt() { + return !this.params.split(',').map(item => item == this.itName).reduce((pre, cur) => pre || cur, false); + } +} + + +class TestTypesFilter { + constructor(suiteName, itName, fi, params) { + this.suiteName = suiteName; + this.itName = itName; + this.params = params; + this.fi = fi; + } + + filterIt() { + return !((this.params === (this.fi & this.params)) || this.fi === 0); + } +} + +class NestFilter { + filterNestName(targetSuiteArray, targetSpecArray, suiteStack, desc) { + let targetSuiteName = ''; + for (let key in suiteStack) { + targetSuiteName = targetSuiteName + '.' + suiteStack[key].description; + } + targetSuiteName = targetSuiteName.substring(2); + const targetSpecName = targetSuiteName + '#' + desc; + let isFilter = true; + if (targetSpecArray.includes(targetSpecName)) { + return false; + } + for (let index in targetSuiteArray) { + if (targetSuiteName.startsWith(targetSuiteArray[index])) { + return false; + } + } + return isFilter; + } + + filterNotClass(notClass, suiteStack, desc) { + let isFilterNotClass = false; + if (notClass != null) { + let notClassArray = notClass.split(','); + let targetSuiteName = ''; + for (let key in suiteStack) { + targetSuiteName = targetSuiteName + '.' + suiteStack[key].description; + } + targetSuiteName = targetSuiteName.substring(2); + const targetSpecName = targetSuiteName + '#' + desc; + if (notClassArray.includes(targetSpecName) || notClassArray.some(key => targetSpecName.startsWith(key))) { + isFilterNotClass = true; + } + } + return isFilterNotClass; + } + + filterLevelOrSizeOrTestType(level, size, testType, filter) { + let result = false; + if (filter === 0 || filter === '0') { + return result; + } + if (level == null && size == null && testType == null) { + return result; + } + if (level != null) { + let levelFilter = LEVEL[`${level}`]; + result = result || filter === levelFilter; + } + if (size != null) { + let sizeFilter = SIZE[`${size}`]; + result = result || filter === sizeFilter; + } + if (testType != null) { + let testTypeFilter = TESTTYPE[`${testType}`]; + result = result || filter === testTypeFilter; + } + return !result; + } +} +export { ClassFilter, NotClassFilter, SuiteAndItNameFilter, TestTypesFilter, NestFilter }; diff --git a/code/UI/CalendarViewSwitch/oh_modules/.ohpm/oh_modules/@ohos/hypium/src/main/module/config/configService.js b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/oh_modules/@ohos/hypium/src/main/module/config/configService.js new file mode 100644 index 0000000000000000000000000000000000000000..17674d8cf7e2343bcb4a14ad47eb11cd03c15aac --- /dev/null +++ b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/oh_modules/@ohos/hypium/src/main/module/config/configService.js @@ -0,0 +1,299 @@ +/* + * 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 { ClassFilter, NotClassFilter, SuiteAndItNameFilter, TestTypesFilter, NestFilter } from './Filter'; +import { TAG, TESTTYPE, LEVEL, SIZE, KEYSET } from '../../Constant'; +const STRESS_RULE = /^[1-9]\d*$/; + +class ConfigService { + constructor(attr) { + this.id = attr.id; + this.supportAsync = true; // 默认异步处理测试用例 + this.random = false; + this.filterValid = []; + this.filter = 0; + this.flag = false; + this.suite = null; + this.itName = null; + this.testType = null; + this.level = null; + this.size = null; + this.class = null; + this.notClass = null; + this.timeout = null; + // 遇错即停模式配置 + this.breakOnError = false; + // 压力测试配置 + this.stress = null; + this.skipMessage = false; + this.runSkipped = ''; + this.filterXdescribe = []; + } + + init(coreContext) { + this.coreContext = coreContext; + } + + isNormalInteger(str) { + const n = Math.floor(Number(str)); + return n !== Infinity && String(n) === String(str) && n >= 0; + } + + + getStress() { + if (this.stress === undefined || this.stress === '' || this.stress === null) { + return 1; + } + return !this.stress.match(STRESS_RULE) ? 1 : Number.parseInt(this.stress); + } + + basicParamValidCheck(params) { + let size = params.size; + if (size !== undefined && size !== '' && size !== null) { + let sizeArray = ['small', 'medium', 'large']; + if (sizeArray.indexOf(size) === -1) { + this.filterValid.push('size:' + size); + } + } + let level = params.level; + if (level !== undefined && level !== '' && level !== null) { + let levelArray = ['0', '1', '2', '3', '4']; + if (levelArray.indexOf(level) === -1) { + this.filterValid.push('level:' + level); + } + } + let testType = params.testType; + if (testType !== undefined && testType !== '' && testType !== null) { + let testTypeArray = ['function', 'performance', 'power', 'reliability', 'security', + 'global', 'compatibility', 'user', 'standard', 'safety', 'resilience']; + if (testTypeArray.indexOf(testType) === -1) { + this.filterValid.push('testType:' + testType); + } + } + } + + filterParamValidCheck(params) { + let timeout = params.timeout; + if (timeout !== undefined && timeout !== '' && timeout !== null) { + if (!this.isNormalInteger(timeout)) { + this.filterValid.push('timeout:' + timeout); + } + } + + let paramKeys = ['dryRun', 'random', 'breakOnError', 'coverage', 'skipMessage']; + for (const key of paramKeys) { + if (params[key] !== undefined && params[key] !== 'true' && params[key] !== 'false') { + this.filterValid.push(`${key}:${params[key]}`); + } + } + + // 压力测试参数验证,正整数 + if (params.stress !== undefined && params.stress !== '' && params.stress !== null) { + if (!params.stress.match(STRESS_RULE)) { + this.filterValid.push('stress:' + params.stress); + } + } + + let nameRule = /^[A-Za-z]{1}[\w#,.]*$/; + let paramClassKeys = ['class', 'notClass']; + for (const key of paramClassKeys) { + if (params[key] !== undefined && params[key] !== '' && params[key] !== null) { + let classArray = params[key].split(','); + classArray.forEach(item => !item.match(nameRule) ? this.filterValid.push(`${key}:${params[key]}`) : null); + } + } + } + + setConfig(params) { + this.basicParamValidCheck(params); + this.filterParamValidCheck(params); + try { + this.class = params.class; + this.notClass = params.notClass; + this.flag = params.flag || { flag: false }; + this.suite = params.suite; + this.itName = params.itName; + this.filter = params.filter; + this.testType = params.testType; + this.level = params.level; + this.size = params.size; + this.timeout = params.timeout; + this.dryRun = params.dryRun; + this.breakOnError = params.breakOnError; + this.random = params.random === 'true' ? true : false; + this.stress = params.stress; + this.coverage = params.coverage; + this.skipMessage = params.skipMessage; + this.runSkipped = params.runSkipped; + this.filterParam = { + testType: TESTTYPE, + level: LEVEL, + size: SIZE + }; + this.parseParams(); + } catch (err) { + console.info(`${TAG}setConfig error: ${err.message}`); + } + } + + parseParams() { + if (this.filter != null) { + return; + } + let testTypeFilter = 0; + let sizeFilter = 0; + let levelFilter = 0; + if (this.testType != null) { + testTypeFilter = this.testType.split(',') + .map(item => this.filterParam.testType[item] || 0) + .reduce((pre, cur) => pre | cur, 0); + } + if (this.level != null) { + levelFilter = this.level.split(',') + .map(item => this.filterParam.level[item] || 0) + .reduce((pre, cur) => pre | cur, 0); + } + if (this.size != null) { + sizeFilter = this.size.split(',') + .map(item => this.filterParam.size[item] || 0) + .reduce((pre, cur) => pre | cur, 0); + } + this.filter = testTypeFilter | sizeFilter | levelFilter; + console.info(`${TAG}filter params:${this.filter}`); + } + + isCurrentSuite(description) { + if (this.suite !== undefined && this.suite !== '' && this.suite !== null) { + let suiteArray = this.suite.split(','); + return suiteArray.indexOf(description) !== -1; + } + return false; + } + + filterSuite(currentSuiteName) { + let filterArray = []; + if (this.suite !== undefined && this.suite !== '' && this.suite !== null) { + filterArray.push(new SuiteAndItNameFilter(currentSuiteName, '', this.suite)); + } + if (this.class !== undefined && this.class !== '' && this.class !== null) { + filterArray.push(new ClassFilter(currentSuiteName, '', this.class)); + } + if (this.notClass !== undefined && this.notClass !== '' && this.notClass !== null) { + filterArray.push(new NotClassFilter(currentSuiteName, '', this.notClass)); + } + + let result = filterArray.map(item => item.filterSuite()).reduce((pre, cur) => pre || cur, false); + return result; + } + + filterDesc(currentSuiteName, desc, fi, coreContext) { + let filterArray = []; + if (this.itName !== undefined && this.itName !== '' && this.itName !== null) { + filterArray.push(new SuiteAndItNameFilter(currentSuiteName, desc, this.itName)); + } + if (this.class !== undefined && this.class !== '' && this.class !== null) { + filterArray.push(new ClassFilter(currentSuiteName, desc, this.class)); + } + if (this.notClass !== undefined && this.notClass !== '' && this.notClass !== null) { + filterArray.push(new NotClassFilter(currentSuiteName, desc, this.notClass)); + } + if (typeof (this.filter) !== 'undefined' && this.filter !== 0 && fi !== 0) { + filterArray.push(new TestTypesFilter('', '', fi, this.filter)); + } + let result = filterArray.map(item => item.filterIt()).reduce((pre, cur) => pre || cur, false); + return result; + } + + filterWithNest(desc, filter) { + let filterArray = []; + const nestFilter = new NestFilter(); + const targetSuiteArray = this.coreContext.getDefaultService('suite').targetSuiteArray; + const targetSpecArray = this.coreContext.getDefaultService('suite').targetSpecArray; + const suiteStack = this.coreContext.getDefaultService('suite').suitesStack; + let isFilter = nestFilter.filterNestName(targetSuiteArray, targetSpecArray, suiteStack, desc); + const isFullRun = this.coreContext.getDefaultService('suite').fullRun; + if (typeof (this.filter) !== 'undefined' && this.filter !== 0 && filter !== 0) { + filterArray.push(new TestTypesFilter('', '', filter, this.filter)); + return filterArray.map(item => item.filterIt()).reduce((pre, cur) => pre || cur, false); + } + if (isFilter && !isFullRun) { + return true; + } + return nestFilter.filterNotClass(this.notClass, suiteStack, desc); + + } + + isRandom() { + return this.random || false; + } + + isBreakOnError() { + return this.breakOnError !== 'true' ? false : true; + } + + setSupportAsync(value) { + this.supportAsync = value; + } + + isSupportAsync() { + return this.supportAsync; + } + + translateParams(parameters) { + const keySet = new Set(KEYSET); + let targetParams = {}; + for (const key in parameters) { + if (keySet.has(key)) { + var newKey = key.replace('-s ', ''); + targetParams[newKey] = parameters[key]; + } + } + return targetParams; + } + translateParamsToString(parameters) { + const keySet = new Set(KEYSET); + let targetParams = ''; + for (const key in parameters) { + if (keySet.has(key)) { + targetParams += ' ' + key + ' ' + parameters[key]; + } + } + return targetParams.trim(); + } + + execute() { + } + + checkIfSuiteInSkipRun(desc) { + return this.runSkipped.split(',').some(item => { + return item === desc || item.startsWith(desc + '.') || item.startsWith(desc + '#') || desc.startsWith(item + '.') || this.runSkipped === 'skipped'; + }); + } + + checkIfSpecInSkipRun(desc) { + return this.runSkipped.split(',').some(item => { + if (item.includes('#')) { + return item === desc; + } else { + return desc.startsWith(item + '.') || desc.startsWith(item + '#') || this.runSkipped === 'skipped'; + } + } + ); + } +} + +export { + ConfigService +}; diff --git a/code/UI/CalendarViewSwitch/oh_modules/.ohpm/oh_modules/@ohos/hypium/src/main/module/coverage/coverageCollect.js b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/oh_modules/@ohos/hypium/src/main/module/coverage/coverageCollect.js new file mode 100644 index 0000000000000000000000000000000000000000..334a33db9561dd2070c4081457632decf2589b83 --- /dev/null +++ b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/oh_modules/@ohos/hypium/src/main/module/coverage/coverageCollect.js @@ -0,0 +1,76 @@ +/* + * 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 SysTestKit from '../kit/SysTestKit'; +import fs from '@ohos.file.fs'; +import {TAG} from '../../Constant'; + +const jsCoverageFileName = 'js_coverage.json'; + +export async function collectCoverageData() { + if (globalThis.__coverage__ === undefined) { + console.info(`${TAG} globalThis not have coverage`); + return; + } + const strJson = JSON.stringify(globalThis.__coverage__); + let testMode = globalThis.__testMode__; + console.info(`${TAG} coverage data testMode: ${testMode}`); + let savePath = globalThis.__savePath__; + console.info(`${TAG} write coverage data to: ${savePath}`); + let readPath = globalThis.__readPath__; + console.info(`${TAG} read coverage data in: ${readPath}`); + + // run callback mode if local test or (save path and read path ) is not defined + if (!testMode || !isCoveragePathValid(savePath)) { + console.info(`${TAG} run coverage data in call back mode`); + const strLen = strJson.length; + const maxLen = 500; + const maxCount = Math.floor(strLen / maxLen); + const OHOS_REPORT_COVERAGE_DATA = 'OHOS_REPORT_COVERAGE_DATA:'; + for (let count = 0; count <= maxCount; count++) { + console.info(`${OHOS_REPORT_COVERAGE_DATA} ${strJson.substring(count * maxLen, (count + 1) * maxLen)}`); + await SysTestKit.print(`${OHOS_REPORT_COVERAGE_DATA} ${strJson.substring(count * maxLen, (count + 1) * maxLen)}`); + } + return; + } + console.info(`${TAG} run coverage data in save file mode`); + if (fs.accessSync(savePath)) { + fs.unlinkSync(savePath); + } + + let inputPathDir = savePath.substring(0, savePath.length - jsCoverageFileName.length); + if (!fs.accessSync(inputPathDir)) { + console.info(`${TAG} coverage data create dir: ${inputPathDir}`); + fs.mkdirSync(inputPathDir); + } + + let file = fs.openSync(savePath, fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE); + let writeLen = fs.writeSync(file.fd, strJson, {encoding:'utf-8'}); + console.info(`${TAG} write coverage data success: ${writeLen}`); + fs.closeSync(file); + const OHOS_REPORT_COVERAGE_PATH = 'OHOS_REPORT_COVERAGE_PATH:'; + await SysTestKit.print(`${OHOS_REPORT_COVERAGE_PATH} ${readPath}`); + console.info(`${OHOS_REPORT_COVERAGE_PATH} ${readPath}`); +} + +function isCoveragePathValid(inputPath) { + if (!inputPath) { + return false; + } + if (inputPath.indexOf(jsCoverageFileName) === -1) { + return false; + } + return true; +} \ No newline at end of file diff --git a/code/UI/CalendarViewSwitch/oh_modules/.ohpm/oh_modules/@ohos/hypium/src/main/module/kit/SysTestKit.js b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/oh_modules/@ohos/hypium/src/main/module/kit/SysTestKit.js new file mode 100644 index 0000000000000000000000000000000000000000..6e2f256514cff87450f910098b1130943a40e39c --- /dev/null +++ b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/oh_modules/@ohos/hypium/src/main/module/kit/SysTestKit.js @@ -0,0 +1,102 @@ +/* + * 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 {TAG} from '../../Constant'; +import Core from '../../core.js'; + +export default class SysTestKit { + + static delegator = null; + static systemTime = null; + static workerPort = null; + + constructor() { + this.id = 'sysTestKit'; + this.index = 0; + } + + static getDescribeName() { + return Core.getInstance().getDefaultService('suite').getCurrentRunningSuite().description; + } + + static getItName() { + return Core.getInstance().getDefaultService('spec').getCurrentRunningSpec().description; + } + + static getItAttribute() { + return Core.getInstance().getDefaultService('spec').getCurrentRunningSpec().fi; + } + + static actionStart(tag) { + console.info(`${TAG}${JSON.stringify(tag)}`); + var message = '\n' + 'OHOS_REPORT_ACTIONSTART: ' + JSON.stringify(tag) + '\n'; + SysTestKit.print(message); + console.info(`${TAG}${JSON.stringify(tag)} actionStart print success`); + } + + static actionEnd(tag) { + console.info(`${TAG}${JSON.stringify(tag)}`); + var message = '\n' + 'OHOS_REPORT_ACTIONEND: ' + JSON.stringify(tag) + '\n'; + SysTestKit.print(message); + console.info(`${TAG}${JSON.stringify(tag)} actionEnd print success`); + } + + static async existKeyword(keyword, timeout) { + let reg = new RegExp(/^[a-zA-Z0-9]{1,}$/); + if (!reg.test(keyword)) { + throw new Error('keyword must contain more than one string, and only letters and numbers are supported.'); + } + timeout = timeout || 4; + + let searchResult = false; + let cmd = 'hilog -x | grep -i \'' + keyword + '\' | wc -l'; + await executePromise(cmd, timeout).then((data) => { + searchResult = data; + }); + return searchResult; + } + static async print(message) { + if ('printSync' in SysTestKit.delegator) { + console.info(`${TAG}printSync called ...`); + SysTestKit.delegator.printSync(message); + } else { + await SysTestKit.delegator.print(message); + } + } + + static async getRealTime() { + let currentTime = new Date().getTime(); + if (SysTestKit.systemTime !== null && SysTestKit.systemTime !== undefined) { + await SysTestKit.systemTime.getRealTime().then((time) => { + console.info(`${TAG}systemTime.getRealTime success data: ${JSON.stringify(time)}`); + currentTime = time; + }).catch((error) => { + console.error(`${TAG}failed to systemTime.getRealTime because ${JSON.stringify(error)}`); + }); + } + return currentTime; + } +} + +function executePromise(cmd, timeout) { + return new Promise((resolve, reject) => { + SysTestKit.delegator.executeShellCommand(cmd, timeout, + (error, data) => { + console.info(`${TAG}existKeyword CallBack: err : ${JSON.stringify(error)}`); + console.info(`${TAG}existKeyword CallBack: data : ${JSON.stringify(data)}`); + resolve(parseInt(data.stdResult) > 3 ? true : false); + }); + }); +} \ No newline at end of file diff --git a/code/UI/CalendarViewSwitch/oh_modules/.ohpm/oh_modules/@ohos/hypium/src/main/module/mock/ArgumentMatchers.js b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/oh_modules/@ohos/hypium/src/main/module/mock/ArgumentMatchers.js new file mode 100644 index 0000000000000000000000000000000000000000..1e69ac401049589986968a8575ca45a02a299327 --- /dev/null +++ b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/oh_modules/@ohos/hypium/src/main/module/mock/ArgumentMatchers.js @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2022-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 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() { + let regex = arguments[0]; + if (ArgumentMatchers.isRegExp(regex)) { + return regex; + } + throw Error('not a regex'); + } + + static isRegExp(value) { + return Object.prototype.toString.call(value) === '[object RegExp]'; + } + + matcheReturnKey() { + let arg = arguments[0]; + let regex = arguments[1]; + let stubSetKey = arguments[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() { + let key = arguments[0]; + + 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; + } +} + +export default ArgumentMatchers; \ No newline at end of file diff --git a/code/UI/CalendarViewSwitch/oh_modules/.ohpm/oh_modules/@ohos/hypium/src/main/module/mock/ExtendInterface.js b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/oh_modules/@ohos/hypium/src/main/module/mock/ExtendInterface.js new file mode 100644 index 0000000000000000000000000000000000000000..c6a866a6df662ad10a7f6869dcbc2381fa47bcdc --- /dev/null +++ b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/oh_modules/@ohos/hypium/src/main/module/mock/ExtendInterface.js @@ -0,0 +1,57 @@ +/* + * 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() { + this.mocker.clear(); + } +} + +export default ExtendInterface; \ No newline at end of file diff --git a/code/UI/CalendarViewSwitch/oh_modules/.ohpm/oh_modules/@ohos/hypium/src/main/module/mock/MockKit.js b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/oh_modules/@ohos/hypium/src/main/module/mock/MockKit.js new file mode 100644 index 0000000000000000000000000000000000000000..5895666bc89ed4270582b436c82045745d5249b4 --- /dev/null +++ b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/oh_modules/@ohos/hypium/src/main/module/mock/MockKit.js @@ -0,0 +1,266 @@ +/* + * Copyright (c) 2022-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 ExtendInterface from './ExtendInterface'; +import VerificationMode from './VerificationMode'; +import ArgumentMatchers from './ArgumentMatchers'; + +class MockKit { + + 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 = {}; + this.recordCalls = {}; + this.currentSetKey = new Map(); + this.mockObj = null; + this.recordMockedMethod = new Map(); + } + + clearAll() { + this.reset(); + var props = Object.keys(this); + for (var i = 0; i < props.length; i++) { + delete this[props[i]]; + } + + var props = Object.getOwnPropertyNames(this); + for (var i = 0; i < props.length; i++) { + delete this[props[i]]; + } + for (var key in this) { + delete this[key]; + } + } + + 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 = null; + 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(newObj) { + if (newObj.__proto__ === null) { + return []; + } + let properties = Object.getOwnPropertyNames(newObj); + return [...properties, ...getProperty(newObj.__proto__)]; + } + return getProperty(obj); + } + + recordMethodCall(originalMethod, args) { + Function.prototype.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; + 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); + } +} + +export {MockKit, when}; \ No newline at end of file diff --git a/code/UI/CalendarViewSwitch/oh_modules/.ohpm/oh_modules/@ohos/hypium/src/main/module/mock/VerificationMode.js b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/oh_modules/@ohos/hypium/src/main/module/mock/VerificationMode.js new file mode 100644 index 0000000000000000000000000000000000000000..aaf2fdfae00135d3d2055320fc5ea403b44d0bf3 --- /dev/null +++ b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/oh_modules/@ohos/hypium/src/main/module/mock/VerificationMode.js @@ -0,0 +1,49 @@ +/* + * 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 {expect} from '../../interface'; + +class VerificationMode { + constructor(times) { + this.doTimes = times; + } + + times(count) { + expect(count).assertEqual(this.doTimes); + } + + never() { + console.info(this.doTimes); + expect(0).assertEqual(this.doTimes); + } + + once() { + expect(1).assertEqual(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; \ No newline at end of file diff --git a/code/UI/CalendarViewSwitch/oh_modules/.ohpm/oh_modules/@ohos/hypium/src/main/module/report/LogExpectError.js b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/oh_modules/@ohos/hypium/src/main/module/report/LogExpectError.js new file mode 100644 index 0000000000000000000000000000000000000000..5a94cecb4625205797ae886c19ac592f189c2232 --- /dev/null +++ b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/oh_modules/@ohos/hypium/src/main/module/report/LogExpectError.js @@ -0,0 +1,65 @@ +/* + * 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. + */ + +class LogExpectError { + static getErrorMsg(matcherName, actualValue, expect, originMsg) { + if (matcherName === 'assertNull') { + return 'expect not null, actualValue is ' + (actualValue); + } + if (matcherName === 'assertTrue') { + return 'expect not true, actualValue is ' + (actualValue); + } + if (matcherName === 'assertFalse') { + return 'expect not false, actualValue is ' + (actualValue); + } + if (matcherName === 'assertEqual') { + return 'expect not Equal, actualValue is ' + actualValue + ' equals ' + expect; + } + if (matcherName === 'assertContain') { + return 'expect not have, ' + actualValue + ' have ' + expect; + } + if (matcherName === 'assertInstanceOf') { + return 'expect not InstanceOf, ' + actualValue + ' is ' + + Object.prototype.toString.call(actualValue) + expect; + } + if (matcherName === 'assertLarger') { + return 'expect not Larger, ' + + (actualValue) + ' is larger than ' + expect; + } + if (matcherName === 'assertLargerOrEqual') { + return 'expect not LargerOrEqual, ' + (actualValue) + ' larger than ' + expect; + } + if (matcherName === 'assertLess') { + return 'expect not Less, ' + (actualValue) + ' less than ' + expect; + } + if (matcherName === 'assertLessOrEqual') { + return 'expect not LessOrEqual, ' + (actualValue) + ' is less than ' + expect; + } + if (matcherName === 'assertNaN') { + return 'expect not NaN, actualValue is ' + (actualValue); + } + if (matcherName === 'assertNegUnlimited') { + return 'expect not NegUnlimited, actualValue is ' + (actualValue); + } + if (matcherName === 'assertPosUnlimited') { + return 'expect not PosUnlimited, actualValue is ' + (actualValue); + } + if (matcherName === 'assertUndefined') { + return 'expect not Undefined, actualValue is ' + (actualValue); + } + return originMsg; + } +} +export default LogExpectError; \ No newline at end of file diff --git a/code/UI/CalendarViewSwitch/oh_modules/.ohpm/oh_modules/@ohos/hypium/src/main/module/report/OhReport.js b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/oh_modules/@ohos/hypium/src/main/module/report/OhReport.js new file mode 100644 index 0000000000000000000000000000000000000000..653e56be9e88e810f6ab1f3d58049cf08d2ac0b6 --- /dev/null +++ b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/oh_modules/@ohos/hypium/src/main/module/report/OhReport.js @@ -0,0 +1,213 @@ +/* + * 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 SysTestKit from "../kit/SysTestKit"; +import { collectCoverageData } from '../coverage/coverageCollect'; +import { TAG, PrintTag } from '../../Constant'; + +class OhReport { + constructor(attr) { + this.delegator = attr.delegator; + this.abilityDelegatorArguments = attr.abilityDelegatorArguments; + this.id = 'report'; + this.index = 0; + this.duration = 0; + this.currentThreadName = 'mainThread'; + } + + init(coreContext) { + this.coreContext = coreContext; + this.suiteService = this.coreContext.getDefaultService('suite'); + this.specService = this.coreContext.getDefaultService('spec'); + if (SysTestKit.workerPort !== null) { + this.currentThreadName = SysTestKit.workerPort.name; + } + } + + taskStart() { + } + + async taskDone() { + let summary = this.suiteService.getSummary(); + if (this.abilityDelegatorArguments !== null) { + this.taskDoneTime = new Date().getTime(); + const configService = this.coreContext.getDefaultService('config'); + const suiteService = this.coreContext.getDefaultService('suite'); + const specService = this.coreContext.getDefaultService('spec'); + if (configService['coverage'] === 'true') { + await collectCoverageData(); + } + let message = '\n' + `${PrintTag.OHOS_REPORT_RESULT}: stream=Tests run: ` + summary.total + ', Failure: ' + summary.failure; + message += ', Error: ' + summary.error; + message += ', Pass: ' + summary.pass; + message += ', Ignore: ' + summary.ignore; + if (specService.skipSpecNum > 0) { + message += ', SkipSpec: ' + specService.skipSpecNum; + } + message += '\n' + `${PrintTag.OHOS_REPORT_CODE}: ` + (summary.failure > 0 ? -1 : 0) + '\n'; + let isHasError = summary.failure > 0 || summary.error > 0; + let config = this.coreContext.getDefaultService('config'); + if (config.isBreakOnError() && isHasError) { + // 未执行全部说明 + message += '\n' + `${PrintTag.OHOS_REPORT_RESULT}: breakOnError model, Stopping whole test suite if one specific test case failed or error` + '\n'; + } + message += `${PrintTag.OHOS_REPORT_STATUS}: taskconsuming=` + summary.duration + '\n'; + console.info(`${message}`); + await SysTestKit.print(message); + } + if (SysTestKit.workerPort === null || SysTestKit.workerPort === undefined) { + // 主线程执行完成 结束任务。 + console.info(`${TAG}report print success`); + this.delegator.finishTest('your test finished!!!', 0, () => { }); + } else { + // worker线程执行完成将数据发送到主线程中。 + let sendData = { + currentThreadName: this.currentThreadName, + summary: summary + }; + console.info(`${TAG}, send data to mainThread, ${this.currentThreadName}, ${JSON.stringify(sendData)}`); + SysTestKit.workerPort.postMessage(sendData); + } + } + + incorrectFormat() { + if (this.coreContext.getDefaultService('config').filterValid.length !== 0) { + var value = this.coreContext.getDefaultService('config').filterValid; + var message = 'this param ' + value.join(',') + ' is invalid' + '\n'; + this.delegator.finishTest(message, 0, () => { + }); + } + } + + incorrectTestSuiteFormat() { + if (this.coreContext.getDefaultService('config').filterXdescribe.length !== 0) { + let value = this.coreContext.getDefaultService('config').filterXdescribe; + let message = 'xdescribe ' + value.join(',') + ' should not contain it' + '\n'; + this.delegator.finishTest(message, 0, () => { + }); + } + } + async suiteStart() { + if (this.abilityDelegatorArguments !== null) { + let specArr = []; + this.suiteService.getAllChildSuiteNum(this.suiteService.getCurrentRunningSuite(), specArr); + let message = '\n' + `${PrintTag.OHOS_REPORT_SUM}: ` + specArr.length; + this.suiteService.setCurrentRunningSuiteDesc(this.suiteService.getRootSuite(), this.suiteService.getCurrentRunningSuite(), ''); + message += '\n' + `${PrintTag.OHOS_REPORT_STATUS}: class=` + this.suiteService.getCurrentRunningSuiteDesc() + '\n'; + if (this.suiteService.currentRunningSuite.isSkip) { + message += `${PrintTag.OHOS_REPORT_STATUS}: skipReason=` + this.suiteService.currentRunningSuite.skipReason + '\n'; + } + if (SysTestKit.workerPort !== null) { + message += `${PrintTag.OHOS_REPORT_STATUS}: currentWorkerName=` + this.currentThreadName; + } + console.info(`${message}`); + await SysTestKit.print(message); + console.info(`${TAG}${this.suiteService.getCurrentRunningSuite().description} suiteStart print success`); + } + } + + async suiteDone() { + if (this.abilityDelegatorArguments !== null) { + const currentRunningSuite = this.suiteService.getCurrentRunningSuite(); + this.suiteService.setCurrentRunningSuiteDesc(this.suiteService.getRootSuite(), this.suiteService.getCurrentRunningSuite(), ''); + let message = '\n' + `${PrintTag.OHOS_REPORT_STATUS}: class=` + this.suiteService.getCurrentRunningSuiteDesc(); + if (this.suiteService.currentRunningSuite.isSkip && this.suiteService.currentRunningSuite.skipReason !== '') { + message += '\n' + `${PrintTag.OHOS_REPORT_STATUS}: skipReason=` + this.suiteService.currentRunningSuite.skipReason; + } + message += '\n' + `${PrintTag.OHOS_REPORT_STATUS}: suiteconsuming=` + this.suiteService.getCurrentRunningSuite().duration; + if (currentRunningSuite.hookError) { + message += '\n' + `${PrintTag.OHOS_REPORT_STATUS}: ${currentRunningSuite.hookError.message}`; + } + message += '\n'; + if (SysTestKit.workerPort !== null) { + message += `${PrintTag.OHOS_REPORT_STATUS}: currentWorkerName=` + this.currentThreadName; + } + console.info(`${message}`); + await SysTestKit.print(message); + console.info(`${TAG}${this.suiteService.getCurrentRunningSuite().description} suiteDone print success`); + } + } + + async specStart() { + if (this.abilityDelegatorArguments !== null) { + let message = '\n' + `${PrintTag.OHOS_REPORT_STATUS}: class=` + this.suiteService.getCurrentRunningSuiteDesc(); + message += '\n' + `${PrintTag.OHOS_REPORT_STATUS}: current=` + (++this.index); + message += '\n' + `${PrintTag.OHOS_REPORT_STATUS}: id=JS`; + message += '\n' + `${PrintTag.OHOS_REPORT_STATUS}: numtests=` + this.specService.getTestTotal(); + message += '\n' + `${PrintTag.OHOS_REPORT_STATUS}: stream=`; + message += '\n' + `${PrintTag.OHOS_REPORT_STATUS}: test=` + this.specService.currentRunningSpec.description; + message += '\n' + `${PrintTag.OHOS_REPORT_STATUS_CODE}: 1` + '\n'; + if (this.specService.currentRunningSpec.isSkip) { + message += `${PrintTag.OHOS_REPORT_STATUS}: skipReason=` + this.specService.currentRunningSpec.skipReason + '\n'; + } + if (SysTestKit.workerPort !== null) { + message += `${PrintTag.OHOS_REPORT_STATUS}: currentWorkerName=` + this.currentThreadName; + } + console.info(`${message}`); + await SysTestKit.print(message); + console.info(`${TAG}${this.specService.currentRunningSpec.description} specStart start print success`); + } + } + + async specDone() { + if (this.abilityDelegatorArguments !== null) { + let message = '\n' + `${PrintTag.OHOS_REPORT_STATUS}: class=` + this.suiteService.getCurrentRunningSuiteDesc(); + message += '\n' + `${PrintTag.OHOS_REPORT_STATUS}: current=` + (this.index); + message += '\n' + `${PrintTag.OHOS_REPORT_STATUS}: id=JS`; + message += '\n' + `${PrintTag.OHOS_REPORT_STATUS}: numtests=` + this.specService.getTestTotal(); + let messageStack = ''; + let messageCode = ''; + if (this.specService.currentRunningSpec.error) { + messageStack = `${PrintTag.OHOS_REPORT_STATUS}: stack=` + this.specService.currentRunningSpec.error?.stack?.slice(0, -1); + messageCode += `${PrintTag.OHOS_REPORT_STATUS}: stream=`; + messageCode += this.specService.currentRunningSpec.expectMsg !== '' ? + `message: ${this.specService.currentRunningSpec.expectMsg}, Error in ${this.specService.currentRunningSpec.description}, ${this.specService.currentRunningSpec.error?.message}` : + `Error in ${this.specService.currentRunningSpec.description}, ${this.specService.currentRunningSpec.error?.message}`; + messageCode += '\n' + `${PrintTag.OHOS_REPORT_STATUS}: test=` + this.specService.currentRunningSpec.description; + messageCode += '\n' + `${PrintTag.OHOS_REPORT_STATUS_CODE}: -1` + '\n'; + } else if (this.specService.currentRunningSpec) { + if (this.specService.currentRunningSpec.fail) { + messageStack += `${PrintTag.OHOS_REPORT_STATUS}: stack=` + this.specService.currentRunningSpec.fail?.stack?.slice(0, -1); + messageCode += `${PrintTag.OHOS_REPORT_STATUS}: stream=`; + messageCode += this.specService.currentRunningSpec.expectMsg !== '' ? + `message: ${this.specService.currentRunningSpec.expectMsg}, Error in ${this.specService.currentRunningSpec.description}, ${this.specService.currentRunningSpec.fail?.message}` : + `Error in ${this.specService.currentRunningSpec.description}, ${this.specService.currentRunningSpec.fail?.message}`; + messageCode += '\n' + `${PrintTag.OHOS_REPORT_STATUS}: test=` + this.specService.currentRunningSpec.description; + messageCode += '\n' + `${PrintTag.OHOS_REPORT_STATUS_CODE}: -2` + '\n'; + } else { + messageStack += `${PrintTag.OHOS_REPORT_STATUS}: stream=`; + messageCode += `${PrintTag.OHOS_REPORT_STATUS}: test=` + this.specService.currentRunningSpec.description; + messageCode += '\n' + `${PrintTag.OHOS_REPORT_STATUS_CODE}: 0` + '\n'; + messageCode += this.specService.currentRunningSpec.isSkip ? (`${PrintTag.OHOS_REPORT_STATUS}: skipReason=` + this.specService.currentRunningSpec.skipReason + '\n') : ''; + } + } else { + messageCode += '\n'; + } + messageCode += `${PrintTag.OHOS_REPORT_STATUS}: consuming=` + this.specService.currentRunningSpec.duration + '\n'; + if (SysTestKit.workerPort !== null) { + messageCode += `${PrintTag.OHOS_REPORT_STATUS}: currentWorkerName=` + this.currentThreadName; + } + console.info(`${message}`); + console.info(`\n${messageStack}`); + console.info(`\n${messageCode}`); + await SysTestKit.print(message); + await SysTestKit.print(messageStack); + await SysTestKit.print(messageCode); + console.info(`${TAG}${this.specService.currentRunningSpec.description} specDone end print success`); + } + } +} + +export default OhReport; diff --git a/code/UI/CalendarViewSwitch/oh_modules/.ohpm/oh_modules/@ohos/hypium/src/main/module/report/ReportExtend.js b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/oh_modules/@ohos/hypium/src/main/module/report/ReportExtend.js new file mode 100644 index 0000000000000000000000000000000000000000..852fbcd5cbf97e776ebe5177a029df0f516594a5 --- /dev/null +++ b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/oh_modules/@ohos/hypium/src/main/module/report/ReportExtend.js @@ -0,0 +1,137 @@ +/* + * 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. + */ + +class ReportExtend { + constructor(fileModule) { + this.id = 'extend'; + this.fileModule = fileModule; + } + + init(coreContext) { + this.coreContext = coreContext; + this.suiteService = this.coreContext.getDefaultService('suite'); + } + + taskStart() { + + } + + taskDone() { + const report = { + tag: 'testsuites', + name: 'summary_report', + timestamp: new Date().toDateString(), + time: '1', + errors: 0, + failures: 0, + tests: 0, + children: [] + }; + const rootSuite = this.suiteService.rootSuite; + if (rootSuite && rootSuite.childSuites) { + for (let testsuite of rootSuite.childSuites) { + let suiteReport = { + tag: 'testsuite', + name: testsuite['description'], + errors: 0, + tests: 0, + failures: 0, + time: '0.1', + children: [] + }; + let specs = testsuite['specs']; + for (let testcase of specs) { + report.tests++; + suiteReport.tests++; + let caseReport = { + tag: 'testcase', + name: testcase['description'], + status: 'run', + time: '0.0', + classname: testsuite['description'] + }; + if (testcase.error) { + caseReport['result'] = false; + caseReport['children'] = [{ + tag: 'error', + type: '', + message: testcase.error.message + }]; + report.errors++; + suiteReport.errors++; + } else if (testcase.result.failExpects.length > 0) { + caseReport['result'] = false; + let message = ''; + testcase.result.failExpects.forEach(failExpect => { + message += failExpect.message || ('expect ' + failExpect.actualValue + ' ' + failExpect.checkFunc + ' ' + (failExpect.expectValue || '')) + ';'; + }); + caseReport['children'] = [{ + tag: 'failure', + type: '', + message: message + }]; + report.failures++; + suiteReport.failures++; + } else { + caseReport['result'] = true; + } + suiteReport.children.push(caseReport); + } + report.children.push(suiteReport); + } + } + + let reportXml = '\n' + json2xml(report); + this.fileModule.writeText({ + uri: 'internal://app/report.xml', + text: reportXml, + success: function () { + console.info('call success callback success'); + }, + fail: function (data, code) { + console.info('call fail callback success:'); + }, + complete: function () { + console.info('call complete callback success'); + } + }); + } +} + +function json2xml(json) { + let tagName; + let hasChildren = false; + let childrenStr = ''; + let attrStr = ''; + for (let key in json) { + if (key === 'tag') { + tagName = json[key]; + } else if (key === 'children') { + if (json[key].length > 0) { + hasChildren = true; + for (let child of json[key]) { + childrenStr += json2xml(child); + } + } + } else { + attrStr += ` ${key}="${json[key]}"`; + } + } + let xml = `<${tagName}${attrStr}`; + xml += hasChildren ? `>${childrenStr}` : '/>'; + return xml; +} + +export default ReportExtend; diff --git a/code/UI/CalendarViewSwitch/oh_modules/.ohpm/oh_modules/@ohos/hypium/src/main/service.js b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/oh_modules/@ohos/hypium/src/main/service.js new file mode 100644 index 0000000000000000000000000000000000000000..92d46b77959af314f40bd601ede62b9b6d8b9ba2 --- /dev/null +++ b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/oh_modules/@ohos/hypium/src/main/service.js @@ -0,0 +1,1230 @@ +/* + * 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 SysTestKit from './module/kit/SysTestKit'; +import { TAG } from './Constant'; +import LogExpectError from './module/report/LogExpectError'; +import { NestFilter } from './module/config/Filter'; + +class AssertException extends Error { + constructor(message) { + super(); + this.name = 'AssertException'; + this.message = message; + } +} + +function getFuncWithArgsZero(func, timeout, isStressTest) { + return new Promise(async (resolve, reject) => { + let timer = null; + if (!isStressTest) { + timer = setTimeout(() => { + reject(new Error('execute timeout ' + timeout + 'ms')); + }, timeout); + } + try { + await func(); + } catch (err) { + reject(err); + } + timer !== null ? clearTimeout(timer) : null; + resolve(); + }); +} + +function getFuncWithArgsOne(func, timeout, isStressTest) { + return new Promise(async (resolve, reject) => { + let timer = null; + if (!isStressTest) { + timer = setTimeout(() => { + reject(new Error('execute timeout ' + timeout + 'ms')); + }, timeout); + } + + function done() { + timer !== null ? clearTimeout(timer) : null; + resolve(); + } + + try { + await func(done); + } catch (err) { + timer !== null ? clearTimeout(timer) : null; + reject(err); + } + }); +} + +function getFuncWithArgsTwo(func, timeout, paramItem, isStressTest) { + return new Promise(async (resolve, reject) => { + let timer = null; + if (!isStressTest) { + timer = setTimeout(() => { + reject(new Error('execute timeout ' + timeout + 'ms')); + }, timeout); + } + + function done() { + timer !== null ? clearTimeout(timer) : null; + resolve(); + } + + try { + await func(done, paramItem); + } catch (err) { + timer !== null ? clearTimeout(timer) : null; + reject(err); + } + }); +} + +function processFunc(coreContext, func) { + let argNames = ((func || '').toString() + .replace(/((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg, '') + .match(/^(function)?\s*[^\(]*\(\s*([^\)]*)\)/m) || ['', '', ''])[2] + .split(',') // split parameters + .map(item => item.replace(/^\s*(_?)(.+?)\1\s*$/, name => name.split('=')[0].trim())) + .filter(String); + let funcLen = func.length; + let processedFunc; + const config = coreContext.getDefaultService('config'); + config.setSupportAsync(true); + const timeout = + (config.timeout === undefined ? 5000 : config.timeout); + const isStressTest = (coreContext.getServices('dataDriver') !== undefined || config.getStress() > 1); + switch (funcLen) { + case 0: { + processedFunc = function () { + return getFuncWithArgsZero(func, timeout, isStressTest); + }; + break; + } + case 1: { + if (argNames[0] === 'data') { + processedFunc = function (paramItem) { + func(paramItem); + }; + } else { + processedFunc = function () { + return getFuncWithArgsOne(func, timeout, isStressTest); + }; + } + break; + } + default: { + processedFunc = function (paramItem) { + return getFuncWithArgsTwo(func, timeout, paramItem, isStressTest); + }; + break; + } + } + return processedFunc; +} + +function secureRandomNumber() { + return crypto.randomBytes(8).readUInt32LE() / 0xffffffff; +} + +class SuiteService { + constructor(attr) { + this.id = attr.id; + this.rootSuite = new SuiteService.Suite({}); + this.currentRunningSuite = this.rootSuite; + this.suitesStack = [this.rootSuite]; + this.targetSuiteArray = []; + this.targetSpecArray = []; + this.currentRunningSuiteDesc = null; + this.fullRun = false; + this.isSkipSuite = false; + this.suiteSkipReason = null; + } + + describe(desc, func) { + const configService = this.coreContext.getDefaultService('config'); + if (this.suitesStack.some(suite => { return suite.description === desc })) { + console.error(`${TAG} Loop nesting occurs : ${desc}`); + this.suiteSkipReason = ''; + this.isSkipSuite = false; + return; + } + let isFilter = this.analyzeConfigServiceClass(configService.class, desc); + if (configService.filterSuite(desc) && isFilter) { + if (this.currentRunningSuite.description === '' || this.currentRunningSuite.description == null) { + console.info(`${TAG}filter suite : ${desc}`); + this.suiteSkipReason = ''; + this.isSkipSuite = false; + return; + } + } + const suite = new SuiteService.Suite({ description: desc }); + if (this.isSkipSuite) { + suite.isSkip = true; + suite.skipReason = this.suiteSkipReason; + } + this.suiteSkipReason = ''; + this.isSkipSuite = false; + if (typeof this.coreContext.getServices('dataDriver') !== 'undefined' && configService['dryRun'] !== 'true') { + let suiteStress = this.coreContext.getServices('dataDriver').dataDriver.getSuiteStress(desc); + for (let i = 1; i < suiteStress; i++) { + this.currentRunningSuite.childSuites.push(suite); + } + } + this.currentRunningSuite.childSuites.push(suite); + this.currentRunningSuite = suite; + this.suitesStack.push(suite); + func.call(); + this.suitesStack.pop(); + this.currentRunningSuite = this.suitesStack.pop(); + this.suitesStack.push(this.currentRunningSuite); + } + xdescribe(desc, func, reason) { + const configService = this.coreContext.getDefaultService('config'); + if (!configService.skipMessage && configService.runSkipped !== 'all') { + if (configService.runSkipped != null && configService.runSkipped !== '') { + let finalDesc = ''; + this.suitesStack.map(suite => { + finalDesc = finalDesc + '.' + suite.description; + }); + finalDesc = (finalDesc + '.' + desc).substring(2); + console.info(`${TAG} finalDesc ${finalDesc}`); + if (configService.checkIfSuiteInSkipRun(finalDesc)) { + console.info(`${TAG} runSkipped suite: ${desc}`); + } else { + console.info(reason == null ? `${TAG} skip suite: ${desc}` : `${TAG} skip suite: ${desc}, and the reason is ${reason}`); + return; + } + } else { + console.info(reason == null ? `${TAG} skip suite: ${desc}` : `${TAG} skip suite: ${desc}, and the reason is ${reason}`); + return; + } + } + this.isSkipSuite = true; + this.suiteSkipReason = reason; + this.describe(desc, func); + } + + beforeAll(func) { + this.currentRunningSuite.beforeAll.push(processFunc(this.coreContext, func)); + } + + beforeEach(func) { + this.currentRunningSuite.beforeEach.push(processFunc(this.coreContext, func)); + } + + beforeItSpecified(itDescs, func) { + this.currentRunningSuite.beforeItSpecified.set(itDescs, processFunc(this.coreContext, func)); + } + + afterItSpecified(itDescs, func) { + this.currentRunningSuite.afterItSpecified.set(itDescs, processFunc(this.coreContext, func)); + } + + afterAll(func) { + this.currentRunningSuite.afterAll.push(processFunc(this.coreContext, func)); + } + + afterEach(func) { + this.currentRunningSuite.afterEach.push(processFunc(this.coreContext, func)); + } + + getCurrentRunningSuite() { + return this.currentRunningSuite; + } + + setCurrentRunningSuite(suite) { + this.currentRunningSuite = suite; + } + + getRootSuite() { + return this.rootSuite; + } + + getCurrentRunningSuiteDesc() { + return this.currentRunningSuiteDesc; + } + + + setCurrentRunningSuiteDesc(suite, currentSuite, prefix) { + if (suite != null && suite === currentSuite) { + this.currentRunningSuiteDesc = prefix; + } else if (suite != null && suite !== currentSuite) { + suite.childSuites.forEach(it => { + let temp = prefix; + if (it.description != null || it.description !== '') { + temp = prefix === '' ? it.description : prefix + '.' + it.description; + } + this.setCurrentRunningSuiteDesc(it, currentSuite, temp); + } + ); + } + } + analyzeConfigServiceClass(configServiceClass, desc) { + if (configServiceClass == null || configServiceClass === '') { + this.fullRun = true + return false; + } + if (this.targetSuiteArray.length === 0) { + const targetArray = configServiceClass.split(','); + for (let index in targetArray) { + if (targetArray[index].includes('#')) { + this.targetSpecArray.push(targetArray[index]); + } else { + this.targetSuiteArray.push(targetArray[index]); + } + } + + } + return !configServiceClass.includes(desc); + + } + traversalResults(suite, obj, breakOnError) { + if (suite.childSuites.length === 0 && suite.specs.length === 0) { + return obj; + } + if (suite.specs.length > 0) { + for (const itItem of suite.specs) { + obj.total++; + let itInfo = { + currentThreadName: 'mainThread', + description: suite.description + '#' + itItem.description, + result: -3 + }; + if (SysTestKit.workerPort !== null) { + itInfo.currentThreadName = SysTestKit.workerPort.name; + } + obj.itItemList.push(itInfo); + if (breakOnError && (obj.error > 0 || obj.failure > 0)) { // breakOnError模式 + continue; + } + if (itItem.error) { + obj.error++; + itInfo.result = -1; + } else if (itItem.fail) { + obj.failure++; + itInfo.result = -2; + } else if (itItem.pass === true) { + obj.pass++; + itInfo.result = 0; + } + } + } + + obj.duration += suite.duration; + + if (suite.childSuites.length > 0) { + for (const suiteItem of suite.childSuites) { + this.traversalResults(suiteItem, obj, breakOnError); + } + } + } + + async setSuiteResults(suite, error, coreContext) { + if (suite.childSuites.length === 0 && suite.specs.length === 0) { + return obj; + } + if (suite.specs.length > 0) { + const specService = coreContext.getDefaultService('spec'); + for (const specItem of suite.specs) { + specService.setCurrentRunningSpec(specItem); + if (error instanceof AssertException) { + specItem.fail = error; + } else { + specItem.error = error; + } + await coreContext.fireEvents('spec', 'specStart', specItem); + await coreContext.fireEvents('spec', 'specDone', specItem); + } + } + if (suite.childSuites.length > 0) { + for (const suiteItem of suite.childSuites) { + await this.setSuiteResults(suiteItem, error, coreContext); + } + } + } + + getSummary() { + let suiteService = this.coreContext.getDefaultService('suite'); + let rootSuite = suiteService.rootSuite; + const specService = this.coreContext.getDefaultService('spec'); + const configService = this.coreContext.getDefaultService('config'); + let breakOnError = configService.isBreakOnError(); + let isError = specService.getStatus(); + let isBreaKOnError = breakOnError && isError; + // itItemList 保存当前用例执行情况, 发送到主线程用例计算最终结果 + let obj = { total: 0, failure: 0, error: 0, pass: 0, ignore: 0, duration: 0, itItemList: []}; + for (const suiteItem of rootSuite.childSuites) { + this.traversalResults(suiteItem, obj, isBreaKOnError); + } + obj.ignore = obj.total - obj.pass - obj.failure - obj.error; + return obj; + } + + init(coreContext) { + this.coreContext = coreContext; + } + + traversalSuites(suite, obj, configService) { + if (suite.childSuites.length === 0 && suite.specs.length === 0) { + return []; + } + if (suite.specs.length > 0) { + let itArray = []; + for (const itItem of suite['specs']) { + if (!configService.filterDesc(suite.description, itItem.description, itItem.fi, null)) { + itArray.push({ 'itName': itItem.description }); + } + } + obj[suite.description] = itArray; + } + if (suite.childSuites.length > 0) { + let suiteArray = []; + for (const suiteItem of suite.childSuites) { + let suiteObj = {}; + this.traversalSuites(suiteItem, suiteObj, configService); + if (!configService.filterSuite(suiteItem.description)) { + suiteArray.push(suiteObj); + } + } + obj.suites = suiteArray; + } + } + + async dryRun(abilityDelegator) { + console.info(`${TAG} rootSuite : ` + JSON.stringify(this.rootSuite)); + let obj = this.rootSuite; + let prefixStack = []; + let suiteArray = []; + let skipSuiteArray = []; + this.analyzeSuitesArray(prefixStack, suiteArray, skipSuiteArray, obj); + const configService = this.coreContext.getDefaultService('config'); + let result; + if (configService.skipMessage) { + result = { 'suites': suiteArray, 'skipSuites': skipSuiteArray }; + } else { + result = { 'suites': suiteArray }; + } + let strJson = JSON.stringify(result); + let strLen = strJson.length; + let maxLen = 500; + let maxCount = Math.floor(strLen / maxLen); + for (let count = 0; count <= maxCount; count++) { + await SysTestKit.print(strJson.substring(count * maxLen, (count + 1) * maxLen)); + } + console.info(`${TAG}dryRun print success`); + abilityDelegator.finishTest('dry run finished!!!', 0, () => { }); + } + + //将suitesArray的嵌套结构展开成三层结构 + analyzeSuitesArray(prefixStack, suiteArray, skipSuiteArray, obj) { + obj.childSuites.map(suite => { + if (suite.description != null && suite.description !== '') { + let prefix = ''; + if (prefixStack.length > 0) { + prefix = prefixStack.join('.') + '.' + suite.description; + } else { + prefix = suite.description; + } + prefixStack.push(suite.description); + let temp = {}; + temp[prefix] = []; + let skipTemp = {}; + skipTemp[prefix] = []; + suite.specs.map(spec => { + let it = { 'itName': spec.description }; + spec.isSkip ? skipTemp[prefix].push(it) : temp[prefix].push(it); + }); + suiteArray.push(temp); + skipSuiteArray.push(skipTemp); + } + this.analyzeSuitesArray(prefixStack, suiteArray, skipSuiteArray, suite); + prefixStack.pop(); + }); + } + //获取当前测试套下的所有测试用例数量 + getAllChildSuiteNum(suite, specArray) { + if (suite.specs != null) { + suite.specs.forEach(spec => specArray.push(spec)); + } + if (suite.childSuites != null) { + suite.childSuites.forEach(it => this.getAllChildSuiteNum(it, specArray)); + } + } + + execute() { + const configService = this.coreContext.getDefaultService('config'); + if (configService.filterValid.length !== 0) { + this.coreContext.fireEvents('task', 'incorrectFormat'); + return; + } + if (configService.filterXdescribe.length !== 0) { + this.coreContext.fireEvents('task', 'incorrectTestSuiteFormat'); + return; + } + if (configService.isRandom() && this.rootSuite.childSuites.length > 0) { + this.rootSuite.childSuites.sort(function () { + return Math.random().toFixed(1) > 0.5 ? -1 : 1; + }); + this.currentRunningSuite = this.rootSuite.childSuites[0]; + } + if (configService.isSupportAsync()) { + console.info(`${TAG} rootSuite:` + JSON.stringify(this.rootSuite)); + let asyncExecute = async () => { + await this.coreContext.fireEvents('task', 'taskStart'); + await this.rootSuite.asyncRun(this.coreContext); + }; + asyncExecute().then(async () => { + await this.coreContext.fireEvents('task', 'taskDone'); + }); + } else { + console.info('${TAG} rootSuite:' + JSON.stringify(this.rootSuite)); + this.coreContext.fireEvents('task', 'taskStart'); + this.rootSuite.run(this.coreContext); + this.coreContext.fireEvents('task', 'taskDone'); + } + } + + apis() { + const _this = this; + return { + describe: function (desc, func) { + return _this.describe(desc, func); + }, + xdescribe: function (desc, func, reason) { + return _this.xdescribe(desc, func, reason); + }, + beforeItSpecified: function (itDescs, func) { + return _this.beforeItSpecified(itDescs, func); + }, + afterItSpecified: function (itDescs, func) { + return _this.afterItSpecified(itDescs, func); + }, + beforeAll: function (func) { + return _this.beforeAll(func); + }, + beforeEach: function (func) { + return _this.beforeEach(func); + }, + afterAll: function (func) { + return _this.afterAll(func); + }, + afterEach: function (func) { + return _this.afterEach(func); + } + }; + } +} + +SuiteService.Suite = class { + constructor(attrs) { + this.description = attrs.description || ''; + this.childSuites = []; + this.specs = []; + this.beforeAll = []; + this.afterAll = []; + this.beforeItSpecified = new Map(); + this.afterItSpecified = new Map(); + this.beforeEach = []; + this.afterEach = []; + this.duration = 0; + this.hookError = null; + this.isSkip = false; + this.skipReason = ''; + } + + pushSpec(spec) { + this.specs.push(spec); + } + + removeSpec(desc) { + this.specs = this.specs.filter((item, index) => { + return item.description !== desc; + }); + } + + getSpecsNum() { + return this.specs.length; + } + + isRun(coreContext) { + const configService = coreContext.getDefaultService('config'); + const suiteService = coreContext.getDefaultService('suite'); + const specService = coreContext.getDefaultService('spec'); + let breakOnError = configService.isBreakOnError(); + let isError = specService.getStatus(); + return breakOnError && isError; + } + + run(coreContext) { + const suiteService = coreContext.getDefaultService('suite'); + suiteService.setCurrentRunningSuite(this); + if (this.description !== '') { + coreContext.fireEvents('suite', 'suiteStart', this); + } + this.runHookFunc('beforeAll'); + if (this.specs.length > 0) { + const configService = coreContext.getDefaultService('config'); + if (configService.isRandom()) { + this.specs.sort(function () { + return Math.random().toFixed(1) > 0.5 ? -1 : 1; + }); + } + for (let spec in this.specs) { + let isBreakOnError = this.isRun(coreContext); + if (isBreakOnError) { + break; + } + this.runHookFunc('beforeEach'); + spec.run(coreContext); + this.runHookFunc('afterEach'); + } + } + if (this.childSuites.length > 0) { + for (let suite in this.childSuites) { + let isBreakOnError = this.isRun(coreContext); + if (isBreakOnError) { + break; + } + suite.run(coreContext); + suiteService.setCurrentRunningSuite(suite); + } + } + this.runHookFunc('afterAll'); + if (this.description !== '') { + coreContext.fireEvents('suite', 'suiteDone'); + } + } + + async asyncRunSpecs(coreContext) { + const configService = coreContext.getDefaultService('config'); + if (configService.isRandom()) { + this.specs.sort(function () { + return Math.random().toFixed(1) > 0.5 ? -1 : 1; + }); + } + const specService = coreContext.getDefaultService('spec'); + for (let specItem of this.specs) { + specService.setCurrentRunningSpec(specItem); + // 遇错即停模式,发现用例有问题,直接返回,不在执行后面的it + let isBreakOnError = this.isRun(coreContext); + if (isBreakOnError) { + console.info('break description :' + this.description); + break; + } + await coreContext.fireEvents('spec', 'specStart', specItem); + try { + for (const [itNames, hookFunc] of this.beforeItSpecified) { + if ((Object.prototype.toString.call(itNames) === '[object Array]' && itNames.includes(specItem.description)) || + (Object.prototype.toString.call(itNames) === '[object String]' && itNames === specItem.description)) { + await Reflect.apply(hookFunc, null, []); + } + break; + } + await this.runAsyncHookFunc('beforeEach'); + await specItem.asyncRun(coreContext); + for (const [itNames, hookFunc] of this.afterItSpecified) { + if ((Object.prototype.toString.call(itNames) === '[object Array]' && itNames.includes(specItem.description)) || + (Object.prototype.toString.call(itNames) === '[object String]' && itNames === specItem.description)) { + await Reflect.apply(hookFunc, null, []); + } + break; + } + await this.runAsyncHookFunc('afterEach'); + } catch (e) { + console.error(`${TAG}stack:${e?.stack}`); + console.error(`${TAG}stack end`); + if (e instanceof AssertException) { + specItem.fail = e; + } else { + specItem.error = e; + } + specService.setStatus(true); + } + specItem.setResult(); + await coreContext.fireEvents('spec', 'specDone', specItem); + specService.setCurrentRunningSpec(null); + } + } + + async asyncRunChildSuites(coreContext) { + for (let i = 0; i < this.childSuites.length; i++) { + // 遇错即停模式, 发现用例有问题,直接返回,不在执行后面的description + let isBreakOnError = this.isRun(coreContext); + if (isBreakOnError) { + console.info(`${TAG}break description : ${this.description}`); + break; + } + await this.childSuites[i].asyncRun(coreContext); + } + } + + async asyncRun(coreContext) { + const suiteService = coreContext.getDefaultService('suite'); + const specService = coreContext.getDefaultService('spec'); + + suiteService.setCurrentRunningSuite(this); + suiteService.suitesStack.push(this); + if (this.description !== '') { + await coreContext.fireEvents('suite', 'suiteStart', this); + } + + try { + await this.runAsyncHookFunc('beforeAll'); + } catch (error) { + console.error(`${TAG}${error?.stack}`); + this.hookError = error; + } + + if (this.hookError !== null) { + specService.setStatus(true); + await suiteService.setSuiteResults(this, this.hookError, coreContext); + } + + if (this.specs.length > 0 && this.hookError === null) { + await this.asyncRunSpecs(coreContext); + } + + if (this.childSuites.length > 0 && this.hookError === null) { + await this.asyncRunChildSuites(coreContext); + } + + try { + await this.runAsyncHookFunc('afterAll'); + } catch (error) { + console.error(`${TAG}${error?.stack}`); + this.hookError = error; + specService.setStatus(true); + } + + if (this.description !== '') { + await coreContext.fireEvents('suite', 'suiteDone'); + let childSuite = suiteService.suitesStack.pop(); + let currentRunningSuite = suiteService.suitesStack.pop(); + suiteService.setCurrentRunningSuite(currentRunningSuite); + suiteService.suitesStack.push(currentRunningSuite); + } + } + + runHookFunc(hookName) { + if (this[hookName] && this[hookName].length > 0) { + this[hookName].forEach(func => { + try { + func(); + } catch (e) { + console.error(`${TAG}${e.stack}`); + } + }); + } + } + + async runAsyncHookFunc(hookName) { + for (const hookItem of this[hookName]) { + try { + await hookItem(); + } catch (error) { + error['message'] += `, error in ${hookName} function`; + throw error; + } + + } + } +}; + +class SpecService { + constructor(attr) { + this.id = attr.id; + this.totalTest = 0; + this.hasError = false; + this.skipSpecNum = 0; + this.isSkipSpec = false; + this.specSkipReason = ''; + } + + init(coreContext) { + this.coreContext = coreContext; + } + + setCurrentRunningSpec(spec) { + this.currentRunningSpec = spec; + } + + setStatus(obj) { + this.hasError = obj; + } + + getStatus() { + return this.hasError; + } + + getTestTotal() { + return this.totalTest; + } + + getCurrentRunningSpec() { + return this.currentRunningSpec; + } + + + getSkipSpecNum() { + return this.skipSpecNum; + } + + initSpecService() { + this.isSkipSpec = false; + this.specSkipReason = ''; + } + + it(desc, filter, func) { + const suiteService = this.coreContext.getDefaultService('suite'); + const configService = this.coreContext.getDefaultService('config'); + let isFilter = new NestFilter().filterNestName(suiteService.targetSuiteArray, suiteService.targetSpecArray, suiteService.suitesStack, desc); + if (configService.filterWithNest(desc, filter)) { + console.info(`${TAG}filter it :${desc}`); + this.initSpecService(); + return; + } + if (configService.filterDesc(suiteService.currentRunningSuite.description, desc, filter, this.coreContext) && isFilter && !suiteService.fullRun) { + console.info(`${TAG}filter it :${desc}`); + this.initSpecService(); + } else { + let processedFunc = processFunc(this.coreContext, func); + const spec = new SpecService.Spec({ description: desc, fi: filter, fn: processedFunc }); + if (this.isSkipSpec) { + spec.isSkip = true; + spec.skipReason = this.specSkipReason; + } + this.initSpecService(); + if (configService.runSkipped === 'skipped' && !spec.isSkip) { + console.info(`${TAG} runSkipped is skipped , just run xit, don't run it: ${spec.description}`); + return; + } + if (suiteService.getCurrentRunningSuite().isSkip && !spec.isSkip) { + configService.filterXdescribe.push(suiteService.getCurrentRunningSuite().description); + } + if (typeof this.coreContext.getServices('dataDriver') !== 'undefined' && configService['dryRun'] !== 'true') { + let specStress = this.coreContext.getServices('dataDriver').dataDriver.getSpecStress(desc); + for (let i = 1; i < specStress; i++) { + this.totalTest++; + suiteService.getCurrentRunningSuite().pushSpec(spec); + } + } + // dryRun 状态下不统计压力测试重复数据 + if (configService['dryRun'] !== 'true') { + let stress = configService.getStress(); // 命令配置压力测试 + console.info(`${TAG}stress length : ${stress}`); + for (let i = 1; i < stress; i++) { + this.totalTest++; + suiteService.getCurrentRunningSuite().pushSpec(spec); + } + } + this.totalTest++; + suiteService.getCurrentRunningSuite().pushSpec(spec); + } + } + + xit(desc, filter, func, reason) { + const configService = this.coreContext.getDefaultService('config'); + const suiteService = this.coreContext.getDefaultService('suite'); + if (!configService.skipMessage && configService.runSkipped !== 'all') { + if (configService.runSkipped != null && configService.runSkipped !== '') { + let finalDesc = ''; + suiteService.suitesStack.map(suite => { + finalDesc = finalDesc + '.' + suite.description; + }); + finalDesc = (finalDesc + '#' + desc).substring(2); + if (configService.checkIfSpecInSkipRun(finalDesc)) { + console.info(`${TAG} runSkipped spec: ${desc}`); + } else { + console.info(reason == null ? `${TAG} skip spec: ${desc}` : `${TAG} skip spec: ${desc}, and the reason is ${reason}`); + return; + } + } else { + console.info(reason == null ? `${TAG} skip spec: ${desc}` : `${TAG} skip spec: ${desc}, and the reason is ${reason}`); + return; + } + } + this.skipSpecNum++; + this.isSkipSpec = true; + this.specSkipReason = reason; + this.it(desc, filter, func); + } + + apis() { + const _this = this; + return { + it: function (desc, filter, func) { + return _this.it(desc, filter, func); + }, + xit: function (desc, filter, func, reason) { + return _this.xit(desc, filter, func, reason); + } + }; + } +} + +SpecService.Spec = class { + constructor(attrs) { + this.description = attrs.description || ''; + this.fi = attrs.fi; + this.fn = attrs.fn || function () { + }; + this.fail = undefined; + this.error = undefined; + this.duration = 0; + this.startTime = 0; + this.isExecuted = false; // 当前用例是否执行 + this.isSkip = false; + this.skipReason = ''; + this.expectMsg = ''; + } + + setResult() { + if (this.fail) { + this.pass = false; + } else { + this.pass = true; + } + } + + run(coreContext) { + const specService = coreContext.getDefaultService('spec'); + specService.setCurrentRunningSpec(this); + coreContext.fireEvents('spec', 'specStart', this); + this.isExecuted = true; + try { + let dataDriver = coreContext.getServices('dataDriver'); + if (typeof dataDriver === 'undefined') { + this.fn(); + } else { + let suiteParams = dataDriver.dataDriver.getSuiteParams(); + let specParams = dataDriver.dataDriver.getSpecParams(); + console.info(`${TAG}[suite params] ${JSON.stringify(suiteParams)}`); + console.info(`${TAG}[spec params] ${JSON.stringify(specParams)}`); + if (this.fn.length === 0) { + this.fn(); + } else if (specParams.length === 0) { + this.fn(suiteParams); + } else { + specParams.forEach(paramItem => this.fn(Object.assign({}, paramItem, suiteParams))); + } + } + this.setResult(); + } catch (e) { + this.error = e; + specService.setStatus(true); + } + coreContext.fireEvents('spec', 'specDone', this); + } + + async asyncRun(coreContext) { + const dataDriver = coreContext.getServices('dataDriver'); + if (typeof dataDriver === 'undefined') { + await this.fn(); + } else { + const suiteParams = dataDriver.dataDriver.getSuiteParams(); + const specParams = dataDriver.dataDriver.getSpecParams(); + console.info(`[suite params] ${JSON.stringify(suiteParams)}`); + console.info(`[spec params] ${JSON.stringify(specParams)}`); + if (this.fn.length === 0) { + await this.fn(); + } else if (specParams.length === 0) { + await this.fn(suiteParams); + } else { + for (const paramItem of specParams) { + await this.fn(Object.assign({}, paramItem, suiteParams)); + } + } + } + + this.isExecuted = true; + } + + filterCheck(coreContext) { + const specService = coreContext.getDefaultService('spec'); + specService.setCurrentRunningSpec(this); + return true; + } +}; + +class ExpectService { + constructor(attr) { + this.id = attr.id; + this.matchers = {}; + this.customMatchers = []; + } + + expect(actualValue) { + return this.wrapMatchers(actualValue); + } + + init(coreContext) { + this.coreContext = coreContext; + this.addMatchers(this.basicMatchers()); + } + + addMatchers(matchers) { + for (const matcherName in matchers) { + if (Object.prototype.hasOwnProperty.call(matchers, matcherName)) { + this.matchers[matcherName] = matchers[matcherName]; + } + } + } + + removeMatchers(customAssertionName) { + if (customAssertionName === 'all') { + for (const matcherName in this.matchers) { + this.matchers[matcherName] = this.customMatchers.includes(matcherName) ? (() => {throw new Error(`${matcherName} is unregistered`)}) : undefined; + } + }else { + this.matchers[customAssertionName] = () => { + throw new Error(`${customAssertionName} is unregistered`) + }; + } + } + + basicMatchers() { + return { + assertTrue: function (actualValue) { + return { + pass: (actualValue) === true, + message: 'expect true, actualValue is ' + actualValue + }; + }, + assertEqual: function (actualValue, args) { + let msg = 'expect ' + actualValue + ' equals ' + args[0]; + if (actualValue == args[0]) { // 数值相同,提示数据类型 + const aClassName = Object.prototype.toString.call(actualValue); + const bClassName = Object.prototype.toString.call(args[0]); + msg = 'expect ' + actualValue + aClassName + ' equals ' + args[0] + bClassName + 'strict mode inspect type'; + } + return { + pass: (actualValue) === args[0], + expectValue: args[0], + message: msg + }; + }, + assertThrow: function (actual, args) { + const result = { + pass: false + }; + if (typeof actual !== 'function') { + result.message = 'toThrow\'s Actual should be a Function'; + } else { + let hasThrow = false; + let throwError; + try { + actual(); + } catch (e) { + hasThrow = true; + throwError = e; + } + if (!hasThrow) { + result.message = 'function did not throw an exception'; + } else if (throwError && throwError.message === args[0]) { + result.pass = true; + } else { + result.message = `expect to throw ${args[0]} , actual throw ${throwError.message}`; + } + } + return result; + } + }; + } + + initWrapMatchers(currentRunningSpec) { + return { + // 翻转标识 + isNot: false, + // 翻转方法 + not: function () { + this.isNot = true; + return this; + }, + message: function (msg) { + currentRunningSpec.expectMsg = msg; + console.info(`${TAG} msg: ${msg}`); + return this; + } + }; + + } + wrapMatchers(actualValue) { + const _this = this; + const specService = _this.coreContext.getDefaultService('spec'); + const currentRunningSpec = specService.getCurrentRunningSpec(); + const wrappedMatchers = this.initWrapMatchers(currentRunningSpec); + const currentRunningSuite = _this.coreContext.getDefaultService('suite').getCurrentRunningSuite(); + for (const matcherName in this.matchers) { + let result = Object.prototype.hasOwnProperty.call(this.matchers, matcherName); + if (!result) { + continue; + } + if (matcherName.search('assertPromise') == 0) { + wrappedMatchers[matcherName] = async function () { + await _this.matchers[matcherName](actualValue, arguments).then(function (result) { + if (wrappedMatchers.isNot) { + result.pass = !result.pass; + } + result.actualValue = actualValue; + result.checkFunc = matcherName; + if (!result.pass) { + const assertError = new AssertException(result.message); + currentRunningSpec ? currentRunningSpec.fail = assertError : currentRunningSuite.hookError = assertError; + throw assertError; + } + }); + }; + } else { + wrappedMatchers[matcherName] = function () { + const result = _this.customMatchers.includes(matcherName) ? _this.matchers[matcherName](actualValue, arguments[0]) : _this.matchers[matcherName](actualValue, arguments); + if (wrappedMatchers.isNot) { + result.pass = !result.pass; + result.message = LogExpectError.getErrorMsg(matcherName, actualValue, arguments[0], result.message); + } + result.actualValue = actualValue; + result.checkFunc = matcherName; + if (!result.pass) { + const assertError = new AssertException(result.message); + currentRunningSpec ? currentRunningSpec.fail = assertError : currentRunningSuite.hookError = assertError; + throw assertError; + } + }; + } + } + return wrappedMatchers; + } + + apis() { + const _this = this; + return { + expect: function (actualValue) { + return _this.expect(actualValue); + } + }; + } +} + +class ReportService { + constructor(attr) { + this.id = attr.id; + } + + init(coreContext) { + this.coreContext = coreContext; + this.specService = this.coreContext.getDefaultService('spec'); + this.suiteService = this.coreContext.getDefaultService('suite'); + this.duration = 0; + } + + taskStart() { + console.info(`${TAG}[start] start run suites`); + } + + async suiteStart() { + console.info(`${TAG}[suite start]${this.suiteService.getCurrentRunningSuite().description}`); + } + + async specStart() { + console.info(`${TAG}start running case '${this.specService.currentRunningSpec.description}'`); + this.index = this.index + 1; + let spec = this.specService.currentRunningSpec; + spec.startTime = await SysTestKit.getRealTime(); + } + + async specDone() { + let msg = ''; + let spec = this.specService.currentRunningSpec; + let suite = this.suiteService.currentRunningSuite; + spec.duration = await SysTestKit.getRealTime() - spec.startTime; + suite.duration += spec.duration; + if (spec.error) { + this.formatPrint('error', spec.description + ' ; consuming ' + spec.duration + 'ms'); + this.formatPrint('errorDetail', spec.error); + } else if (spec.fail) { + this.formatPrint('fail', spec.description + ' ; consuming ' + spec.duration + 'ms'); + this.formatPrint('failDetail', spec.fail?.message); + } else { + this.formatPrint('pass', spec.description + ' ; consuming ' + spec.duration + 'ms'); + } + this.formatPrint(this.specService.currentRunningSpec.error, msg); + } + + suiteDone() { + let suite = this.suiteService.currentRunningSuite; + let message = suite.hookError ? `, ${suite.hookError?.message}` : ''; + console.info(`[suite end] ${suite.description} consuming ${suite.duration} ms${message}`); + } + + taskDone() { + let msg = ''; + let summary = this.suiteService.getSummary(); + msg = 'total cases:' + summary.total + ';failure ' + summary.failure + ',' + 'error ' + summary.error; + msg += ',pass ' + summary.pass + '; consuming ' + summary.duration + 'ms'; + console.info(`${TAG}${msg}`); + console.info(`${TAG}[end] run suites end`); + } + + incorrectFormat() { + if (this.coreContext.getDefaultService('config').filterValid.length !== 0) { + this.coreContext.getDefaultService('config').filterValid.forEach(function (item) { + console.info(`${TAG}this param ${item} is invalid`); + }); + } + } + + incorrectTestSuiteFormat() { + if (this.coreContext.getDefaultService('config').filterXdescribe.length !== 0) { + this.coreContext.getDefaultService('config').filterXdescribe.forEach(function (item) { + console.info(`${TAG}xdescribe: ${item} should not contain it`); + }) + } + } + + formatPrint(type, msg) { + switch (type) { + case 'pass': + console.info(`${TAG}[pass]${msg}`); + break; + case 'fail': + console.info(`${TAG}[fail]${msg}`); + break; + case 'failDetail': + console.info(`${TAG}[failDetail]${msg}`); + break; + case 'error': + console.info(`${TAG}[error]${msg}`); + break; + case 'errorDetail': + console.info(`${TAG}[errorDetail]${msg}`); + break; + } + } + + sleep(numberMillis) { + var now = new Date(); + var exitTime = now.getTime() + numberMillis; + while (true) { + now = new Date(); + if (now.getTime() > exitTime) { + return; + } + } + } +} + +export { + SuiteService, + SpecService, + ExpectService, + ReportService +}; diff --git a/code/UI/CalendarViewSwitch/oh_modules/.ohpm/oh_modules/@ohos/hypium/src/main/testrunner/OpenHarmonyTestRunner.ts b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/oh_modules/@ohos/hypium/src/main/testrunner/OpenHarmonyTestRunner.ts new file mode 100644 index 0000000000000000000000000000000000000000..e6f4c1b12dd69714ed5a4524671abca1fbcaa58c --- /dev/null +++ b/code/UI/CalendarViewSwitch/oh_modules/.ohpm/oh_modules/@ohos/hypium/src/main/testrunner/OpenHarmonyTestRunner.ts @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2023-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 { abilityDelegatorRegistry, TestRunner } from '@kit.TestKit'; +import { BusinessError } from '@kit.BasicServicesKit'; +import { hilog } from '@kit.PerformanceAnalysisKit'; +import { resourceManager } from '@kit.LocalizationKit'; +import { util } from '@kit.ArkTS'; +import { Hypium } from '@ohos/hypium'; +import testsuite from '../test/List.test'; + +let abilityDelegator: abilityDelegatorRegistry.AbilityDelegator; +let abilityDelegatorArguments: abilityDelegatorRegistry.AbilityDelegatorArgs; +let jsonPath: string = 'mock/mock-config.json'; +let domain: number = 0x0000; //日志标识,0x0000作为测试框架的业务标识 +let tag: string = 'testTag'; //日志标识字符串,作为tag标识当前runner类下的测试行为 + +export default class OpenHarmonyTestRunner implements TestRunner { + constructor() { + } + + onPrepare() { + hilog.info(domain, tag, '%{public}s', 'OpenHarmonyTestRunner OnPrepare'); + } + + async onRun() { + hilog.info(domain, tag, '%{public}s', 'OpenHarmonyTestRunner onRun run'); + abilityDelegatorArguments = abilityDelegatorRegistry.getArguments(); + abilityDelegator = abilityDelegatorRegistry.getAbilityDelegator(); + let moduleName = abilityDelegatorArguments.parameters['-m']; + let context = abilityDelegator.getAppContext().getApplicationContext().createModuleContext(moduleName); + let mResourceManager = context.resourceManager; + await checkMock(abilityDelegator, mResourceManager); + hilog.info(domain, tag, '%{public}s', 'start run testcase!!!'); + Hypium.hypiumTest(abilityDelegator, abilityDelegatorArguments, testsuite); + hilog.info(domain, tag, '%{public}s', 'OpenHarmonyTestRunner onRun end'); + } +} + +async function checkMock(abilityDelegator: abilityDelegatorRegistry.AbilityDelegator, resourceManager: resourceManager.ResourceManager) { + let rawFile: Uint8Array; + try { + rawFile = resourceManager.getRawFileContentSync(jsonPath); + hilog.info(domain, tag, 'MockList file exists'); + let mockStr: string = util.TextDecoder.create("utf-8", { ignoreBOM: true }).decodeWithStream(rawFile); + let mockMap: Record = getMockList(mockStr); + try { + abilityDelegator.setMockList(mockMap); + } catch (error) { + let code = (error as BusinessError).code; + let message = (error as BusinessError).message; + hilog.error(domain, tag, `abilityDelegator.setMockList failed, error code: ${code}, message: ${message}.`); + } + } catch (error) { + let code = (error as BusinessError).code; + let message = (error as BusinessError).message; + hilog.error(domain, tag, `ResourceManager:callback getRawFileContent failed, error code: ${code}, message: ${message}.`); + } +} + +function getMockList(jsonStr: string) { + let jsonObj: Record = JSON.parse(jsonStr); + let map: Map = new Map(Object.entries(jsonObj)); + let mockList: Record = {}; + map.forEach((value: object, key: string) => { + let realValue: string = value['source'].toString(); + mockList[key] = realValue; + }); + hilog.info(domain, tag, '%{public}s', 'mock-json value:' + JSON.stringify(mockList) ?? ''); + return mockList; +} \ No newline at end of file diff --git a/code/UI/CalendarViewSwitch/oh_modules/@ohos/hypium/BuildProfile.ets b/code/UI/CalendarViewSwitch/oh_modules/@ohos/hypium/BuildProfile.ets new file mode 100644 index 0000000000000000000000000000000000000000..b054e98af7ad6092740cef28d7dcf6207e514dcb --- /dev/null +++ b/code/UI/CalendarViewSwitch/oh_modules/@ohos/hypium/BuildProfile.ets @@ -0,0 +1,5 @@ +export default class BuildProfile { + static readonly HAR_VERSION = '1.0.19'; + static readonly BUILD_MODE_NAME = 'debug'; + static readonly DEBUG = true; +} \ No newline at end of file diff --git a/code/UI/CalendarViewSwitch/oh_modules/@ohos/hypium/CHANGELOG.md b/code/UI/CalendarViewSwitch/oh_modules/@ohos/hypium/CHANGELOG.md new file mode 100644 index 0000000000000000000000000000000000000000..309d707c3d19f506f5582509edf1d0db9011a5b1 --- /dev/null +++ b/code/UI/CalendarViewSwitch/oh_modules/@ohos/hypium/CHANGELOG.md @@ -0,0 +1,24 @@ +## 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.18-rc.1 +规范日志格式 +## 1.0.19 +- 规范日志格式 +- 代码规范整改 \ No newline at end of file diff --git a/code/UI/CalendarViewSwitch/oh_modules/@ohos/hypium/LICENSE b/code/UI/CalendarViewSwitch/oh_modules/@ohos/hypium/LICENSE new file mode 100644 index 0000000000000000000000000000000000000000..4947287f7b5ccb5d1e8b7b2d3aa5d89f322c160d --- /dev/null +++ b/code/UI/CalendarViewSwitch/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/code/UI/CalendarViewSwitch/oh_modules/@ohos/hypium/README.md b/code/UI/CalendarViewSwitch/oh_modules/@ohos/hypium/README.md new file mode 100644 index 0000000000000000000000000000000000000000..6d795105533a9b3b9949e91d2c3dd14e8f867433 --- /dev/null +++ b/code/UI/CalendarViewSwitch/oh_modules/@ohos/hypium/README.md @@ -0,0 +1,219 @@ +
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后缀 +``` + +## 安装使用 +*** +- 在DevEco Studio内使用Hypium +- 工程级package.json内配置: +```json +"dependencies": { + "@ohos/hypium": "1.0.19" +} +``` +注: +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/code/UI/CalendarViewSwitch/oh_modules/@ohos/hypium/build-profile.json5 b/code/UI/CalendarViewSwitch/oh_modules/@ohos/hypium/build-profile.json5 new file mode 100644 index 0000000000000000000000000000000000000000..312d38eb08629793b3484c7373213f22840c8d82 --- /dev/null +++ b/code/UI/CalendarViewSwitch/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/code/UI/CalendarViewSwitch/oh_modules/@ohos/hypium/hvigorfile.ts b/code/UI/CalendarViewSwitch/oh_modules/@ohos/hypium/hvigorfile.ts new file mode 100644 index 0000000000000000000000000000000000000000..42187071482d292588ad40babeda74f7b8d97a23 --- /dev/null +++ b/code/UI/CalendarViewSwitch/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/code/UI/CalendarViewSwitch/oh_modules/@ohos/hypium/index.d.ts b/code/UI/CalendarViewSwitch/oh_modules/@ohos/hypium/index.d.ts new file mode 100644 index 0000000000000000000000000000000000000000..7272b5fa839a2cd510d0c70d517bb6800133dba2 --- /dev/null +++ b/code/UI/CalendarViewSwitch/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/code/UI/CalendarViewSwitch/oh_modules/@ohos/hypium/index.ets b/code/UI/CalendarViewSwitch/oh_modules/@ohos/hypium/index.ets new file mode 100644 index 0000000000000000000000000000000000000000..02a237f1c999d1f48b3974b6076d5dae9213245a --- /dev/null +++ b/code/UI/CalendarViewSwitch/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): (f?: 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/code/UI/CalendarViewSwitch/oh_modules/@ohos/hypium/index.js b/code/UI/CalendarViewSwitch/oh_modules/@ohos/hypium/index.js new file mode 100644 index 0000000000000000000000000000000000000000..02d06d9d1b4b478aa2aec70ba3a73a5e123c98db --- /dev/null +++ b/code/UI/CalendarViewSwitch/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/code/UI/CalendarViewSwitch/oh_modules/@ohos/hypium/index.ts b/code/UI/CalendarViewSwitch/oh_modules/@ohos/hypium/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..b7082ebc98214b58d41e8681791809f1aee48f12 --- /dev/null +++ b/code/UI/CalendarViewSwitch/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/code/UI/CalendarViewSwitch/oh_modules/@ohos/hypium/oh-package.json5 b/code/UI/CalendarViewSwitch/oh_modules/@ohos/hypium/oh-package.json5 new file mode 100644 index 0000000000000000000000000000000000000000..d91344eda60f35477a5caf4bf5c116ffac2e53db --- /dev/null +++ b/code/UI/CalendarViewSwitch/oh_modules/@ohos/hypium/oh-package.json5 @@ -0,0 +1 @@ +{"name":"@ohos/hypium","version":"1.0.19","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":{}} diff --git a/code/UI/CalendarViewSwitch/oh_modules/@ohos/hypium/src/main/Constant.js b/code/UI/CalendarViewSwitch/oh_modules/@ohos/hypium/src/main/Constant.js new file mode 100644 index 0000000000000000000000000000000000000000..f470d69cd9a3302b19d45c147ca7d7c1dd8a3b18 --- /dev/null +++ b/code/UI/CalendarViewSwitch/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/code/UI/CalendarViewSwitch/oh_modules/@ohos/hypium/src/main/core.js b/code/UI/CalendarViewSwitch/oh_modules/@ohos/hypium/src/main/core.js new file mode 100644 index 0000000000000000000000000000000000000000..cfcb5f17287208f5e6869b4248faf6c9093002d9 --- /dev/null +++ b/code/UI/CalendarViewSwitch/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/code/UI/CalendarViewSwitch/oh_modules/@ohos/hypium/src/main/event.js b/code/UI/CalendarViewSwitch/oh_modules/@ohos/hypium/src/main/event.js new file mode 100644 index 0000000000000000000000000000000000000000..3be0211f01646c9c269c2425cbee82c87ac6d9ea --- /dev/null +++ b/code/UI/CalendarViewSwitch/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/code/UI/CalendarViewSwitch/oh_modules/@ohos/hypium/src/main/interface.js b/code/UI/CalendarViewSwitch/oh_modules/@ohos/hypium/src/main/interface.js new file mode 100644 index 0000000000000000000000000000000000000000..1bf43509ac3f70f1275e1da79388e1511e72a3f9 --- /dev/null +++ b/code/UI/CalendarViewSwitch/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/code/UI/CalendarViewSwitch/oh_modules/@ohos/hypium/src/main/module.json b/code/UI/CalendarViewSwitch/oh_modules/@ohos/hypium/src/main/module.json new file mode 100644 index 0000000000000000000000000000000000000000..b0e022bd13205c4c3310480d6732db4707193b3a --- /dev/null +++ b/code/UI/CalendarViewSwitch/oh_modules/@ohos/hypium/src/main/module.json @@ -0,0 +1,26 @@ +{ + "app": { + "bundleName": "com.ohos.myapplication", + "debug": true, + "versionCode": 1000000, + "versionName": "1.0.0", + "minAPIVersion": 40100011, + "targetAPIVersion": 40100011, + "apiReleaseType": "Beta1", + "compileSdkVersion": "4.1.0.55", + "compileSdkType": "HarmonyOS", + "bundleType": "app" + }, + "module": { + "name": "hypium", + "type": "har", + "deviceTypes": [ + "default", + "tablet", + "tv", + "wearable", + "car" + ], + "installationFree": false + } +} diff --git a/code/UI/CalendarViewSwitch/oh_modules/@ohos/hypium/src/main/module/assert/ExpectExtend.js b/code/UI/CalendarViewSwitch/oh_modules/@ohos/hypium/src/main/module/assert/ExpectExtend.js new file mode 100644 index 0000000000000000000000000000000000000000..d10d15e6f9955c6d04610101f8766c951ee1a35d --- /dev/null +++ b/code/UI/CalendarViewSwitch/oh_modules/@ohos/hypium/src/main/module/assert/ExpectExtend.js @@ -0,0 +1,85 @@ +/* + * 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 assertNull from './assertNull'; +import assertClose from './assertClose'; +import assertContain from './assertContain'; +import assertLess from './assertLess'; +import assertLarger from './assertLarger'; +import assertFail from './assertFail'; +import assertUndefined from './assertUndefined'; +import assertFalse from './assertFalse'; +import assertInstanceOf from './assertInstanceOf'; +import assertThrowError from './assertThrowError'; +import assertLargerOrEqual from './assertLargerOrEqual' +import assertLessOrEqual from './assertLessOrEqual' +import assertNaN from './assertNaN' +import assertNegUnlimited from './assertNegUnlimited' +import assertPosUnlimited from './assertPosUnlimited' +import assertDeepEquals from './deepEquals/assertDeepEquals' +import assertPromiseIsPending from './assertPromiseIsPending'; +import assertPromiseIsRejected from './assertPromiseIsRejected'; +import assertPromiseIsRejectedWith from './assertPromiseIsRejectedWith'; +import assertPromiseIsRejectedWithError from './assertPromiseIsRejectedWithError'; +import assertPromiseIsResolved from './assertPromiseIsResolved'; +import assertPromiseIsResolvedWith from './assertPromiseIsResolvedWith'; +class ExpectExtend { + constructor(attr) { + this.id = attr.id; + this.matchers = {}; + } + + extendsMatchers() { + this.matchers.assertNull = assertNull; + this.matchers.assertClose = assertClose; + this.matchers.assertContain = assertContain; + this.matchers.assertLess = assertLess; + this.matchers.assertLarger = assertLarger; + this.matchers.assertFail = assertFail; + this.matchers.assertUndefined = assertUndefined; + this.matchers.assertFalse = assertFalse; + this.matchers.assertInstanceOf = assertInstanceOf; + this.matchers.assertThrowError = assertThrowError; + this.matchers.assertLargerOrEqual = assertLargerOrEqual; + this.matchers.assertLessOrEqual = assertLessOrEqual; + this.matchers.assertNaN = assertNaN; + this.matchers.assertNegUnlimited = assertNegUnlimited; + this.matchers.assertPosUnlimited = assertPosUnlimited; + this.matchers.assertDeepEquals = assertDeepEquals; + this.matchers.assertPromiseIsPending = assertPromiseIsPending; + this.matchers.assertPromiseIsRejected = assertPromiseIsRejected; + this.matchers.assertPromiseIsRejectedWith = assertPromiseIsRejectedWith; + this.matchers.assertPromiseIsRejectedWithError = assertPromiseIsRejectedWithError; + this.matchers.assertPromiseIsResolved = assertPromiseIsResolved; + this.matchers.assertPromiseIsResolvedWith = assertPromiseIsResolvedWith; + } + + init(coreContext) { + this.coreContext = coreContext; + this.extendsMatchers(); + const expectService = this.coreContext.getDefaultService('expect'); + expectService.addMatchers(this.matchers); + } + + apis() { + return { + 'expect': function (actualValue) { + return this.coreContext.getDefaultService('expect').expect(actualValue); + } + }; + } +} + +export default ExpectExtend; diff --git a/code/UI/CalendarViewSwitch/oh_modules/@ohos/hypium/src/main/module/assert/assertClose.js b/code/UI/CalendarViewSwitch/oh_modules/@ohos/hypium/src/main/module/assert/assertClose.js new file mode 100644 index 0000000000000000000000000000000000000000..7e692bd25f1c026640978a042a9c9f64b0e8d5d3 --- /dev/null +++ b/code/UI/CalendarViewSwitch/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/code/UI/CalendarViewSwitch/oh_modules/@ohos/hypium/src/main/module/assert/assertContain.js b/code/UI/CalendarViewSwitch/oh_modules/@ohos/hypium/src/main/module/assert/assertContain.js new file mode 100644 index 0000000000000000000000000000000000000000..7fba0d9755503e5e926f6c1a4e425e0d1cf47570 --- /dev/null +++ b/code/UI/CalendarViewSwitch/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/code/UI/CalendarViewSwitch/oh_modules/@ohos/hypium/src/main/module/assert/assertFail.js b/code/UI/CalendarViewSwitch/oh_modules/@ohos/hypium/src/main/module/assert/assertFail.js new file mode 100644 index 0000000000000000000000000000000000000000..8ab4ac5caef712c75c4eac49dfbbb91d33669d9a --- /dev/null +++ b/code/UI/CalendarViewSwitch/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/code/UI/CalendarViewSwitch/oh_modules/@ohos/hypium/src/main/module/assert/assertFalse.js b/code/UI/CalendarViewSwitch/oh_modules/@ohos/hypium/src/main/module/assert/assertFalse.js new file mode 100644 index 0000000000000000000000000000000000000000..c5008e94f4b2ce13ed35b604811793c76b542347 --- /dev/null +++ b/code/UI/CalendarViewSwitch/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/code/UI/CalendarViewSwitch/oh_modules/@ohos/hypium/src/main/module/assert/assertInstanceOf.js b/code/UI/CalendarViewSwitch/oh_modules/@ohos/hypium/src/main/module/assert/assertInstanceOf.js new file mode 100644 index 0000000000000000000000000000000000000000..1e11b93f7251c67f5455c5007cd7be268aa53b32 --- /dev/null +++ b/code/UI/CalendarViewSwitch/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/code/UI/CalendarViewSwitch/oh_modules/@ohos/hypium/src/main/module/assert/assertLarger.js b/code/UI/CalendarViewSwitch/oh_modules/@ohos/hypium/src/main/module/assert/assertLarger.js new file mode 100644 index 0000000000000000000000000000000000000000..a74f4a8cedaf3add9c2dc2d3799081a83198732f --- /dev/null +++ b/code/UI/CalendarViewSwitch/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/code/UI/CalendarViewSwitch/oh_modules/@ohos/hypium/src/main/module/assert/assertLargerOrEqual.js b/code/UI/CalendarViewSwitch/oh_modules/@ohos/hypium/src/main/module/assert/assertLargerOrEqual.js new file mode 100644 index 0000000000000000000000000000000000000000..e847e6c217364b7f69c173c66fb98d10efc45ef1 --- /dev/null +++ b/code/UI/CalendarViewSwitch/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/code/UI/CalendarViewSwitch/oh_modules/@ohos/hypium/src/main/module/assert/assertLess.js b/code/UI/CalendarViewSwitch/oh_modules/@ohos/hypium/src/main/module/assert/assertLess.js new file mode 100644 index 0000000000000000000000000000000000000000..17e84b0abaeb20804048a5a15c19e0603634846d --- /dev/null +++ b/code/UI/CalendarViewSwitch/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/code/UI/CalendarViewSwitch/oh_modules/@ohos/hypium/src/main/module/assert/assertLessOrEqual.js b/code/UI/CalendarViewSwitch/oh_modules/@ohos/hypium/src/main/module/assert/assertLessOrEqual.js new file mode 100644 index 0000000000000000000000000000000000000000..f754f97ffa9d24e7852efe2423a1dd35d448af82 --- /dev/null +++ b/code/UI/CalendarViewSwitch/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/code/UI/CalendarViewSwitch/oh_modules/@ohos/hypium/src/main/module/assert/assertNaN.js b/code/UI/CalendarViewSwitch/oh_modules/@ohos/hypium/src/main/module/assert/assertNaN.js new file mode 100644 index 0000000000000000000000000000000000000000..8d45d6a93b86c5ed325a68b32ff014835993a58e --- /dev/null +++ b/code/UI/CalendarViewSwitch/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/code/UI/CalendarViewSwitch/oh_modules/@ohos/hypium/src/main/module/assert/assertNegUnlimited.js b/code/UI/CalendarViewSwitch/oh_modules/@ohos/hypium/src/main/module/assert/assertNegUnlimited.js new file mode 100644 index 0000000000000000000000000000000000000000..ceac555afc826e057970e6cfe9c73b322c672aa2 --- /dev/null +++ b/code/UI/CalendarViewSwitch/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/code/UI/CalendarViewSwitch/oh_modules/@ohos/hypium/src/main/module/assert/assertNull.js b/code/UI/CalendarViewSwitch/oh_modules/@ohos/hypium/src/main/module/assert/assertNull.js new file mode 100644 index 0000000000000000000000000000000000000000..53a7bad827323a98d3302a4e7eea679551b459c5 --- /dev/null +++ b/code/UI/CalendarViewSwitch/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/code/UI/CalendarViewSwitch/oh_modules/@ohos/hypium/src/main/module/assert/assertPosUnlimited.js b/code/UI/CalendarViewSwitch/oh_modules/@ohos/hypium/src/main/module/assert/assertPosUnlimited.js new file mode 100644 index 0000000000000000000000000000000000000000..6e68c0e2b6c499f4dc3dd56c13e9ea1073a3c54c --- /dev/null +++ b/code/UI/CalendarViewSwitch/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; diff --git a/code/UI/CalendarViewSwitch/oh_modules/@ohos/hypium/src/main/module/assert/assertPromiseIsPending.js b/code/UI/CalendarViewSwitch/oh_modules/@ohos/hypium/src/main/module/assert/assertPromiseIsPending.js new file mode 100644 index 0000000000000000000000000000000000000000..7e2ca2ce14d50c39554fc1157d6d4eb9329d5c39 --- /dev/null +++ b/code/UI/CalendarViewSwitch/oh_modules/@ohos/hypium/src/main/module/assert/assertPromiseIsPending.js @@ -0,0 +1,42 @@ +/* + * 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 isPromiseLike from './isPromiseLike'; + +function assertPromiseIsPending(actualPromise) { + if (!isPromiseLike(actualPromise)) { + return Promise.reject().then(function () { + }, function () { + return {pass: false, message: 'Expected not be called on a promise.'}; + }); + } + const helper = {}; + return Promise.race([actualPromise, Promise.resolve(helper)]).then( + function (got) { + return helper === got ? {pass: true, message: 'actualValue is isPending'} + : { + pass: false, + message: 'expect isPending, actualValue is resolve' + }; + }, + function () { + return { + pass: false + , message: 'expect isPending, actualValue is reject' + }; + }); +} + +export default assertPromiseIsPending; \ No newline at end of file diff --git a/code/UI/CalendarViewSwitch/oh_modules/@ohos/hypium/src/main/module/assert/assertPromiseIsRejected.js b/code/UI/CalendarViewSwitch/oh_modules/@ohos/hypium/src/main/module/assert/assertPromiseIsRejected.js new file mode 100644 index 0000000000000000000000000000000000000000..380075a369a84d6856e7f2db366f704e04302a8d --- /dev/null +++ b/code/UI/CalendarViewSwitch/oh_modules/@ohos/hypium/src/main/module/assert/assertPromiseIsRejected.js @@ -0,0 +1,38 @@ +/* + * 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 isPromiseLike from './isPromiseLike'; + +function assertPromiseIsRejected(actualPromise) { + if (!isPromiseLike(actualPromise)) { + return Promise.reject().then(function () { + }, function () { + return {pass: false, message: 'Expected not be called on a promise.'}; + }); + } + return actualPromise.then( + function (got) { + return { + pass: false, + message: 'expect isRejected, but actualValue is resolve' + }; + }, + function () { + return {pass: true, message: 'actualValue is isRejected'}; + } + ); +} + +export default assertPromiseIsRejected; \ No newline at end of file diff --git a/code/UI/CalendarViewSwitch/oh_modules/@ohos/hypium/src/main/module/assert/assertPromiseIsRejectedWith.js b/code/UI/CalendarViewSwitch/oh_modules/@ohos/hypium/src/main/module/assert/assertPromiseIsRejectedWith.js new file mode 100644 index 0000000000000000000000000000000000000000..8179589d5580f9c305d2200b4b197d64ac9d53ae --- /dev/null +++ b/code/UI/CalendarViewSwitch/oh_modules/@ohos/hypium/src/main/module/assert/assertPromiseIsRejectedWith.js @@ -0,0 +1,55 @@ +/* + * 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 isPromiseLike from './isPromiseLike'; + +function assertPromiseIsRejectedWith(actualPromise, expectedValue) { + + if (!isPromiseLike(actualPromise)) { + return Promise.reject().then(function () { + }, function () { + return {pass: false, message: 'Expected not be called on a promise.'}; + }); + } + + function tips(passed) { + return ('Expected a promise ' + (passed ? 'not ' : '') + + 'to be rejected with ' + JSON.stringify(expectedValue[0])); + } + + return actualPromise.then( + function (got) { + return { + pass: false, + message: tips(false) + ' but actualValue is resolve' + }; + }, + function (actualValue) { + if (JSON.stringify(actualValue) == JSON.stringify(expectedValue[0])) { + return { + pass: true, + message: 'actualValue was rejected with ' + JSON.stringify(actualValue) + '.' + }; + } else { + return { + pass: false, + message: tips(false) + ' but it was rejected with ' + JSON.stringify(actualValue) + '.' + }; + } + } + ); +} + +export default assertPromiseIsRejectedWith; \ No newline at end of file diff --git a/code/UI/CalendarViewSwitch/oh_modules/@ohos/hypium/src/main/module/assert/assertPromiseIsRejectedWithError.js b/code/UI/CalendarViewSwitch/oh_modules/@ohos/hypium/src/main/module/assert/assertPromiseIsRejectedWithError.js new file mode 100644 index 0000000000000000000000000000000000000000..291af8e5032b7bcd9bcb3e996a39a4fa8ba23157 --- /dev/null +++ b/code/UI/CalendarViewSwitch/oh_modules/@ohos/hypium/src/main/module/assert/assertPromiseIsRejectedWithError.js @@ -0,0 +1,76 @@ +/* + * 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 isPromiseLike from './isPromiseLike'; + +function assertPromiseIsRejectedWithError(actualPromise, expectedValue) { + if (!isPromiseLike(actualPromise)) { + return Promise.reject().then(function () { + }, function () { + return {pass: false, message: 'Expected not be called on a promise.'}; + }); + } + return actualPromise.then( + function (got) { + return { + pass: false, + message: 'Expected a promise to be rejected but actualValue is resolve' + }; + }, + function (actualValue) { + return matchError(actualValue, expectedValue); + } + ); + +} + +function matchError(actualValue, expectedValue) { + if (expectedValue.length == 1 && typeof expectedValue[0] === 'function') { + if (expectedValue[0].name === actualValue.__proto__.name) { + return {pass: true, message: 'actual error type is ' + actualValue.name + '.'}; + } + return {pass: false, message: `except error type is ${expectedValue[0].name},but actual is ${actualValue.name}.`}; + } + + if (expectedValue.length == 1 && typeof expectedValue[0] === 'string') { + if (expectedValue[0] === actualValue.message) { + return {pass: true, message: `actual error message is ${actualValue.message}.`}; + } + return {pass: false, message: `except error message ${expectedValue[0]},but actual is ${actualValue.message}.`}; + } + + if (expectedValue.length == 1) { + return {pass: false, message: 'When only one parameter, it should be error type or error message.'}; + } + + if (expectedValue.length == 2 && typeof expectedValue[0] === 'function' && expectedValue[0].name === actualValue.name) { + if (typeof expectedValue[1] === 'string' && actualValue.message === expectedValue[1]) { + return {pass: true, message: 'actual error message is ' + actualValue.message + '.'}; + } + return {pass: false, message: `except error message is ${expectedValue[1]},but actual is ${actualValue.message}.`}; + } + + if (expectedValue.length == 2 && typeof expectedValue[0] === 'function' && expectedValue[0].name !== actualValue.name) { + if (typeof expectedValue[1] === 'string' && actualValue.message === expectedValue[1]) { + return {pass: false, message: `except error type is ${expectedValue[0].name},but actual is ${actualValue.name}.`}; + } + return {pass: false, message: 'except error type and message are incorrect.'}; + } + if (expectedValue.length > 2) { + return {pass: false, message: 'Up to two parameters are supported.'}; + } +} + +export default assertPromiseIsRejectedWithError; \ No newline at end of file diff --git a/code/UI/CalendarViewSwitch/oh_modules/@ohos/hypium/src/main/module/assert/assertPromiseIsResolved.js b/code/UI/CalendarViewSwitch/oh_modules/@ohos/hypium/src/main/module/assert/assertPromiseIsResolved.js new file mode 100644 index 0000000000000000000000000000000000000000..86f559c32873f27b95d635452d760029de0ed657 --- /dev/null +++ b/code/UI/CalendarViewSwitch/oh_modules/@ohos/hypium/src/main/module/assert/assertPromiseIsResolved.js @@ -0,0 +1,40 @@ +/* + * 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 isPromiseLike from './isPromiseLike'; + +function assertPromiseIsResolved(actualPromise) { + if (!isPromiseLike(actualPromise)) { + return Promise.reject().then(function () { + }, function () { + return {pass: false, message: 'Expected not be called on a promise.'}; + }); + } + + return actualPromise.then( + function (got) { + return {pass: true, message: 'actualValue is isResolved'}; + }, + function (rej) { + return { + pass: false, + message: 'Expected a promise to be resolved but it was ' + + 'rejected with ' + JSON.stringify(rej) + '.' + }; + } + ); +} + +export default assertPromiseIsResolved; \ No newline at end of file diff --git a/code/UI/CalendarViewSwitch/oh_modules/@ohos/hypium/src/main/module/assert/assertPromiseIsResolvedWith.js b/code/UI/CalendarViewSwitch/oh_modules/@ohos/hypium/src/main/module/assert/assertPromiseIsResolvedWith.js new file mode 100644 index 0000000000000000000000000000000000000000..c6f0ef68fde5b04a589a9fa3c6e2ab2b39acf4d3 --- /dev/null +++ b/code/UI/CalendarViewSwitch/oh_modules/@ohos/hypium/src/main/module/assert/assertPromiseIsResolvedWith.js @@ -0,0 +1,55 @@ +/* + * 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 isPromiseLike from './isPromiseLike'; + +function assertPromiseIsResolvedWith(actualPromise, expectedValue) { + if (!isPromiseLike(actualPromise)) { + return Promise.reject().then(function () { + }, function () { + return {pass: false, message: 'Expected not be called on a promise.'}; + }); + } + + function tips(passed) { + return ( + 'Expected a promise ' + (passed ? 'not ' : '') + + 'to be resolved with ' + JSON.stringify(expectedValue[0])); + } + + return actualPromise.then( + function (got) { + if (JSON.stringify(got) == JSON.stringify(expectedValue[0])) { + return { + pass: true, + message: 'actualValue was resolved with ' + JSON.stringify(got) + '.' + }; + } + return { + pass: false, + message: tips(false) + ' but it was resolved with ' + + JSON.stringify(got) + '.' + }; + }, + function (rej) { + return { + pass: false, + message: tips(false) + ' but it was rejected with ' + JSON.stringify(rej) + '.' + }; + } + ); +} + +export default assertPromiseIsResolvedWith; \ No newline at end of file diff --git a/code/UI/CalendarViewSwitch/oh_modules/@ohos/hypium/src/main/module/assert/assertThrowError.js b/code/UI/CalendarViewSwitch/oh_modules/@ohos/hypium/src/main/module/assert/assertThrowError.js new file mode 100644 index 0000000000000000000000000000000000000000..c4544a7f825bcecd1a07d5e98dd9a7b99d237278 --- /dev/null +++ b/code/UI/CalendarViewSwitch/oh_modules/@ohos/hypium/src/main/module/assert/assertThrowError.js @@ -0,0 +1,48 @@ +/* + * 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. + */ + +function assertThrowError(actualValue, expected) { + let result = false; + let message = ''; + let err; + if (typeof actualValue !== 'function') { + throw new Error('actualValue is not a function'); + } + try { + actualValue(); + return { + pass: result, + message: ' An error is not thrown while it is expected!' + }; + } catch (e) { + err = e; + } + if (err instanceof Error) { + let type = typeof expected[0]; + if (type === 'function') { + result = err.constructor.name === expected[0].name; + message = 'expected throw failed , ' + err.constructor.name + ' is not ' + expected[0].name; + }else if(type === 'string'){ + result = err.message.includes(expected[0]); + message = 'expected throw failed , ' + err.message + ' is not ' + expected[0]; + } + } + return { + pass: result, + message: message + }; +} + +export default assertThrowError; diff --git a/code/UI/CalendarViewSwitch/oh_modules/@ohos/hypium/src/main/module/assert/assertUndefined.js b/code/UI/CalendarViewSwitch/oh_modules/@ohos/hypium/src/main/module/assert/assertUndefined.js new file mode 100644 index 0000000000000000000000000000000000000000..61f092d715dd1630297518b59ff13ef0940991e1 --- /dev/null +++ b/code/UI/CalendarViewSwitch/oh_modules/@ohos/hypium/src/main/module/assert/assertUndefined.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 assertUndefined(actualValue) { + return { + pass: undefined === (actualValue), + message: 'expect Undefined, actualValue is ' + (actualValue) + }; +} + +export default assertUndefined; diff --git a/code/UI/CalendarViewSwitch/oh_modules/@ohos/hypium/src/main/module/assert/deepEquals/DeepTypeUtils.js b/code/UI/CalendarViewSwitch/oh_modules/@ohos/hypium/src/main/module/assert/deepEquals/DeepTypeUtils.js new file mode 100644 index 0000000000000000000000000000000000000000..916824d9cb77a75d1fb35bc3500d7598bfc73e80 --- /dev/null +++ b/code/UI/CalendarViewSwitch/oh_modules/@ohos/hypium/src/main/module/assert/deepEquals/DeepTypeUtils.js @@ -0,0 +1,139 @@ +/* + * Copyright (c) 2022-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 DeepTypeUtils { + static getType(value) { + return Object.prototype.toString.apply(value); + } + static isA(typeName, value) { + return this.getType(value) === '[object ' + typeName + ']'; + } + static isAsymmetricEqualityTester(obj) { + return obj ? this.isA('Function', obj.asymmetricMatch) : false; + } + + /** + * 是否是function + * @param value + */ + static isFunction(value) { + return this.isA('Function', value); + } + + /** + * 是否是undefined + * @param obj + */ + static isUndefined(obj) { + return obj === void 0; + } + + /** + * 是否是Node + * @param obj + */ + static isDomNode(obj) { + return obj !== null && + typeof obj === 'object' && + typeof obj.nodeType === 'number' && + typeof obj.nodeName === 'string'; + } + + /** + * 是否是promise对象 + * @param obj + */ + static isPromise(obj) { + return !!obj && obj.constructor === Promise; + }; + /** + * 是否是map对象 + * @param obj + */ + static isMap(obj) { + return ( + obj !== null && + typeof obj !== 'undefined' && + obj.constructor === Map + ); + } + + /** + * 是否是set对象 + * @param obj 对象 + */ + static isSet(obj) { + return ( + obj !== null && + typeof obj !== 'undefined' && + obj.constructor === Set + ); + } + + /** + * 对象是否有key属性 + * @param obj 对象 + * @param key 对象属性名称 + */ + static has(obj, key) { + return Object.prototype.hasOwnProperty.call(obj, key); + } + + /** + * 获取对象的自有属性 + * @param obj 对象 + * @param isArray 是否是数组,[object Array] + */ + static keys(obj, isArray) { + const extraKeys = []; + // 获取对象所有属性 + const allKeys = this.getAllKeys(obj); + if (!isArray) { + return allKeys; + } + if (allKeys.length === 0) { + return allKeys; + } + for (const k of allKeys) { + if (typeof k === 'symbol' || !/^[0-9]+$/.test(k)) { + extraKeys.push(k); + } + } + return extraKeys; + } + + /** + * 获取obj对象的所有属性 + * @param obj obj对象 + */ + static getAllKeys(obj) { + const keys = []; + for (let key in obj) { + if (this.has(obj, key)) { + keys.push(key); + } + } + const symbols = Object.getOwnPropertySymbols(obj); + for (const sym of symbols) { + // obj.propertyIsEnumerable(sym) + if (Object.prototype.propertyIsEnumerable.call(obj, sym)) { + keys.push(sym); + } + } + return keys; + } + +} +export default DeepTypeUtils; \ No newline at end of file diff --git a/code/UI/CalendarViewSwitch/oh_modules/@ohos/hypium/src/main/module/assert/deepEquals/assertDeepEquals.js b/code/UI/CalendarViewSwitch/oh_modules/@ohos/hypium/src/main/module/assert/deepEquals/assertDeepEquals.js new file mode 100644 index 0000000000000000000000000000000000000000..60de33f7e1afdcfaf205c8c56484ef33dfda8160 --- /dev/null +++ b/code/UI/CalendarViewSwitch/oh_modules/@ohos/hypium/src/main/module/assert/deepEquals/assertDeepEquals.js @@ -0,0 +1,341 @@ +/* + * Copyright (c) 2022-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 DeepTypeUtils from './DeepTypeUtils'; +function assertDeepEquals(actualValue, expected) { + let result = eq(actualValue, expected[0]) + let msg = logMsg(actualValue, expected[0]); + return { + pass: result, + message: msg + }; +} + +/** + * 获取失败显示日志 + * @param actualValue 实际对象 + * @param expected 期待比较对象 + */ +function logMsg(actualValue, expected) { + // 获取a的对象名称 + const aClassName = Object.prototype.toString.call(actualValue); + const bClassName = Object.prototype.toString.call(expected); + let actualMsg; + let expectMsg; + if (aClassName == '[object Function]') { + actualMsg = 'actualValue Function'; + } else if (aClassName == '[object Promise]') { + actualMsg = 'actualValue Promise'; + } else if (aClassName == '[object Set]' || aClassName == '[object Map]') { + actualMsg = JSON.stringify(Array.from(actualValue)); + } else if (aClassName == '[object RegExp]') { + actualMsg = JSON.stringify(actualValue.source.replace('\\','')); + } else if (aClassName == '[object BigInt]') { + actualMsg = actualValue; + } else if (aClassName == '[object Error]') { + actualMsg = actualValue.message; + } else if (aClassName == '[object ArrayBuffer]') { + actualMsg = actualValue.byteLength; + } + else { + actualMsg = JSON.stringify(actualValue); + } + if (bClassName == '[object Function]') { + expectMsg = 'expected Function'; + } else if (bClassName == '[object Promise]') { + expectMsg = 'expected Promise'; + } else if (bClassName == '[object Set]' || bClassName == '[object Map]') { + expectMsg = JSON.stringify(Array.from(expected)); + } else if (bClassName == '[object RegExp]') { + expectMsg = JSON.stringify(expected.source.replace('\\','')); + } else if (bClassName == '[object BigInt]') { + expectMsg = expected; + } else if (bClassName == '[object Error]') { + expectMsg = expected.message; + } else if (bClassName == '[object ArrayBuffer]') { + expectMsg = expected.byteLength; + } + else { + expectMsg = JSON.stringify(expected); + } + return actualMsg + ' is not deep equal ' + expectMsg; +} + +function eq(a, b) { + let result = true; + + if (a === b) { + result = a !== 0 || 1 / a === 1 / b; + return result; + } + + if (a === null || b === null) { + result = a === b; + return result; + } + // 获取a的对象名称 + const aClassName = Object.prototype.toString.call(a); + const bClassName = Object.prototype.toString.call(b); + // 不同类型不同对象 + if (aClassName !== bClassName) { + return false; + } + if (aClassName === '[object String]' || aClassName === '[object Number]' || aClassName === '[object Date]' || + aClassName === '[object Boolean]' || aClassName === '[object ArrayBuffer]' || + aClassName === '[object RegExp]' || aClassName === '[object Error]') { + result = isEqualSampleObj(a, b); + return result; + } + + if (typeof a !== 'object' || typeof b !== 'object') { + return false; + } + + if (DeepTypeUtils.isDomNode(a) || DeepTypeUtils.isPromise(a) || DeepTypeUtils.isFunction(a)) { + result = isEqualNodeOrPromiseOrFunction(a, b); + return result; + } + + if (aClassName === '[object Array]' || aClassName === '[object Map]' || aClassName === '[object Set]') { + result = isEqualCollection(a, b); + return result; + } + + result = isEqualObj(a, b); + return result; +} + +function isEqualNodeOrPromiseOrFunction(a, b) { + let equalNodeOrPromiseOrFunction = true; + if (DeepTypeUtils.isDomNode(a) && DeepTypeUtils.isDomNode(b)) { + const aIsDomNode = DeepTypeUtils.isDomNode(a); + const bIsDomNode = DeepTypeUtils.isDomNode(b); + if (aIsDomNode && bIsDomNode) { + // At first try to use DOM3 method isEqualNode + equalNodeOrPromiseOrFunction = a.isEqualNode(b); + return equalNodeOrPromiseOrFunction; + } + if (aIsDomNode || bIsDomNode) { + equalNodeOrPromiseOrFunction = false; + return false; + } + } + + if (DeepTypeUtils.isPromise(a) && DeepTypeUtils.isPromise(b)) { + const aIsPromise = DeepTypeUtils.isPromise(a); + const bIsPromise = DeepTypeUtils.isPromise(b); + // 俩个Promise对象 + if (aIsPromise && bIsPromise) { + equalNodeOrPromiseOrFunction = a === b; + return a === b; + } + } + if (DeepTypeUtils.isFunction(a) && DeepTypeUtils.isFunction(b)) { + // 俩个函数对象 + const aCtor = a.constructor, + bCtor = b.constructor; + if ( + aCtor !== bCtor && + DeepTypeUtils.isFunction(aCtor) && + DeepTypeUtils.isFunction(bCtor) && + a instanceof aCtor && + b instanceof bCtor && + !(aCtor instanceof aCtor && bCtor instanceof bCtor) + ) { + equalNodeOrPromiseOrFunction = false; + return false; + } + } + return equalNodeOrPromiseOrFunction; +} + +function isEqualCollection(a, b) { + let equalCollection = true; + // 获取a的对象名称 + const aClassName = Object.prototype.toString.call(a); + const bClassName = Object.prototype.toString.call(b); + // 都是数组 + if (aClassName === '[object Array]') { + equalCollection = isEqualArray(a, b); + return equalCollection; + } + + // 都是Map + if (DeepTypeUtils.isMap(a) && DeepTypeUtils.isMap(b)) { + equalCollection = isEqualMap(a, b); + return equalCollection; + } + + // 都是Set + if (DeepTypeUtils.isSet(a) && DeepTypeUtils.isSet(b)) { + equalCollection = isEqualSet(a, b); + return equalCollection; + } + + return true; +} + +function isEqualSampleObj(a, b) { + let equalSampleObj = true; + const aClassName = Object.prototype.toString.call(a); + const bClassName = Object.prototype.toString.call(b); + // 俩个string对象 + if (aClassName === '[object String]') { + equalSampleObj = a === String(b); + return equalSampleObj; + } + // 俩个Number对象 + if (aClassName === '[object Number]') { + equalSampleObj = a !== +a ? b !== +b : a === 0 && b === 0 ? 1 / a === 1 / b : a === +b; + return equalSampleObj; + } + + // 俩个Date对象/ boolean对象 + if (aClassName === '[object Date]' || aClassName === '[object Boolean]') { + equalSampleObj = +a === +b; + return equalSampleObj; + } + + // 俩个ArrayBuffer + if (aClassName === '[object ArrayBuffer]') { + equalSampleObj = eq(new Uint8Array(a), new Uint8Array(b)); + return equalSampleObj; + } + + // 正则表达式 + if (aClassName === '[object RegExp]') { + return ( + a.source === b.source && + a.global === b.global && + a.multiline === b.multiline && + a.ignoreCase === b.ignoreCase + ); + } + + if (a instanceof Error && b instanceof Error) { + equalSampleObj = a.message === b.message; + return equalSampleObj; + } + + return equalSampleObj; +} + +function isEqualObj(a, b) { + let equalObj = true; + const aClassName = Object.prototype.toString.call(a); + const bClassName = Object.prototype.toString.call(b); + const aKeys = DeepTypeUtils.keys(a, aClassName === '[object Array]'); + let size = aKeys.length; + + // 俩个对象属性长度不一致, 俩对象不相同 + if (DeepTypeUtils.keys(b, bClassName === '[object Array]').length !== size) { + return false; + } + + // 俩对象属性数量相同, 递归比较每个属性值得值 + for (const key of aKeys) { + // b 没有 key 属性 + if (!DeepTypeUtils.has(b, key)) { + equalObj = false; + continue; + } + if (!eq(a[key], b[key])) { + equalObj = false; + } + } + return equalObj; +} + +function isEqualArray(a, b) { + let equalArray = true; + const aLength = a.length; + const bLength = b.length; + if (aLength !== bLength) { + // 数组长度不同,不是同一个对象 + return false; + } + for (let i = 0; i < aLength || i < bLength; i++) { + // 递归每一个元素是否相同 + equalArray = eq(i < aLength ? a[i] : void 0, i < bLength ? b[i] : void 0) && equalArray; + } + return equalArray; +} + +function isEqualMap(a, b) { + let equalMap = true; + if (a.size !== b.size) { + return false; + } + const keysA = []; + const keysB = []; + a.forEach(function(valueA, keyA) { + keysA.push(keyA); + }); + b.forEach(function(valueB, keyB) { + keysB.push(keyB); + }); + const mapKeys = [keysA, keysB]; + const cmpKeys = [keysB, keysA]; + for (let i = 0; equalMap && i < mapKeys.length; i++) { + const mapIter = mapKeys[i]; + const cmpIter = cmpKeys[i]; + + for (let j = 0; equalMap && j < mapIter.length; j++) { + const mapKey = mapIter[j]; + const cmpKey = cmpIter[j]; + const mapValueA = a.get(mapKey); + let mapValueB; + if (eq(mapKey, cmpKey)) { + mapValueB = b.get(cmpKey); + } else { + mapValueB = b.get(mapKey); + } + equalMap = eq(mapValueA, mapValueB); + } + } + return equalMap; +} + +function isEqualSet(a, b) { + let equalSet = true; + if (a.size !== b.size) { + return false; + } + const valuesA = []; + a.forEach(function(valueA) { + valuesA.push(valueA); + }); + const valuesB = []; + b.forEach(function(valueB) { + valuesB.push(valueB); + }); + const setPairs = [[valuesA, valuesB], [valuesB, valuesA]]; + for (let i = 0; equalSet && i < setPairs.length; i++) { + const baseValues = setPairs[i][0]; + const otherValues = setPairs[i][1]; + for (const baseValue of baseValues) { + let found = false; + for (let j = 0; !found && j < otherValues.length; j++) { + const otherValue = otherValues[j]; + // 深度比较对象 + found = eq(baseValue, otherValue); + } + equalSet = equalSet && found; + } + } + return equalSet; +} + +export default assertDeepEquals; diff --git a/code/UI/CalendarViewSwitch/oh_modules/@ohos/hypium/src/main/module/assert/isPromiseLike.js b/code/UI/CalendarViewSwitch/oh_modules/@ohos/hypium/src/main/module/assert/isPromiseLike.js new file mode 100644 index 0000000000000000000000000000000000000000..015ab19a2a0c4872d7cb490b61f8e1dd6a8ac90b --- /dev/null +++ b/code/UI/CalendarViewSwitch/oh_modules/@ohos/hypium/src/main/module/assert/isPromiseLike.js @@ -0,0 +1,32 @@ +/* + * 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 isPromiseLike(obj) { + return !!obj && isFunction_(obj.then); +} + +function isFunction_(value) { + return isA_('Function', value); +} + +function isA_(typeName, value) { + return getType_(value) === '[object ' + typeName + ']'; +} + +function getType_(value) { + return Object.prototype.toString.apply(value); +} + +export default isPromiseLike; diff --git a/code/UI/CalendarViewSwitch/oh_modules/@ohos/hypium/src/main/module/config/DataDriver.js b/code/UI/CalendarViewSwitch/oh_modules/@ohos/hypium/src/main/module/config/DataDriver.js new file mode 100644 index 0000000000000000000000000000000000000000..639dffc9cdb912f1f33a6ccb61868c9ed7c695bf --- /dev/null +++ b/code/UI/CalendarViewSwitch/oh_modules/@ohos/hypium/src/main/module/config/DataDriver.js @@ -0,0 +1,116 @@ +/* + * 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. + */ + +const SUITES_KEY = 'suites'; +const SPECS_KEY = 'items'; +const DESCRIBE_KEY = 'describe'; +const IT_KEY = 'it'; +const PARAMS_KEY = 'params'; +const STRESS_KEY = 'stress'; + +class ObjectUtils { + static get(object, name, defaultValue) { + let result = defaultValue; + for (const key in object) { + if (key === name) { + return object[key]; + } + } + return result; + } + + static has(object, key) { + return Object.prototype.hasOwnProperty.call(object, key); + } +} + +class DataDriver { + constructor(attr) { + this.id = 'dataDriver'; + this.data = attr.data || {}; + } + + init(coreContext) { + this.coreContext = coreContext; + this.suiteService = this.coreContext.getDefaultService('suite'); + this.specService = this.coreContext.getDefaultService('spec'); + } + + getSpecParams() { + let specParams = []; + let suiteDesc = this.suiteService.getCurrentRunningSuite().description; + let specDesc = this.specService.getCurrentRunningSpec().description; + let suites = ObjectUtils.get(this.data, SUITES_KEY, []); + for (const suiteItem of suites) { + let describeValue = ObjectUtils.get(suiteItem, DESCRIBE_KEY, ''); + if (ObjectUtils.has(suiteItem, DESCRIBE_KEY) && (typeof describeValue === 'object') && describeValue.constructor === Array && describeValue.includes(suiteDesc)) { + let specs = ObjectUtils.get(suiteItem, SPECS_KEY, []); + for (const specItem of specs) { + if (ObjectUtils.has(specItem, IT_KEY) && ObjectUtils.get(specItem, IT_KEY) === specDesc) { + return ObjectUtils.get(specItem, PARAMS_KEY, specParams); + } + } + } + } + return specParams; + } + + getSuiteParams() { + let suiteParams = {}; + let suiteDesc = this.suiteService.getCurrentRunningSuite().description; + let suites = ObjectUtils.get(this.data, SUITES_KEY, []); + for (const suiteItem of suites) { + let describeValue = ObjectUtils.get(suiteItem, DESCRIBE_KEY, []); + if (ObjectUtils.has(suiteItem, DESCRIBE_KEY) && (typeof describeValue === 'object') && describeValue.constructor === Array && describeValue.includes(suiteDesc)) { + suiteParams = Object.assign({}, suiteParams, ObjectUtils.get(suiteItem, PARAMS_KEY, suiteParams)); + } + } + return suiteParams; + } + + getSpecStress(specDesc) { + let stress = 1; + let suiteDesc = this.suiteService.getCurrentRunningSuite().description; + let suites = ObjectUtils.get(this.data, SUITES_KEY, []); + for (const suiteItem of suites) { + let describeValue = ObjectUtils.get(suiteItem, DESCRIBE_KEY, ''); + if (ObjectUtils.has(suiteItem, DESCRIBE_KEY) && (typeof describeValue === 'object') && describeValue.constructor === Array && describeValue.includes(suiteDesc)) { + let specs = ObjectUtils.get(suiteItem, SPECS_KEY, []); + for (const specItem of specs) { + if (ObjectUtils.has(specItem, IT_KEY) && ObjectUtils.get(specItem, IT_KEY) === specDesc) { + let tempStress = ObjectUtils.get(specItem, STRESS_KEY, stress); + return (Number.isInteger(tempStress) && tempStress >= 1) ? tempStress : stress; + } + } + } + } + return stress; + } + + getSuiteStress(suiteDesc) { + let stress = 1; + let suites = ObjectUtils.get(this.data, SUITES_KEY, []); + for (const suiteItem of suites) { + let describeValue = ObjectUtils.get(suiteItem, DESCRIBE_KEY, []); + if (ObjectUtils.has(suiteItem, DESCRIBE_KEY) && (typeof describeValue === 'object') && describeValue.constructor === Array && describeValue.includes(suiteDesc)) { + let tempStress = ObjectUtils.get(suiteItem, STRESS_KEY, stress); + return (Number.isInteger(tempStress) && tempStress >= 1) ? tempStress : stress; + } + } + return stress; + } +} + +export default DataDriver; diff --git a/code/UI/CalendarViewSwitch/oh_modules/@ohos/hypium/src/main/module/config/Filter.js b/code/UI/CalendarViewSwitch/oh_modules/@ohos/hypium/src/main/module/config/Filter.js new file mode 100644 index 0000000000000000000000000000000000000000..b07e6c681bfff618cc9f5ca92ec85d1d9880202d --- /dev/null +++ b/code/UI/CalendarViewSwitch/oh_modules/@ohos/hypium/src/main/module/config/Filter.js @@ -0,0 +1,143 @@ +/* + * 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 { LEVEL, SIZE, TESTTYPE } from '../../Constant'; + +class ClassFilter { + constructor(suiteName, itName, params) { + this.suiteName = suiteName; + this.itName = itName; + this.params = params; + } + + filterSuite() { + return !this.params.split(',').map(item => item.split('#')[0]).map(item => item == this.suiteName).reduce((pre, cur) => pre || cur, false); + } + + filterIt() { + let classArray = this.params.split(',') || []; + let suiteFilterResult = classArray.filter(item => !item.includes('#')).map(item => item == this.suiteName).reduce((pre, cur) => pre || cur, false); + let itFilterResult = classArray.filter(item => item.includes('#')).map(item => item == (this.suiteName + '#' + this.itName)).reduce((pre, cur) => pre || cur, false); + return !(suiteFilterResult || itFilterResult); + } +} + +class NotClassFilter { + constructor(suiteName, itName, params) { + this.suiteName = suiteName; + this.itName = itName; + this.params = params; + } + + filterSuite() { + return this.params.split(',').map(item => item == this.suiteName).reduce((pre, cur) => pre || cur, false); + } + + filterIt() { + return this.params.split(',').some(item => item == (this.suiteName + '#' + this.itName)); + } +} + +class SuiteAndItNameFilter { + constructor(suiteName, itName, params) { + this.suiteName = suiteName; + this.itName = itName; + this.params = params; + } + + filterSuite() { + return !this.params.split(',').map(item => item == this.suiteName).reduce((pre, cur) => pre || cur, false); + } + + filterIt() { + return !this.params.split(',').map(item => item == this.itName).reduce((pre, cur) => pre || cur, false); + } +} + + +class TestTypesFilter { + constructor(suiteName, itName, fi, params) { + this.suiteName = suiteName; + this.itName = itName; + this.params = params; + this.fi = fi; + } + + filterIt() { + return !((this.params === (this.fi & this.params)) || this.fi === 0); + } +} + +class NestFilter { + filterNestName(targetSuiteArray, targetSpecArray, suiteStack, desc) { + let targetSuiteName = ''; + for (let key in suiteStack) { + targetSuiteName = targetSuiteName + '.' + suiteStack[key].description; + } + targetSuiteName = targetSuiteName.substring(2); + const targetSpecName = targetSuiteName + '#' + desc; + let isFilter = true; + if (targetSpecArray.includes(targetSpecName)) { + return false; + } + for (let index in targetSuiteArray) { + if (targetSuiteName.startsWith(targetSuiteArray[index])) { + return false; + } + } + return isFilter; + } + + filterNotClass(notClass, suiteStack, desc) { + let isFilterNotClass = false; + if (notClass != null) { + let notClassArray = notClass.split(','); + let targetSuiteName = ''; + for (let key in suiteStack) { + targetSuiteName = targetSuiteName + '.' + suiteStack[key].description; + } + targetSuiteName = targetSuiteName.substring(2); + const targetSpecName = targetSuiteName + '#' + desc; + if (notClassArray.includes(targetSpecName) || notClassArray.some(key => targetSpecName.startsWith(key))) { + isFilterNotClass = true; + } + } + return isFilterNotClass; + } + + filterLevelOrSizeOrTestType(level, size, testType, filter) { + let result = false; + if (filter === 0 || filter === '0') { + return result; + } + if (level == null && size == null && testType == null) { + return result; + } + if (level != null) { + let levelFilter = LEVEL[`${level}`]; + result = result || filter === levelFilter; + } + if (size != null) { + let sizeFilter = SIZE[`${size}`]; + result = result || filter === sizeFilter; + } + if (testType != null) { + let testTypeFilter = TESTTYPE[`${testType}`]; + result = result || filter === testTypeFilter; + } + return !result; + } +} +export { ClassFilter, NotClassFilter, SuiteAndItNameFilter, TestTypesFilter, NestFilter }; diff --git a/code/UI/CalendarViewSwitch/oh_modules/@ohos/hypium/src/main/module/config/configService.js b/code/UI/CalendarViewSwitch/oh_modules/@ohos/hypium/src/main/module/config/configService.js new file mode 100644 index 0000000000000000000000000000000000000000..17674d8cf7e2343bcb4a14ad47eb11cd03c15aac --- /dev/null +++ b/code/UI/CalendarViewSwitch/oh_modules/@ohos/hypium/src/main/module/config/configService.js @@ -0,0 +1,299 @@ +/* + * 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 { ClassFilter, NotClassFilter, SuiteAndItNameFilter, TestTypesFilter, NestFilter } from './Filter'; +import { TAG, TESTTYPE, LEVEL, SIZE, KEYSET } from '../../Constant'; +const STRESS_RULE = /^[1-9]\d*$/; + +class ConfigService { + constructor(attr) { + this.id = attr.id; + this.supportAsync = true; // 默认异步处理测试用例 + this.random = false; + this.filterValid = []; + this.filter = 0; + this.flag = false; + this.suite = null; + this.itName = null; + this.testType = null; + this.level = null; + this.size = null; + this.class = null; + this.notClass = null; + this.timeout = null; + // 遇错即停模式配置 + this.breakOnError = false; + // 压力测试配置 + this.stress = null; + this.skipMessage = false; + this.runSkipped = ''; + this.filterXdescribe = []; + } + + init(coreContext) { + this.coreContext = coreContext; + } + + isNormalInteger(str) { + const n = Math.floor(Number(str)); + return n !== Infinity && String(n) === String(str) && n >= 0; + } + + + getStress() { + if (this.stress === undefined || this.stress === '' || this.stress === null) { + return 1; + } + return !this.stress.match(STRESS_RULE) ? 1 : Number.parseInt(this.stress); + } + + basicParamValidCheck(params) { + let size = params.size; + if (size !== undefined && size !== '' && size !== null) { + let sizeArray = ['small', 'medium', 'large']; + if (sizeArray.indexOf(size) === -1) { + this.filterValid.push('size:' + size); + } + } + let level = params.level; + if (level !== undefined && level !== '' && level !== null) { + let levelArray = ['0', '1', '2', '3', '4']; + if (levelArray.indexOf(level) === -1) { + this.filterValid.push('level:' + level); + } + } + let testType = params.testType; + if (testType !== undefined && testType !== '' && testType !== null) { + let testTypeArray = ['function', 'performance', 'power', 'reliability', 'security', + 'global', 'compatibility', 'user', 'standard', 'safety', 'resilience']; + if (testTypeArray.indexOf(testType) === -1) { + this.filterValid.push('testType:' + testType); + } + } + } + + filterParamValidCheck(params) { + let timeout = params.timeout; + if (timeout !== undefined && timeout !== '' && timeout !== null) { + if (!this.isNormalInteger(timeout)) { + this.filterValid.push('timeout:' + timeout); + } + } + + let paramKeys = ['dryRun', 'random', 'breakOnError', 'coverage', 'skipMessage']; + for (const key of paramKeys) { + if (params[key] !== undefined && params[key] !== 'true' && params[key] !== 'false') { + this.filterValid.push(`${key}:${params[key]}`); + } + } + + // 压力测试参数验证,正整数 + if (params.stress !== undefined && params.stress !== '' && params.stress !== null) { + if (!params.stress.match(STRESS_RULE)) { + this.filterValid.push('stress:' + params.stress); + } + } + + let nameRule = /^[A-Za-z]{1}[\w#,.]*$/; + let paramClassKeys = ['class', 'notClass']; + for (const key of paramClassKeys) { + if (params[key] !== undefined && params[key] !== '' && params[key] !== null) { + let classArray = params[key].split(','); + classArray.forEach(item => !item.match(nameRule) ? this.filterValid.push(`${key}:${params[key]}`) : null); + } + } + } + + setConfig(params) { + this.basicParamValidCheck(params); + this.filterParamValidCheck(params); + try { + this.class = params.class; + this.notClass = params.notClass; + this.flag = params.flag || { flag: false }; + this.suite = params.suite; + this.itName = params.itName; + this.filter = params.filter; + this.testType = params.testType; + this.level = params.level; + this.size = params.size; + this.timeout = params.timeout; + this.dryRun = params.dryRun; + this.breakOnError = params.breakOnError; + this.random = params.random === 'true' ? true : false; + this.stress = params.stress; + this.coverage = params.coverage; + this.skipMessage = params.skipMessage; + this.runSkipped = params.runSkipped; + this.filterParam = { + testType: TESTTYPE, + level: LEVEL, + size: SIZE + }; + this.parseParams(); + } catch (err) { + console.info(`${TAG}setConfig error: ${err.message}`); + } + } + + parseParams() { + if (this.filter != null) { + return; + } + let testTypeFilter = 0; + let sizeFilter = 0; + let levelFilter = 0; + if (this.testType != null) { + testTypeFilter = this.testType.split(',') + .map(item => this.filterParam.testType[item] || 0) + .reduce((pre, cur) => pre | cur, 0); + } + if (this.level != null) { + levelFilter = this.level.split(',') + .map(item => this.filterParam.level[item] || 0) + .reduce((pre, cur) => pre | cur, 0); + } + if (this.size != null) { + sizeFilter = this.size.split(',') + .map(item => this.filterParam.size[item] || 0) + .reduce((pre, cur) => pre | cur, 0); + } + this.filter = testTypeFilter | sizeFilter | levelFilter; + console.info(`${TAG}filter params:${this.filter}`); + } + + isCurrentSuite(description) { + if (this.suite !== undefined && this.suite !== '' && this.suite !== null) { + let suiteArray = this.suite.split(','); + return suiteArray.indexOf(description) !== -1; + } + return false; + } + + filterSuite(currentSuiteName) { + let filterArray = []; + if (this.suite !== undefined && this.suite !== '' && this.suite !== null) { + filterArray.push(new SuiteAndItNameFilter(currentSuiteName, '', this.suite)); + } + if (this.class !== undefined && this.class !== '' && this.class !== null) { + filterArray.push(new ClassFilter(currentSuiteName, '', this.class)); + } + if (this.notClass !== undefined && this.notClass !== '' && this.notClass !== null) { + filterArray.push(new NotClassFilter(currentSuiteName, '', this.notClass)); + } + + let result = filterArray.map(item => item.filterSuite()).reduce((pre, cur) => pre || cur, false); + return result; + } + + filterDesc(currentSuiteName, desc, fi, coreContext) { + let filterArray = []; + if (this.itName !== undefined && this.itName !== '' && this.itName !== null) { + filterArray.push(new SuiteAndItNameFilter(currentSuiteName, desc, this.itName)); + } + if (this.class !== undefined && this.class !== '' && this.class !== null) { + filterArray.push(new ClassFilter(currentSuiteName, desc, this.class)); + } + if (this.notClass !== undefined && this.notClass !== '' && this.notClass !== null) { + filterArray.push(new NotClassFilter(currentSuiteName, desc, this.notClass)); + } + if (typeof (this.filter) !== 'undefined' && this.filter !== 0 && fi !== 0) { + filterArray.push(new TestTypesFilter('', '', fi, this.filter)); + } + let result = filterArray.map(item => item.filterIt()).reduce((pre, cur) => pre || cur, false); + return result; + } + + filterWithNest(desc, filter) { + let filterArray = []; + const nestFilter = new NestFilter(); + const targetSuiteArray = this.coreContext.getDefaultService('suite').targetSuiteArray; + const targetSpecArray = this.coreContext.getDefaultService('suite').targetSpecArray; + const suiteStack = this.coreContext.getDefaultService('suite').suitesStack; + let isFilter = nestFilter.filterNestName(targetSuiteArray, targetSpecArray, suiteStack, desc); + const isFullRun = this.coreContext.getDefaultService('suite').fullRun; + if (typeof (this.filter) !== 'undefined' && this.filter !== 0 && filter !== 0) { + filterArray.push(new TestTypesFilter('', '', filter, this.filter)); + return filterArray.map(item => item.filterIt()).reduce((pre, cur) => pre || cur, false); + } + if (isFilter && !isFullRun) { + return true; + } + return nestFilter.filterNotClass(this.notClass, suiteStack, desc); + + } + + isRandom() { + return this.random || false; + } + + isBreakOnError() { + return this.breakOnError !== 'true' ? false : true; + } + + setSupportAsync(value) { + this.supportAsync = value; + } + + isSupportAsync() { + return this.supportAsync; + } + + translateParams(parameters) { + const keySet = new Set(KEYSET); + let targetParams = {}; + for (const key in parameters) { + if (keySet.has(key)) { + var newKey = key.replace('-s ', ''); + targetParams[newKey] = parameters[key]; + } + } + return targetParams; + } + translateParamsToString(parameters) { + const keySet = new Set(KEYSET); + let targetParams = ''; + for (const key in parameters) { + if (keySet.has(key)) { + targetParams += ' ' + key + ' ' + parameters[key]; + } + } + return targetParams.trim(); + } + + execute() { + } + + checkIfSuiteInSkipRun(desc) { + return this.runSkipped.split(',').some(item => { + return item === desc || item.startsWith(desc + '.') || item.startsWith(desc + '#') || desc.startsWith(item + '.') || this.runSkipped === 'skipped'; + }); + } + + checkIfSpecInSkipRun(desc) { + return this.runSkipped.split(',').some(item => { + if (item.includes('#')) { + return item === desc; + } else { + return desc.startsWith(item + '.') || desc.startsWith(item + '#') || this.runSkipped === 'skipped'; + } + } + ); + } +} + +export { + ConfigService +}; diff --git a/code/UI/CalendarViewSwitch/oh_modules/@ohos/hypium/src/main/module/coverage/coverageCollect.js b/code/UI/CalendarViewSwitch/oh_modules/@ohos/hypium/src/main/module/coverage/coverageCollect.js new file mode 100644 index 0000000000000000000000000000000000000000..334a33db9561dd2070c4081457632decf2589b83 --- /dev/null +++ b/code/UI/CalendarViewSwitch/oh_modules/@ohos/hypium/src/main/module/coverage/coverageCollect.js @@ -0,0 +1,76 @@ +/* + * 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 SysTestKit from '../kit/SysTestKit'; +import fs from '@ohos.file.fs'; +import {TAG} from '../../Constant'; + +const jsCoverageFileName = 'js_coverage.json'; + +export async function collectCoverageData() { + if (globalThis.__coverage__ === undefined) { + console.info(`${TAG} globalThis not have coverage`); + return; + } + const strJson = JSON.stringify(globalThis.__coverage__); + let testMode = globalThis.__testMode__; + console.info(`${TAG} coverage data testMode: ${testMode}`); + let savePath = globalThis.__savePath__; + console.info(`${TAG} write coverage data to: ${savePath}`); + let readPath = globalThis.__readPath__; + console.info(`${TAG} read coverage data in: ${readPath}`); + + // run callback mode if local test or (save path and read path ) is not defined + if (!testMode || !isCoveragePathValid(savePath)) { + console.info(`${TAG} run coverage data in call back mode`); + const strLen = strJson.length; + const maxLen = 500; + const maxCount = Math.floor(strLen / maxLen); + const OHOS_REPORT_COVERAGE_DATA = 'OHOS_REPORT_COVERAGE_DATA:'; + for (let count = 0; count <= maxCount; count++) { + console.info(`${OHOS_REPORT_COVERAGE_DATA} ${strJson.substring(count * maxLen, (count + 1) * maxLen)}`); + await SysTestKit.print(`${OHOS_REPORT_COVERAGE_DATA} ${strJson.substring(count * maxLen, (count + 1) * maxLen)}`); + } + return; + } + console.info(`${TAG} run coverage data in save file mode`); + if (fs.accessSync(savePath)) { + fs.unlinkSync(savePath); + } + + let inputPathDir = savePath.substring(0, savePath.length - jsCoverageFileName.length); + if (!fs.accessSync(inputPathDir)) { + console.info(`${TAG} coverage data create dir: ${inputPathDir}`); + fs.mkdirSync(inputPathDir); + } + + let file = fs.openSync(savePath, fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE); + let writeLen = fs.writeSync(file.fd, strJson, {encoding:'utf-8'}); + console.info(`${TAG} write coverage data success: ${writeLen}`); + fs.closeSync(file); + const OHOS_REPORT_COVERAGE_PATH = 'OHOS_REPORT_COVERAGE_PATH:'; + await SysTestKit.print(`${OHOS_REPORT_COVERAGE_PATH} ${readPath}`); + console.info(`${OHOS_REPORT_COVERAGE_PATH} ${readPath}`); +} + +function isCoveragePathValid(inputPath) { + if (!inputPath) { + return false; + } + if (inputPath.indexOf(jsCoverageFileName) === -1) { + return false; + } + return true; +} \ No newline at end of file diff --git a/code/UI/CalendarViewSwitch/oh_modules/@ohos/hypium/src/main/module/kit/SysTestKit.js b/code/UI/CalendarViewSwitch/oh_modules/@ohos/hypium/src/main/module/kit/SysTestKit.js new file mode 100644 index 0000000000000000000000000000000000000000..6e2f256514cff87450f910098b1130943a40e39c --- /dev/null +++ b/code/UI/CalendarViewSwitch/oh_modules/@ohos/hypium/src/main/module/kit/SysTestKit.js @@ -0,0 +1,102 @@ +/* + * 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 {TAG} from '../../Constant'; +import Core from '../../core.js'; + +export default class SysTestKit { + + static delegator = null; + static systemTime = null; + static workerPort = null; + + constructor() { + this.id = 'sysTestKit'; + this.index = 0; + } + + static getDescribeName() { + return Core.getInstance().getDefaultService('suite').getCurrentRunningSuite().description; + } + + static getItName() { + return Core.getInstance().getDefaultService('spec').getCurrentRunningSpec().description; + } + + static getItAttribute() { + return Core.getInstance().getDefaultService('spec').getCurrentRunningSpec().fi; + } + + static actionStart(tag) { + console.info(`${TAG}${JSON.stringify(tag)}`); + var message = '\n' + 'OHOS_REPORT_ACTIONSTART: ' + JSON.stringify(tag) + '\n'; + SysTestKit.print(message); + console.info(`${TAG}${JSON.stringify(tag)} actionStart print success`); + } + + static actionEnd(tag) { + console.info(`${TAG}${JSON.stringify(tag)}`); + var message = '\n' + 'OHOS_REPORT_ACTIONEND: ' + JSON.stringify(tag) + '\n'; + SysTestKit.print(message); + console.info(`${TAG}${JSON.stringify(tag)} actionEnd print success`); + } + + static async existKeyword(keyword, timeout) { + let reg = new RegExp(/^[a-zA-Z0-9]{1,}$/); + if (!reg.test(keyword)) { + throw new Error('keyword must contain more than one string, and only letters and numbers are supported.'); + } + timeout = timeout || 4; + + let searchResult = false; + let cmd = 'hilog -x | grep -i \'' + keyword + '\' | wc -l'; + await executePromise(cmd, timeout).then((data) => { + searchResult = data; + }); + return searchResult; + } + static async print(message) { + if ('printSync' in SysTestKit.delegator) { + console.info(`${TAG}printSync called ...`); + SysTestKit.delegator.printSync(message); + } else { + await SysTestKit.delegator.print(message); + } + } + + static async getRealTime() { + let currentTime = new Date().getTime(); + if (SysTestKit.systemTime !== null && SysTestKit.systemTime !== undefined) { + await SysTestKit.systemTime.getRealTime().then((time) => { + console.info(`${TAG}systemTime.getRealTime success data: ${JSON.stringify(time)}`); + currentTime = time; + }).catch((error) => { + console.error(`${TAG}failed to systemTime.getRealTime because ${JSON.stringify(error)}`); + }); + } + return currentTime; + } +} + +function executePromise(cmd, timeout) { + return new Promise((resolve, reject) => { + SysTestKit.delegator.executeShellCommand(cmd, timeout, + (error, data) => { + console.info(`${TAG}existKeyword CallBack: err : ${JSON.stringify(error)}`); + console.info(`${TAG}existKeyword CallBack: data : ${JSON.stringify(data)}`); + resolve(parseInt(data.stdResult) > 3 ? true : false); + }); + }); +} \ No newline at end of file diff --git a/code/UI/CalendarViewSwitch/oh_modules/@ohos/hypium/src/main/module/mock/ArgumentMatchers.js b/code/UI/CalendarViewSwitch/oh_modules/@ohos/hypium/src/main/module/mock/ArgumentMatchers.js new file mode 100644 index 0000000000000000000000000000000000000000..1e69ac401049589986968a8575ca45a02a299327 --- /dev/null +++ b/code/UI/CalendarViewSwitch/oh_modules/@ohos/hypium/src/main/module/mock/ArgumentMatchers.js @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2022-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 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() { + let regex = arguments[0]; + if (ArgumentMatchers.isRegExp(regex)) { + return regex; + } + throw Error('not a regex'); + } + + static isRegExp(value) { + return Object.prototype.toString.call(value) === '[object RegExp]'; + } + + matcheReturnKey() { + let arg = arguments[0]; + let regex = arguments[1]; + let stubSetKey = arguments[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() { + let key = arguments[0]; + + 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; + } +} + +export default ArgumentMatchers; \ No newline at end of file diff --git a/code/UI/CalendarViewSwitch/oh_modules/@ohos/hypium/src/main/module/mock/ExtendInterface.js b/code/UI/CalendarViewSwitch/oh_modules/@ohos/hypium/src/main/module/mock/ExtendInterface.js new file mode 100644 index 0000000000000000000000000000000000000000..c6a866a6df662ad10a7f6869dcbc2381fa47bcdc --- /dev/null +++ b/code/UI/CalendarViewSwitch/oh_modules/@ohos/hypium/src/main/module/mock/ExtendInterface.js @@ -0,0 +1,57 @@ +/* + * 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() { + this.mocker.clear(); + } +} + +export default ExtendInterface; \ No newline at end of file diff --git a/code/UI/CalendarViewSwitch/oh_modules/@ohos/hypium/src/main/module/mock/MockKit.js b/code/UI/CalendarViewSwitch/oh_modules/@ohos/hypium/src/main/module/mock/MockKit.js new file mode 100644 index 0000000000000000000000000000000000000000..5895666bc89ed4270582b436c82045745d5249b4 --- /dev/null +++ b/code/UI/CalendarViewSwitch/oh_modules/@ohos/hypium/src/main/module/mock/MockKit.js @@ -0,0 +1,266 @@ +/* + * Copyright (c) 2022-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 ExtendInterface from './ExtendInterface'; +import VerificationMode from './VerificationMode'; +import ArgumentMatchers from './ArgumentMatchers'; + +class MockKit { + + 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 = {}; + this.recordCalls = {}; + this.currentSetKey = new Map(); + this.mockObj = null; + this.recordMockedMethod = new Map(); + } + + clearAll() { + this.reset(); + var props = Object.keys(this); + for (var i = 0; i < props.length; i++) { + delete this[props[i]]; + } + + var props = Object.getOwnPropertyNames(this); + for (var i = 0; i < props.length; i++) { + delete this[props[i]]; + } + for (var key in this) { + delete this[key]; + } + } + + 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 = null; + 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(newObj) { + if (newObj.__proto__ === null) { + return []; + } + let properties = Object.getOwnPropertyNames(newObj); + return [...properties, ...getProperty(newObj.__proto__)]; + } + return getProperty(obj); + } + + recordMethodCall(originalMethod, args) { + Function.prototype.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; + 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); + } +} + +export {MockKit, when}; \ No newline at end of file diff --git a/code/UI/CalendarViewSwitch/oh_modules/@ohos/hypium/src/main/module/mock/VerificationMode.js b/code/UI/CalendarViewSwitch/oh_modules/@ohos/hypium/src/main/module/mock/VerificationMode.js new file mode 100644 index 0000000000000000000000000000000000000000..aaf2fdfae00135d3d2055320fc5ea403b44d0bf3 --- /dev/null +++ b/code/UI/CalendarViewSwitch/oh_modules/@ohos/hypium/src/main/module/mock/VerificationMode.js @@ -0,0 +1,49 @@ +/* + * 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 {expect} from '../../interface'; + +class VerificationMode { + constructor(times) { + this.doTimes = times; + } + + times(count) { + expect(count).assertEqual(this.doTimes); + } + + never() { + console.info(this.doTimes); + expect(0).assertEqual(this.doTimes); + } + + once() { + expect(1).assertEqual(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; \ No newline at end of file diff --git a/code/UI/CalendarViewSwitch/oh_modules/@ohos/hypium/src/main/module/report/LogExpectError.js b/code/UI/CalendarViewSwitch/oh_modules/@ohos/hypium/src/main/module/report/LogExpectError.js new file mode 100644 index 0000000000000000000000000000000000000000..5a94cecb4625205797ae886c19ac592f189c2232 --- /dev/null +++ b/code/UI/CalendarViewSwitch/oh_modules/@ohos/hypium/src/main/module/report/LogExpectError.js @@ -0,0 +1,65 @@ +/* + * 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. + */ + +class LogExpectError { + static getErrorMsg(matcherName, actualValue, expect, originMsg) { + if (matcherName === 'assertNull') { + return 'expect not null, actualValue is ' + (actualValue); + } + if (matcherName === 'assertTrue') { + return 'expect not true, actualValue is ' + (actualValue); + } + if (matcherName === 'assertFalse') { + return 'expect not false, actualValue is ' + (actualValue); + } + if (matcherName === 'assertEqual') { + return 'expect not Equal, actualValue is ' + actualValue + ' equals ' + expect; + } + if (matcherName === 'assertContain') { + return 'expect not have, ' + actualValue + ' have ' + expect; + } + if (matcherName === 'assertInstanceOf') { + return 'expect not InstanceOf, ' + actualValue + ' is ' + + Object.prototype.toString.call(actualValue) + expect; + } + if (matcherName === 'assertLarger') { + return 'expect not Larger, ' + + (actualValue) + ' is larger than ' + expect; + } + if (matcherName === 'assertLargerOrEqual') { + return 'expect not LargerOrEqual, ' + (actualValue) + ' larger than ' + expect; + } + if (matcherName === 'assertLess') { + return 'expect not Less, ' + (actualValue) + ' less than ' + expect; + } + if (matcherName === 'assertLessOrEqual') { + return 'expect not LessOrEqual, ' + (actualValue) + ' is less than ' + expect; + } + if (matcherName === 'assertNaN') { + return 'expect not NaN, actualValue is ' + (actualValue); + } + if (matcherName === 'assertNegUnlimited') { + return 'expect not NegUnlimited, actualValue is ' + (actualValue); + } + if (matcherName === 'assertPosUnlimited') { + return 'expect not PosUnlimited, actualValue is ' + (actualValue); + } + if (matcherName === 'assertUndefined') { + return 'expect not Undefined, actualValue is ' + (actualValue); + } + return originMsg; + } +} +export default LogExpectError; \ No newline at end of file diff --git a/code/UI/CalendarViewSwitch/oh_modules/@ohos/hypium/src/main/module/report/OhReport.js b/code/UI/CalendarViewSwitch/oh_modules/@ohos/hypium/src/main/module/report/OhReport.js new file mode 100644 index 0000000000000000000000000000000000000000..653e56be9e88e810f6ab1f3d58049cf08d2ac0b6 --- /dev/null +++ b/code/UI/CalendarViewSwitch/oh_modules/@ohos/hypium/src/main/module/report/OhReport.js @@ -0,0 +1,213 @@ +/* + * 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 SysTestKit from "../kit/SysTestKit"; +import { collectCoverageData } from '../coverage/coverageCollect'; +import { TAG, PrintTag } from '../../Constant'; + +class OhReport { + constructor(attr) { + this.delegator = attr.delegator; + this.abilityDelegatorArguments = attr.abilityDelegatorArguments; + this.id = 'report'; + this.index = 0; + this.duration = 0; + this.currentThreadName = 'mainThread'; + } + + init(coreContext) { + this.coreContext = coreContext; + this.suiteService = this.coreContext.getDefaultService('suite'); + this.specService = this.coreContext.getDefaultService('spec'); + if (SysTestKit.workerPort !== null) { + this.currentThreadName = SysTestKit.workerPort.name; + } + } + + taskStart() { + } + + async taskDone() { + let summary = this.suiteService.getSummary(); + if (this.abilityDelegatorArguments !== null) { + this.taskDoneTime = new Date().getTime(); + const configService = this.coreContext.getDefaultService('config'); + const suiteService = this.coreContext.getDefaultService('suite'); + const specService = this.coreContext.getDefaultService('spec'); + if (configService['coverage'] === 'true') { + await collectCoverageData(); + } + let message = '\n' + `${PrintTag.OHOS_REPORT_RESULT}: stream=Tests run: ` + summary.total + ', Failure: ' + summary.failure; + message += ', Error: ' + summary.error; + message += ', Pass: ' + summary.pass; + message += ', Ignore: ' + summary.ignore; + if (specService.skipSpecNum > 0) { + message += ', SkipSpec: ' + specService.skipSpecNum; + } + message += '\n' + `${PrintTag.OHOS_REPORT_CODE}: ` + (summary.failure > 0 ? -1 : 0) + '\n'; + let isHasError = summary.failure > 0 || summary.error > 0; + let config = this.coreContext.getDefaultService('config'); + if (config.isBreakOnError() && isHasError) { + // 未执行全部说明 + message += '\n' + `${PrintTag.OHOS_REPORT_RESULT}: breakOnError model, Stopping whole test suite if one specific test case failed or error` + '\n'; + } + message += `${PrintTag.OHOS_REPORT_STATUS}: taskconsuming=` + summary.duration + '\n'; + console.info(`${message}`); + await SysTestKit.print(message); + } + if (SysTestKit.workerPort === null || SysTestKit.workerPort === undefined) { + // 主线程执行完成 结束任务。 + console.info(`${TAG}report print success`); + this.delegator.finishTest('your test finished!!!', 0, () => { }); + } else { + // worker线程执行完成将数据发送到主线程中。 + let sendData = { + currentThreadName: this.currentThreadName, + summary: summary + }; + console.info(`${TAG}, send data to mainThread, ${this.currentThreadName}, ${JSON.stringify(sendData)}`); + SysTestKit.workerPort.postMessage(sendData); + } + } + + incorrectFormat() { + if (this.coreContext.getDefaultService('config').filterValid.length !== 0) { + var value = this.coreContext.getDefaultService('config').filterValid; + var message = 'this param ' + value.join(',') + ' is invalid' + '\n'; + this.delegator.finishTest(message, 0, () => { + }); + } + } + + incorrectTestSuiteFormat() { + if (this.coreContext.getDefaultService('config').filterXdescribe.length !== 0) { + let value = this.coreContext.getDefaultService('config').filterXdescribe; + let message = 'xdescribe ' + value.join(',') + ' should not contain it' + '\n'; + this.delegator.finishTest(message, 0, () => { + }); + } + } + async suiteStart() { + if (this.abilityDelegatorArguments !== null) { + let specArr = []; + this.suiteService.getAllChildSuiteNum(this.suiteService.getCurrentRunningSuite(), specArr); + let message = '\n' + `${PrintTag.OHOS_REPORT_SUM}: ` + specArr.length; + this.suiteService.setCurrentRunningSuiteDesc(this.suiteService.getRootSuite(), this.suiteService.getCurrentRunningSuite(), ''); + message += '\n' + `${PrintTag.OHOS_REPORT_STATUS}: class=` + this.suiteService.getCurrentRunningSuiteDesc() + '\n'; + if (this.suiteService.currentRunningSuite.isSkip) { + message += `${PrintTag.OHOS_REPORT_STATUS}: skipReason=` + this.suiteService.currentRunningSuite.skipReason + '\n'; + } + if (SysTestKit.workerPort !== null) { + message += `${PrintTag.OHOS_REPORT_STATUS}: currentWorkerName=` + this.currentThreadName; + } + console.info(`${message}`); + await SysTestKit.print(message); + console.info(`${TAG}${this.suiteService.getCurrentRunningSuite().description} suiteStart print success`); + } + } + + async suiteDone() { + if (this.abilityDelegatorArguments !== null) { + const currentRunningSuite = this.suiteService.getCurrentRunningSuite(); + this.suiteService.setCurrentRunningSuiteDesc(this.suiteService.getRootSuite(), this.suiteService.getCurrentRunningSuite(), ''); + let message = '\n' + `${PrintTag.OHOS_REPORT_STATUS}: class=` + this.suiteService.getCurrentRunningSuiteDesc(); + if (this.suiteService.currentRunningSuite.isSkip && this.suiteService.currentRunningSuite.skipReason !== '') { + message += '\n' + `${PrintTag.OHOS_REPORT_STATUS}: skipReason=` + this.suiteService.currentRunningSuite.skipReason; + } + message += '\n' + `${PrintTag.OHOS_REPORT_STATUS}: suiteconsuming=` + this.suiteService.getCurrentRunningSuite().duration; + if (currentRunningSuite.hookError) { + message += '\n' + `${PrintTag.OHOS_REPORT_STATUS}: ${currentRunningSuite.hookError.message}`; + } + message += '\n'; + if (SysTestKit.workerPort !== null) { + message += `${PrintTag.OHOS_REPORT_STATUS}: currentWorkerName=` + this.currentThreadName; + } + console.info(`${message}`); + await SysTestKit.print(message); + console.info(`${TAG}${this.suiteService.getCurrentRunningSuite().description} suiteDone print success`); + } + } + + async specStart() { + if (this.abilityDelegatorArguments !== null) { + let message = '\n' + `${PrintTag.OHOS_REPORT_STATUS}: class=` + this.suiteService.getCurrentRunningSuiteDesc(); + message += '\n' + `${PrintTag.OHOS_REPORT_STATUS}: current=` + (++this.index); + message += '\n' + `${PrintTag.OHOS_REPORT_STATUS}: id=JS`; + message += '\n' + `${PrintTag.OHOS_REPORT_STATUS}: numtests=` + this.specService.getTestTotal(); + message += '\n' + `${PrintTag.OHOS_REPORT_STATUS}: stream=`; + message += '\n' + `${PrintTag.OHOS_REPORT_STATUS}: test=` + this.specService.currentRunningSpec.description; + message += '\n' + `${PrintTag.OHOS_REPORT_STATUS_CODE}: 1` + '\n'; + if (this.specService.currentRunningSpec.isSkip) { + message += `${PrintTag.OHOS_REPORT_STATUS}: skipReason=` + this.specService.currentRunningSpec.skipReason + '\n'; + } + if (SysTestKit.workerPort !== null) { + message += `${PrintTag.OHOS_REPORT_STATUS}: currentWorkerName=` + this.currentThreadName; + } + console.info(`${message}`); + await SysTestKit.print(message); + console.info(`${TAG}${this.specService.currentRunningSpec.description} specStart start print success`); + } + } + + async specDone() { + if (this.abilityDelegatorArguments !== null) { + let message = '\n' + `${PrintTag.OHOS_REPORT_STATUS}: class=` + this.suiteService.getCurrentRunningSuiteDesc(); + message += '\n' + `${PrintTag.OHOS_REPORT_STATUS}: current=` + (this.index); + message += '\n' + `${PrintTag.OHOS_REPORT_STATUS}: id=JS`; + message += '\n' + `${PrintTag.OHOS_REPORT_STATUS}: numtests=` + this.specService.getTestTotal(); + let messageStack = ''; + let messageCode = ''; + if (this.specService.currentRunningSpec.error) { + messageStack = `${PrintTag.OHOS_REPORT_STATUS}: stack=` + this.specService.currentRunningSpec.error?.stack?.slice(0, -1); + messageCode += `${PrintTag.OHOS_REPORT_STATUS}: stream=`; + messageCode += this.specService.currentRunningSpec.expectMsg !== '' ? + `message: ${this.specService.currentRunningSpec.expectMsg}, Error in ${this.specService.currentRunningSpec.description}, ${this.specService.currentRunningSpec.error?.message}` : + `Error in ${this.specService.currentRunningSpec.description}, ${this.specService.currentRunningSpec.error?.message}`; + messageCode += '\n' + `${PrintTag.OHOS_REPORT_STATUS}: test=` + this.specService.currentRunningSpec.description; + messageCode += '\n' + `${PrintTag.OHOS_REPORT_STATUS_CODE}: -1` + '\n'; + } else if (this.specService.currentRunningSpec) { + if (this.specService.currentRunningSpec.fail) { + messageStack += `${PrintTag.OHOS_REPORT_STATUS}: stack=` + this.specService.currentRunningSpec.fail?.stack?.slice(0, -1); + messageCode += `${PrintTag.OHOS_REPORT_STATUS}: stream=`; + messageCode += this.specService.currentRunningSpec.expectMsg !== '' ? + `message: ${this.specService.currentRunningSpec.expectMsg}, Error in ${this.specService.currentRunningSpec.description}, ${this.specService.currentRunningSpec.fail?.message}` : + `Error in ${this.specService.currentRunningSpec.description}, ${this.specService.currentRunningSpec.fail?.message}`; + messageCode += '\n' + `${PrintTag.OHOS_REPORT_STATUS}: test=` + this.specService.currentRunningSpec.description; + messageCode += '\n' + `${PrintTag.OHOS_REPORT_STATUS_CODE}: -2` + '\n'; + } else { + messageStack += `${PrintTag.OHOS_REPORT_STATUS}: stream=`; + messageCode += `${PrintTag.OHOS_REPORT_STATUS}: test=` + this.specService.currentRunningSpec.description; + messageCode += '\n' + `${PrintTag.OHOS_REPORT_STATUS_CODE}: 0` + '\n'; + messageCode += this.specService.currentRunningSpec.isSkip ? (`${PrintTag.OHOS_REPORT_STATUS}: skipReason=` + this.specService.currentRunningSpec.skipReason + '\n') : ''; + } + } else { + messageCode += '\n'; + } + messageCode += `${PrintTag.OHOS_REPORT_STATUS}: consuming=` + this.specService.currentRunningSpec.duration + '\n'; + if (SysTestKit.workerPort !== null) { + messageCode += `${PrintTag.OHOS_REPORT_STATUS}: currentWorkerName=` + this.currentThreadName; + } + console.info(`${message}`); + console.info(`\n${messageStack}`); + console.info(`\n${messageCode}`); + await SysTestKit.print(message); + await SysTestKit.print(messageStack); + await SysTestKit.print(messageCode); + console.info(`${TAG}${this.specService.currentRunningSpec.description} specDone end print success`); + } + } +} + +export default OhReport; diff --git a/code/UI/CalendarViewSwitch/oh_modules/@ohos/hypium/src/main/module/report/ReportExtend.js b/code/UI/CalendarViewSwitch/oh_modules/@ohos/hypium/src/main/module/report/ReportExtend.js new file mode 100644 index 0000000000000000000000000000000000000000..852fbcd5cbf97e776ebe5177a029df0f516594a5 --- /dev/null +++ b/code/UI/CalendarViewSwitch/oh_modules/@ohos/hypium/src/main/module/report/ReportExtend.js @@ -0,0 +1,137 @@ +/* + * 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. + */ + +class ReportExtend { + constructor(fileModule) { + this.id = 'extend'; + this.fileModule = fileModule; + } + + init(coreContext) { + this.coreContext = coreContext; + this.suiteService = this.coreContext.getDefaultService('suite'); + } + + taskStart() { + + } + + taskDone() { + const report = { + tag: 'testsuites', + name: 'summary_report', + timestamp: new Date().toDateString(), + time: '1', + errors: 0, + failures: 0, + tests: 0, + children: [] + }; + const rootSuite = this.suiteService.rootSuite; + if (rootSuite && rootSuite.childSuites) { + for (let testsuite of rootSuite.childSuites) { + let suiteReport = { + tag: 'testsuite', + name: testsuite['description'], + errors: 0, + tests: 0, + failures: 0, + time: '0.1', + children: [] + }; + let specs = testsuite['specs']; + for (let testcase of specs) { + report.tests++; + suiteReport.tests++; + let caseReport = { + tag: 'testcase', + name: testcase['description'], + status: 'run', + time: '0.0', + classname: testsuite['description'] + }; + if (testcase.error) { + caseReport['result'] = false; + caseReport['children'] = [{ + tag: 'error', + type: '', + message: testcase.error.message + }]; + report.errors++; + suiteReport.errors++; + } else if (testcase.result.failExpects.length > 0) { + caseReport['result'] = false; + let message = ''; + testcase.result.failExpects.forEach(failExpect => { + message += failExpect.message || ('expect ' + failExpect.actualValue + ' ' + failExpect.checkFunc + ' ' + (failExpect.expectValue || '')) + ';'; + }); + caseReport['children'] = [{ + tag: 'failure', + type: '', + message: message + }]; + report.failures++; + suiteReport.failures++; + } else { + caseReport['result'] = true; + } + suiteReport.children.push(caseReport); + } + report.children.push(suiteReport); + } + } + + let reportXml = '\n' + json2xml(report); + this.fileModule.writeText({ + uri: 'internal://app/report.xml', + text: reportXml, + success: function () { + console.info('call success callback success'); + }, + fail: function (data, code) { + console.info('call fail callback success:'); + }, + complete: function () { + console.info('call complete callback success'); + } + }); + } +} + +function json2xml(json) { + let tagName; + let hasChildren = false; + let childrenStr = ''; + let attrStr = ''; + for (let key in json) { + if (key === 'tag') { + tagName = json[key]; + } else if (key === 'children') { + if (json[key].length > 0) { + hasChildren = true; + for (let child of json[key]) { + childrenStr += json2xml(child); + } + } + } else { + attrStr += ` ${key}="${json[key]}"`; + } + } + let xml = `<${tagName}${attrStr}`; + xml += hasChildren ? `>${childrenStr}` : '/>'; + return xml; +} + +export default ReportExtend; diff --git a/code/UI/CalendarViewSwitch/oh_modules/@ohos/hypium/src/main/service.js b/code/UI/CalendarViewSwitch/oh_modules/@ohos/hypium/src/main/service.js new file mode 100644 index 0000000000000000000000000000000000000000..92d46b77959af314f40bd601ede62b9b6d8b9ba2 --- /dev/null +++ b/code/UI/CalendarViewSwitch/oh_modules/@ohos/hypium/src/main/service.js @@ -0,0 +1,1230 @@ +/* + * 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 SysTestKit from './module/kit/SysTestKit'; +import { TAG } from './Constant'; +import LogExpectError from './module/report/LogExpectError'; +import { NestFilter } from './module/config/Filter'; + +class AssertException extends Error { + constructor(message) { + super(); + this.name = 'AssertException'; + this.message = message; + } +} + +function getFuncWithArgsZero(func, timeout, isStressTest) { + return new Promise(async (resolve, reject) => { + let timer = null; + if (!isStressTest) { + timer = setTimeout(() => { + reject(new Error('execute timeout ' + timeout + 'ms')); + }, timeout); + } + try { + await func(); + } catch (err) { + reject(err); + } + timer !== null ? clearTimeout(timer) : null; + resolve(); + }); +} + +function getFuncWithArgsOne(func, timeout, isStressTest) { + return new Promise(async (resolve, reject) => { + let timer = null; + if (!isStressTest) { + timer = setTimeout(() => { + reject(new Error('execute timeout ' + timeout + 'ms')); + }, timeout); + } + + function done() { + timer !== null ? clearTimeout(timer) : null; + resolve(); + } + + try { + await func(done); + } catch (err) { + timer !== null ? clearTimeout(timer) : null; + reject(err); + } + }); +} + +function getFuncWithArgsTwo(func, timeout, paramItem, isStressTest) { + return new Promise(async (resolve, reject) => { + let timer = null; + if (!isStressTest) { + timer = setTimeout(() => { + reject(new Error('execute timeout ' + timeout + 'ms')); + }, timeout); + } + + function done() { + timer !== null ? clearTimeout(timer) : null; + resolve(); + } + + try { + await func(done, paramItem); + } catch (err) { + timer !== null ? clearTimeout(timer) : null; + reject(err); + } + }); +} + +function processFunc(coreContext, func) { + let argNames = ((func || '').toString() + .replace(/((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg, '') + .match(/^(function)?\s*[^\(]*\(\s*([^\)]*)\)/m) || ['', '', ''])[2] + .split(',') // split parameters + .map(item => item.replace(/^\s*(_?)(.+?)\1\s*$/, name => name.split('=')[0].trim())) + .filter(String); + let funcLen = func.length; + let processedFunc; + const config = coreContext.getDefaultService('config'); + config.setSupportAsync(true); + const timeout = + (config.timeout === undefined ? 5000 : config.timeout); + const isStressTest = (coreContext.getServices('dataDriver') !== undefined || config.getStress() > 1); + switch (funcLen) { + case 0: { + processedFunc = function () { + return getFuncWithArgsZero(func, timeout, isStressTest); + }; + break; + } + case 1: { + if (argNames[0] === 'data') { + processedFunc = function (paramItem) { + func(paramItem); + }; + } else { + processedFunc = function () { + return getFuncWithArgsOne(func, timeout, isStressTest); + }; + } + break; + } + default: { + processedFunc = function (paramItem) { + return getFuncWithArgsTwo(func, timeout, paramItem, isStressTest); + }; + break; + } + } + return processedFunc; +} + +function secureRandomNumber() { + return crypto.randomBytes(8).readUInt32LE() / 0xffffffff; +} + +class SuiteService { + constructor(attr) { + this.id = attr.id; + this.rootSuite = new SuiteService.Suite({}); + this.currentRunningSuite = this.rootSuite; + this.suitesStack = [this.rootSuite]; + this.targetSuiteArray = []; + this.targetSpecArray = []; + this.currentRunningSuiteDesc = null; + this.fullRun = false; + this.isSkipSuite = false; + this.suiteSkipReason = null; + } + + describe(desc, func) { + const configService = this.coreContext.getDefaultService('config'); + if (this.suitesStack.some(suite => { return suite.description === desc })) { + console.error(`${TAG} Loop nesting occurs : ${desc}`); + this.suiteSkipReason = ''; + this.isSkipSuite = false; + return; + } + let isFilter = this.analyzeConfigServiceClass(configService.class, desc); + if (configService.filterSuite(desc) && isFilter) { + if (this.currentRunningSuite.description === '' || this.currentRunningSuite.description == null) { + console.info(`${TAG}filter suite : ${desc}`); + this.suiteSkipReason = ''; + this.isSkipSuite = false; + return; + } + } + const suite = new SuiteService.Suite({ description: desc }); + if (this.isSkipSuite) { + suite.isSkip = true; + suite.skipReason = this.suiteSkipReason; + } + this.suiteSkipReason = ''; + this.isSkipSuite = false; + if (typeof this.coreContext.getServices('dataDriver') !== 'undefined' && configService['dryRun'] !== 'true') { + let suiteStress = this.coreContext.getServices('dataDriver').dataDriver.getSuiteStress(desc); + for (let i = 1; i < suiteStress; i++) { + this.currentRunningSuite.childSuites.push(suite); + } + } + this.currentRunningSuite.childSuites.push(suite); + this.currentRunningSuite = suite; + this.suitesStack.push(suite); + func.call(); + this.suitesStack.pop(); + this.currentRunningSuite = this.suitesStack.pop(); + this.suitesStack.push(this.currentRunningSuite); + } + xdescribe(desc, func, reason) { + const configService = this.coreContext.getDefaultService('config'); + if (!configService.skipMessage && configService.runSkipped !== 'all') { + if (configService.runSkipped != null && configService.runSkipped !== '') { + let finalDesc = ''; + this.suitesStack.map(suite => { + finalDesc = finalDesc + '.' + suite.description; + }); + finalDesc = (finalDesc + '.' + desc).substring(2); + console.info(`${TAG} finalDesc ${finalDesc}`); + if (configService.checkIfSuiteInSkipRun(finalDesc)) { + console.info(`${TAG} runSkipped suite: ${desc}`); + } else { + console.info(reason == null ? `${TAG} skip suite: ${desc}` : `${TAG} skip suite: ${desc}, and the reason is ${reason}`); + return; + } + } else { + console.info(reason == null ? `${TAG} skip suite: ${desc}` : `${TAG} skip suite: ${desc}, and the reason is ${reason}`); + return; + } + } + this.isSkipSuite = true; + this.suiteSkipReason = reason; + this.describe(desc, func); + } + + beforeAll(func) { + this.currentRunningSuite.beforeAll.push(processFunc(this.coreContext, func)); + } + + beforeEach(func) { + this.currentRunningSuite.beforeEach.push(processFunc(this.coreContext, func)); + } + + beforeItSpecified(itDescs, func) { + this.currentRunningSuite.beforeItSpecified.set(itDescs, processFunc(this.coreContext, func)); + } + + afterItSpecified(itDescs, func) { + this.currentRunningSuite.afterItSpecified.set(itDescs, processFunc(this.coreContext, func)); + } + + afterAll(func) { + this.currentRunningSuite.afterAll.push(processFunc(this.coreContext, func)); + } + + afterEach(func) { + this.currentRunningSuite.afterEach.push(processFunc(this.coreContext, func)); + } + + getCurrentRunningSuite() { + return this.currentRunningSuite; + } + + setCurrentRunningSuite(suite) { + this.currentRunningSuite = suite; + } + + getRootSuite() { + return this.rootSuite; + } + + getCurrentRunningSuiteDesc() { + return this.currentRunningSuiteDesc; + } + + + setCurrentRunningSuiteDesc(suite, currentSuite, prefix) { + if (suite != null && suite === currentSuite) { + this.currentRunningSuiteDesc = prefix; + } else if (suite != null && suite !== currentSuite) { + suite.childSuites.forEach(it => { + let temp = prefix; + if (it.description != null || it.description !== '') { + temp = prefix === '' ? it.description : prefix + '.' + it.description; + } + this.setCurrentRunningSuiteDesc(it, currentSuite, temp); + } + ); + } + } + analyzeConfigServiceClass(configServiceClass, desc) { + if (configServiceClass == null || configServiceClass === '') { + this.fullRun = true + return false; + } + if (this.targetSuiteArray.length === 0) { + const targetArray = configServiceClass.split(','); + for (let index in targetArray) { + if (targetArray[index].includes('#')) { + this.targetSpecArray.push(targetArray[index]); + } else { + this.targetSuiteArray.push(targetArray[index]); + } + } + + } + return !configServiceClass.includes(desc); + + } + traversalResults(suite, obj, breakOnError) { + if (suite.childSuites.length === 0 && suite.specs.length === 0) { + return obj; + } + if (suite.specs.length > 0) { + for (const itItem of suite.specs) { + obj.total++; + let itInfo = { + currentThreadName: 'mainThread', + description: suite.description + '#' + itItem.description, + result: -3 + }; + if (SysTestKit.workerPort !== null) { + itInfo.currentThreadName = SysTestKit.workerPort.name; + } + obj.itItemList.push(itInfo); + if (breakOnError && (obj.error > 0 || obj.failure > 0)) { // breakOnError模式 + continue; + } + if (itItem.error) { + obj.error++; + itInfo.result = -1; + } else if (itItem.fail) { + obj.failure++; + itInfo.result = -2; + } else if (itItem.pass === true) { + obj.pass++; + itInfo.result = 0; + } + } + } + + obj.duration += suite.duration; + + if (suite.childSuites.length > 0) { + for (const suiteItem of suite.childSuites) { + this.traversalResults(suiteItem, obj, breakOnError); + } + } + } + + async setSuiteResults(suite, error, coreContext) { + if (suite.childSuites.length === 0 && suite.specs.length === 0) { + return obj; + } + if (suite.specs.length > 0) { + const specService = coreContext.getDefaultService('spec'); + for (const specItem of suite.specs) { + specService.setCurrentRunningSpec(specItem); + if (error instanceof AssertException) { + specItem.fail = error; + } else { + specItem.error = error; + } + await coreContext.fireEvents('spec', 'specStart', specItem); + await coreContext.fireEvents('spec', 'specDone', specItem); + } + } + if (suite.childSuites.length > 0) { + for (const suiteItem of suite.childSuites) { + await this.setSuiteResults(suiteItem, error, coreContext); + } + } + } + + getSummary() { + let suiteService = this.coreContext.getDefaultService('suite'); + let rootSuite = suiteService.rootSuite; + const specService = this.coreContext.getDefaultService('spec'); + const configService = this.coreContext.getDefaultService('config'); + let breakOnError = configService.isBreakOnError(); + let isError = specService.getStatus(); + let isBreaKOnError = breakOnError && isError; + // itItemList 保存当前用例执行情况, 发送到主线程用例计算最终结果 + let obj = { total: 0, failure: 0, error: 0, pass: 0, ignore: 0, duration: 0, itItemList: []}; + for (const suiteItem of rootSuite.childSuites) { + this.traversalResults(suiteItem, obj, isBreaKOnError); + } + obj.ignore = obj.total - obj.pass - obj.failure - obj.error; + return obj; + } + + init(coreContext) { + this.coreContext = coreContext; + } + + traversalSuites(suite, obj, configService) { + if (suite.childSuites.length === 0 && suite.specs.length === 0) { + return []; + } + if (suite.specs.length > 0) { + let itArray = []; + for (const itItem of suite['specs']) { + if (!configService.filterDesc(suite.description, itItem.description, itItem.fi, null)) { + itArray.push({ 'itName': itItem.description }); + } + } + obj[suite.description] = itArray; + } + if (suite.childSuites.length > 0) { + let suiteArray = []; + for (const suiteItem of suite.childSuites) { + let suiteObj = {}; + this.traversalSuites(suiteItem, suiteObj, configService); + if (!configService.filterSuite(suiteItem.description)) { + suiteArray.push(suiteObj); + } + } + obj.suites = suiteArray; + } + } + + async dryRun(abilityDelegator) { + console.info(`${TAG} rootSuite : ` + JSON.stringify(this.rootSuite)); + let obj = this.rootSuite; + let prefixStack = []; + let suiteArray = []; + let skipSuiteArray = []; + this.analyzeSuitesArray(prefixStack, suiteArray, skipSuiteArray, obj); + const configService = this.coreContext.getDefaultService('config'); + let result; + if (configService.skipMessage) { + result = { 'suites': suiteArray, 'skipSuites': skipSuiteArray }; + } else { + result = { 'suites': suiteArray }; + } + let strJson = JSON.stringify(result); + let strLen = strJson.length; + let maxLen = 500; + let maxCount = Math.floor(strLen / maxLen); + for (let count = 0; count <= maxCount; count++) { + await SysTestKit.print(strJson.substring(count * maxLen, (count + 1) * maxLen)); + } + console.info(`${TAG}dryRun print success`); + abilityDelegator.finishTest('dry run finished!!!', 0, () => { }); + } + + //将suitesArray的嵌套结构展开成三层结构 + analyzeSuitesArray(prefixStack, suiteArray, skipSuiteArray, obj) { + obj.childSuites.map(suite => { + if (suite.description != null && suite.description !== '') { + let prefix = ''; + if (prefixStack.length > 0) { + prefix = prefixStack.join('.') + '.' + suite.description; + } else { + prefix = suite.description; + } + prefixStack.push(suite.description); + let temp = {}; + temp[prefix] = []; + let skipTemp = {}; + skipTemp[prefix] = []; + suite.specs.map(spec => { + let it = { 'itName': spec.description }; + spec.isSkip ? skipTemp[prefix].push(it) : temp[prefix].push(it); + }); + suiteArray.push(temp); + skipSuiteArray.push(skipTemp); + } + this.analyzeSuitesArray(prefixStack, suiteArray, skipSuiteArray, suite); + prefixStack.pop(); + }); + } + //获取当前测试套下的所有测试用例数量 + getAllChildSuiteNum(suite, specArray) { + if (suite.specs != null) { + suite.specs.forEach(spec => specArray.push(spec)); + } + if (suite.childSuites != null) { + suite.childSuites.forEach(it => this.getAllChildSuiteNum(it, specArray)); + } + } + + execute() { + const configService = this.coreContext.getDefaultService('config'); + if (configService.filterValid.length !== 0) { + this.coreContext.fireEvents('task', 'incorrectFormat'); + return; + } + if (configService.filterXdescribe.length !== 0) { + this.coreContext.fireEvents('task', 'incorrectTestSuiteFormat'); + return; + } + if (configService.isRandom() && this.rootSuite.childSuites.length > 0) { + this.rootSuite.childSuites.sort(function () { + return Math.random().toFixed(1) > 0.5 ? -1 : 1; + }); + this.currentRunningSuite = this.rootSuite.childSuites[0]; + } + if (configService.isSupportAsync()) { + console.info(`${TAG} rootSuite:` + JSON.stringify(this.rootSuite)); + let asyncExecute = async () => { + await this.coreContext.fireEvents('task', 'taskStart'); + await this.rootSuite.asyncRun(this.coreContext); + }; + asyncExecute().then(async () => { + await this.coreContext.fireEvents('task', 'taskDone'); + }); + } else { + console.info('${TAG} rootSuite:' + JSON.stringify(this.rootSuite)); + this.coreContext.fireEvents('task', 'taskStart'); + this.rootSuite.run(this.coreContext); + this.coreContext.fireEvents('task', 'taskDone'); + } + } + + apis() { + const _this = this; + return { + describe: function (desc, func) { + return _this.describe(desc, func); + }, + xdescribe: function (desc, func, reason) { + return _this.xdescribe(desc, func, reason); + }, + beforeItSpecified: function (itDescs, func) { + return _this.beforeItSpecified(itDescs, func); + }, + afterItSpecified: function (itDescs, func) { + return _this.afterItSpecified(itDescs, func); + }, + beforeAll: function (func) { + return _this.beforeAll(func); + }, + beforeEach: function (func) { + return _this.beforeEach(func); + }, + afterAll: function (func) { + return _this.afterAll(func); + }, + afterEach: function (func) { + return _this.afterEach(func); + } + }; + } +} + +SuiteService.Suite = class { + constructor(attrs) { + this.description = attrs.description || ''; + this.childSuites = []; + this.specs = []; + this.beforeAll = []; + this.afterAll = []; + this.beforeItSpecified = new Map(); + this.afterItSpecified = new Map(); + this.beforeEach = []; + this.afterEach = []; + this.duration = 0; + this.hookError = null; + this.isSkip = false; + this.skipReason = ''; + } + + pushSpec(spec) { + this.specs.push(spec); + } + + removeSpec(desc) { + this.specs = this.specs.filter((item, index) => { + return item.description !== desc; + }); + } + + getSpecsNum() { + return this.specs.length; + } + + isRun(coreContext) { + const configService = coreContext.getDefaultService('config'); + const suiteService = coreContext.getDefaultService('suite'); + const specService = coreContext.getDefaultService('spec'); + let breakOnError = configService.isBreakOnError(); + let isError = specService.getStatus(); + return breakOnError && isError; + } + + run(coreContext) { + const suiteService = coreContext.getDefaultService('suite'); + suiteService.setCurrentRunningSuite(this); + if (this.description !== '') { + coreContext.fireEvents('suite', 'suiteStart', this); + } + this.runHookFunc('beforeAll'); + if (this.specs.length > 0) { + const configService = coreContext.getDefaultService('config'); + if (configService.isRandom()) { + this.specs.sort(function () { + return Math.random().toFixed(1) > 0.5 ? -1 : 1; + }); + } + for (let spec in this.specs) { + let isBreakOnError = this.isRun(coreContext); + if (isBreakOnError) { + break; + } + this.runHookFunc('beforeEach'); + spec.run(coreContext); + this.runHookFunc('afterEach'); + } + } + if (this.childSuites.length > 0) { + for (let suite in this.childSuites) { + let isBreakOnError = this.isRun(coreContext); + if (isBreakOnError) { + break; + } + suite.run(coreContext); + suiteService.setCurrentRunningSuite(suite); + } + } + this.runHookFunc('afterAll'); + if (this.description !== '') { + coreContext.fireEvents('suite', 'suiteDone'); + } + } + + async asyncRunSpecs(coreContext) { + const configService = coreContext.getDefaultService('config'); + if (configService.isRandom()) { + this.specs.sort(function () { + return Math.random().toFixed(1) > 0.5 ? -1 : 1; + }); + } + const specService = coreContext.getDefaultService('spec'); + for (let specItem of this.specs) { + specService.setCurrentRunningSpec(specItem); + // 遇错即停模式,发现用例有问题,直接返回,不在执行后面的it + let isBreakOnError = this.isRun(coreContext); + if (isBreakOnError) { + console.info('break description :' + this.description); + break; + } + await coreContext.fireEvents('spec', 'specStart', specItem); + try { + for (const [itNames, hookFunc] of this.beforeItSpecified) { + if ((Object.prototype.toString.call(itNames) === '[object Array]' && itNames.includes(specItem.description)) || + (Object.prototype.toString.call(itNames) === '[object String]' && itNames === specItem.description)) { + await Reflect.apply(hookFunc, null, []); + } + break; + } + await this.runAsyncHookFunc('beforeEach'); + await specItem.asyncRun(coreContext); + for (const [itNames, hookFunc] of this.afterItSpecified) { + if ((Object.prototype.toString.call(itNames) === '[object Array]' && itNames.includes(specItem.description)) || + (Object.prototype.toString.call(itNames) === '[object String]' && itNames === specItem.description)) { + await Reflect.apply(hookFunc, null, []); + } + break; + } + await this.runAsyncHookFunc('afterEach'); + } catch (e) { + console.error(`${TAG}stack:${e?.stack}`); + console.error(`${TAG}stack end`); + if (e instanceof AssertException) { + specItem.fail = e; + } else { + specItem.error = e; + } + specService.setStatus(true); + } + specItem.setResult(); + await coreContext.fireEvents('spec', 'specDone', specItem); + specService.setCurrentRunningSpec(null); + } + } + + async asyncRunChildSuites(coreContext) { + for (let i = 0; i < this.childSuites.length; i++) { + // 遇错即停模式, 发现用例有问题,直接返回,不在执行后面的description + let isBreakOnError = this.isRun(coreContext); + if (isBreakOnError) { + console.info(`${TAG}break description : ${this.description}`); + break; + } + await this.childSuites[i].asyncRun(coreContext); + } + } + + async asyncRun(coreContext) { + const suiteService = coreContext.getDefaultService('suite'); + const specService = coreContext.getDefaultService('spec'); + + suiteService.setCurrentRunningSuite(this); + suiteService.suitesStack.push(this); + if (this.description !== '') { + await coreContext.fireEvents('suite', 'suiteStart', this); + } + + try { + await this.runAsyncHookFunc('beforeAll'); + } catch (error) { + console.error(`${TAG}${error?.stack}`); + this.hookError = error; + } + + if (this.hookError !== null) { + specService.setStatus(true); + await suiteService.setSuiteResults(this, this.hookError, coreContext); + } + + if (this.specs.length > 0 && this.hookError === null) { + await this.asyncRunSpecs(coreContext); + } + + if (this.childSuites.length > 0 && this.hookError === null) { + await this.asyncRunChildSuites(coreContext); + } + + try { + await this.runAsyncHookFunc('afterAll'); + } catch (error) { + console.error(`${TAG}${error?.stack}`); + this.hookError = error; + specService.setStatus(true); + } + + if (this.description !== '') { + await coreContext.fireEvents('suite', 'suiteDone'); + let childSuite = suiteService.suitesStack.pop(); + let currentRunningSuite = suiteService.suitesStack.pop(); + suiteService.setCurrentRunningSuite(currentRunningSuite); + suiteService.suitesStack.push(currentRunningSuite); + } + } + + runHookFunc(hookName) { + if (this[hookName] && this[hookName].length > 0) { + this[hookName].forEach(func => { + try { + func(); + } catch (e) { + console.error(`${TAG}${e.stack}`); + } + }); + } + } + + async runAsyncHookFunc(hookName) { + for (const hookItem of this[hookName]) { + try { + await hookItem(); + } catch (error) { + error['message'] += `, error in ${hookName} function`; + throw error; + } + + } + } +}; + +class SpecService { + constructor(attr) { + this.id = attr.id; + this.totalTest = 0; + this.hasError = false; + this.skipSpecNum = 0; + this.isSkipSpec = false; + this.specSkipReason = ''; + } + + init(coreContext) { + this.coreContext = coreContext; + } + + setCurrentRunningSpec(spec) { + this.currentRunningSpec = spec; + } + + setStatus(obj) { + this.hasError = obj; + } + + getStatus() { + return this.hasError; + } + + getTestTotal() { + return this.totalTest; + } + + getCurrentRunningSpec() { + return this.currentRunningSpec; + } + + + getSkipSpecNum() { + return this.skipSpecNum; + } + + initSpecService() { + this.isSkipSpec = false; + this.specSkipReason = ''; + } + + it(desc, filter, func) { + const suiteService = this.coreContext.getDefaultService('suite'); + const configService = this.coreContext.getDefaultService('config'); + let isFilter = new NestFilter().filterNestName(suiteService.targetSuiteArray, suiteService.targetSpecArray, suiteService.suitesStack, desc); + if (configService.filterWithNest(desc, filter)) { + console.info(`${TAG}filter it :${desc}`); + this.initSpecService(); + return; + } + if (configService.filterDesc(suiteService.currentRunningSuite.description, desc, filter, this.coreContext) && isFilter && !suiteService.fullRun) { + console.info(`${TAG}filter it :${desc}`); + this.initSpecService(); + } else { + let processedFunc = processFunc(this.coreContext, func); + const spec = new SpecService.Spec({ description: desc, fi: filter, fn: processedFunc }); + if (this.isSkipSpec) { + spec.isSkip = true; + spec.skipReason = this.specSkipReason; + } + this.initSpecService(); + if (configService.runSkipped === 'skipped' && !spec.isSkip) { + console.info(`${TAG} runSkipped is skipped , just run xit, don't run it: ${spec.description}`); + return; + } + if (suiteService.getCurrentRunningSuite().isSkip && !spec.isSkip) { + configService.filterXdescribe.push(suiteService.getCurrentRunningSuite().description); + } + if (typeof this.coreContext.getServices('dataDriver') !== 'undefined' && configService['dryRun'] !== 'true') { + let specStress = this.coreContext.getServices('dataDriver').dataDriver.getSpecStress(desc); + for (let i = 1; i < specStress; i++) { + this.totalTest++; + suiteService.getCurrentRunningSuite().pushSpec(spec); + } + } + // dryRun 状态下不统计压力测试重复数据 + if (configService['dryRun'] !== 'true') { + let stress = configService.getStress(); // 命令配置压力测试 + console.info(`${TAG}stress length : ${stress}`); + for (let i = 1; i < stress; i++) { + this.totalTest++; + suiteService.getCurrentRunningSuite().pushSpec(spec); + } + } + this.totalTest++; + suiteService.getCurrentRunningSuite().pushSpec(spec); + } + } + + xit(desc, filter, func, reason) { + const configService = this.coreContext.getDefaultService('config'); + const suiteService = this.coreContext.getDefaultService('suite'); + if (!configService.skipMessage && configService.runSkipped !== 'all') { + if (configService.runSkipped != null && configService.runSkipped !== '') { + let finalDesc = ''; + suiteService.suitesStack.map(suite => { + finalDesc = finalDesc + '.' + suite.description; + }); + finalDesc = (finalDesc + '#' + desc).substring(2); + if (configService.checkIfSpecInSkipRun(finalDesc)) { + console.info(`${TAG} runSkipped spec: ${desc}`); + } else { + console.info(reason == null ? `${TAG} skip spec: ${desc}` : `${TAG} skip spec: ${desc}, and the reason is ${reason}`); + return; + } + } else { + console.info(reason == null ? `${TAG} skip spec: ${desc}` : `${TAG} skip spec: ${desc}, and the reason is ${reason}`); + return; + } + } + this.skipSpecNum++; + this.isSkipSpec = true; + this.specSkipReason = reason; + this.it(desc, filter, func); + } + + apis() { + const _this = this; + return { + it: function (desc, filter, func) { + return _this.it(desc, filter, func); + }, + xit: function (desc, filter, func, reason) { + return _this.xit(desc, filter, func, reason); + } + }; + } +} + +SpecService.Spec = class { + constructor(attrs) { + this.description = attrs.description || ''; + this.fi = attrs.fi; + this.fn = attrs.fn || function () { + }; + this.fail = undefined; + this.error = undefined; + this.duration = 0; + this.startTime = 0; + this.isExecuted = false; // 当前用例是否执行 + this.isSkip = false; + this.skipReason = ''; + this.expectMsg = ''; + } + + setResult() { + if (this.fail) { + this.pass = false; + } else { + this.pass = true; + } + } + + run(coreContext) { + const specService = coreContext.getDefaultService('spec'); + specService.setCurrentRunningSpec(this); + coreContext.fireEvents('spec', 'specStart', this); + this.isExecuted = true; + try { + let dataDriver = coreContext.getServices('dataDriver'); + if (typeof dataDriver === 'undefined') { + this.fn(); + } else { + let suiteParams = dataDriver.dataDriver.getSuiteParams(); + let specParams = dataDriver.dataDriver.getSpecParams(); + console.info(`${TAG}[suite params] ${JSON.stringify(suiteParams)}`); + console.info(`${TAG}[spec params] ${JSON.stringify(specParams)}`); + if (this.fn.length === 0) { + this.fn(); + } else if (specParams.length === 0) { + this.fn(suiteParams); + } else { + specParams.forEach(paramItem => this.fn(Object.assign({}, paramItem, suiteParams))); + } + } + this.setResult(); + } catch (e) { + this.error = e; + specService.setStatus(true); + } + coreContext.fireEvents('spec', 'specDone', this); + } + + async asyncRun(coreContext) { + const dataDriver = coreContext.getServices('dataDriver'); + if (typeof dataDriver === 'undefined') { + await this.fn(); + } else { + const suiteParams = dataDriver.dataDriver.getSuiteParams(); + const specParams = dataDriver.dataDriver.getSpecParams(); + console.info(`[suite params] ${JSON.stringify(suiteParams)}`); + console.info(`[spec params] ${JSON.stringify(specParams)}`); + if (this.fn.length === 0) { + await this.fn(); + } else if (specParams.length === 0) { + await this.fn(suiteParams); + } else { + for (const paramItem of specParams) { + await this.fn(Object.assign({}, paramItem, suiteParams)); + } + } + } + + this.isExecuted = true; + } + + filterCheck(coreContext) { + const specService = coreContext.getDefaultService('spec'); + specService.setCurrentRunningSpec(this); + return true; + } +}; + +class ExpectService { + constructor(attr) { + this.id = attr.id; + this.matchers = {}; + this.customMatchers = []; + } + + expect(actualValue) { + return this.wrapMatchers(actualValue); + } + + init(coreContext) { + this.coreContext = coreContext; + this.addMatchers(this.basicMatchers()); + } + + addMatchers(matchers) { + for (const matcherName in matchers) { + if (Object.prototype.hasOwnProperty.call(matchers, matcherName)) { + this.matchers[matcherName] = matchers[matcherName]; + } + } + } + + removeMatchers(customAssertionName) { + if (customAssertionName === 'all') { + for (const matcherName in this.matchers) { + this.matchers[matcherName] = this.customMatchers.includes(matcherName) ? (() => {throw new Error(`${matcherName} is unregistered`)}) : undefined; + } + }else { + this.matchers[customAssertionName] = () => { + throw new Error(`${customAssertionName} is unregistered`) + }; + } + } + + basicMatchers() { + return { + assertTrue: function (actualValue) { + return { + pass: (actualValue) === true, + message: 'expect true, actualValue is ' + actualValue + }; + }, + assertEqual: function (actualValue, args) { + let msg = 'expect ' + actualValue + ' equals ' + args[0]; + if (actualValue == args[0]) { // 数值相同,提示数据类型 + const aClassName = Object.prototype.toString.call(actualValue); + const bClassName = Object.prototype.toString.call(args[0]); + msg = 'expect ' + actualValue + aClassName + ' equals ' + args[0] + bClassName + 'strict mode inspect type'; + } + return { + pass: (actualValue) === args[0], + expectValue: args[0], + message: msg + }; + }, + assertThrow: function (actual, args) { + const result = { + pass: false + }; + if (typeof actual !== 'function') { + result.message = 'toThrow\'s Actual should be a Function'; + } else { + let hasThrow = false; + let throwError; + try { + actual(); + } catch (e) { + hasThrow = true; + throwError = e; + } + if (!hasThrow) { + result.message = 'function did not throw an exception'; + } else if (throwError && throwError.message === args[0]) { + result.pass = true; + } else { + result.message = `expect to throw ${args[0]} , actual throw ${throwError.message}`; + } + } + return result; + } + }; + } + + initWrapMatchers(currentRunningSpec) { + return { + // 翻转标识 + isNot: false, + // 翻转方法 + not: function () { + this.isNot = true; + return this; + }, + message: function (msg) { + currentRunningSpec.expectMsg = msg; + console.info(`${TAG} msg: ${msg}`); + return this; + } + }; + + } + wrapMatchers(actualValue) { + const _this = this; + const specService = _this.coreContext.getDefaultService('spec'); + const currentRunningSpec = specService.getCurrentRunningSpec(); + const wrappedMatchers = this.initWrapMatchers(currentRunningSpec); + const currentRunningSuite = _this.coreContext.getDefaultService('suite').getCurrentRunningSuite(); + for (const matcherName in this.matchers) { + let result = Object.prototype.hasOwnProperty.call(this.matchers, matcherName); + if (!result) { + continue; + } + if (matcherName.search('assertPromise') == 0) { + wrappedMatchers[matcherName] = async function () { + await _this.matchers[matcherName](actualValue, arguments).then(function (result) { + if (wrappedMatchers.isNot) { + result.pass = !result.pass; + } + result.actualValue = actualValue; + result.checkFunc = matcherName; + if (!result.pass) { + const assertError = new AssertException(result.message); + currentRunningSpec ? currentRunningSpec.fail = assertError : currentRunningSuite.hookError = assertError; + throw assertError; + } + }); + }; + } else { + wrappedMatchers[matcherName] = function () { + const result = _this.customMatchers.includes(matcherName) ? _this.matchers[matcherName](actualValue, arguments[0]) : _this.matchers[matcherName](actualValue, arguments); + if (wrappedMatchers.isNot) { + result.pass = !result.pass; + result.message = LogExpectError.getErrorMsg(matcherName, actualValue, arguments[0], result.message); + } + result.actualValue = actualValue; + result.checkFunc = matcherName; + if (!result.pass) { + const assertError = new AssertException(result.message); + currentRunningSpec ? currentRunningSpec.fail = assertError : currentRunningSuite.hookError = assertError; + throw assertError; + } + }; + } + } + return wrappedMatchers; + } + + apis() { + const _this = this; + return { + expect: function (actualValue) { + return _this.expect(actualValue); + } + }; + } +} + +class ReportService { + constructor(attr) { + this.id = attr.id; + } + + init(coreContext) { + this.coreContext = coreContext; + this.specService = this.coreContext.getDefaultService('spec'); + this.suiteService = this.coreContext.getDefaultService('suite'); + this.duration = 0; + } + + taskStart() { + console.info(`${TAG}[start] start run suites`); + } + + async suiteStart() { + console.info(`${TAG}[suite start]${this.suiteService.getCurrentRunningSuite().description}`); + } + + async specStart() { + console.info(`${TAG}start running case '${this.specService.currentRunningSpec.description}'`); + this.index = this.index + 1; + let spec = this.specService.currentRunningSpec; + spec.startTime = await SysTestKit.getRealTime(); + } + + async specDone() { + let msg = ''; + let spec = this.specService.currentRunningSpec; + let suite = this.suiteService.currentRunningSuite; + spec.duration = await SysTestKit.getRealTime() - spec.startTime; + suite.duration += spec.duration; + if (spec.error) { + this.formatPrint('error', spec.description + ' ; consuming ' + spec.duration + 'ms'); + this.formatPrint('errorDetail', spec.error); + } else if (spec.fail) { + this.formatPrint('fail', spec.description + ' ; consuming ' + spec.duration + 'ms'); + this.formatPrint('failDetail', spec.fail?.message); + } else { + this.formatPrint('pass', spec.description + ' ; consuming ' + spec.duration + 'ms'); + } + this.formatPrint(this.specService.currentRunningSpec.error, msg); + } + + suiteDone() { + let suite = this.suiteService.currentRunningSuite; + let message = suite.hookError ? `, ${suite.hookError?.message}` : ''; + console.info(`[suite end] ${suite.description} consuming ${suite.duration} ms${message}`); + } + + taskDone() { + let msg = ''; + let summary = this.suiteService.getSummary(); + msg = 'total cases:' + summary.total + ';failure ' + summary.failure + ',' + 'error ' + summary.error; + msg += ',pass ' + summary.pass + '; consuming ' + summary.duration + 'ms'; + console.info(`${TAG}${msg}`); + console.info(`${TAG}[end] run suites end`); + } + + incorrectFormat() { + if (this.coreContext.getDefaultService('config').filterValid.length !== 0) { + this.coreContext.getDefaultService('config').filterValid.forEach(function (item) { + console.info(`${TAG}this param ${item} is invalid`); + }); + } + } + + incorrectTestSuiteFormat() { + if (this.coreContext.getDefaultService('config').filterXdescribe.length !== 0) { + this.coreContext.getDefaultService('config').filterXdescribe.forEach(function (item) { + console.info(`${TAG}xdescribe: ${item} should not contain it`); + }) + } + } + + formatPrint(type, msg) { + switch (type) { + case 'pass': + console.info(`${TAG}[pass]${msg}`); + break; + case 'fail': + console.info(`${TAG}[fail]${msg}`); + break; + case 'failDetail': + console.info(`${TAG}[failDetail]${msg}`); + break; + case 'error': + console.info(`${TAG}[error]${msg}`); + break; + case 'errorDetail': + console.info(`${TAG}[errorDetail]${msg}`); + break; + } + } + + sleep(numberMillis) { + var now = new Date(); + var exitTime = now.getTime() + numberMillis; + while (true) { + now = new Date(); + if (now.getTime() > exitTime) { + return; + } + } + } +} + +export { + SuiteService, + SpecService, + ExpectService, + ReportService +}; diff --git a/code/UI/CalendarViewSwitch/oh_modules/@ohos/hypium/src/main/testrunner/OpenHarmonyTestRunner.ts b/code/UI/CalendarViewSwitch/oh_modules/@ohos/hypium/src/main/testrunner/OpenHarmonyTestRunner.ts new file mode 100644 index 0000000000000000000000000000000000000000..e6f4c1b12dd69714ed5a4524671abca1fbcaa58c --- /dev/null +++ b/code/UI/CalendarViewSwitch/oh_modules/@ohos/hypium/src/main/testrunner/OpenHarmonyTestRunner.ts @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2023-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 { abilityDelegatorRegistry, TestRunner } from '@kit.TestKit'; +import { BusinessError } from '@kit.BasicServicesKit'; +import { hilog } from '@kit.PerformanceAnalysisKit'; +import { resourceManager } from '@kit.LocalizationKit'; +import { util } from '@kit.ArkTS'; +import { Hypium } from '@ohos/hypium'; +import testsuite from '../test/List.test'; + +let abilityDelegator: abilityDelegatorRegistry.AbilityDelegator; +let abilityDelegatorArguments: abilityDelegatorRegistry.AbilityDelegatorArgs; +let jsonPath: string = 'mock/mock-config.json'; +let domain: number = 0x0000; //日志标识,0x0000作为测试框架的业务标识 +let tag: string = 'testTag'; //日志标识字符串,作为tag标识当前runner类下的测试行为 + +export default class OpenHarmonyTestRunner implements TestRunner { + constructor() { + } + + onPrepare() { + hilog.info(domain, tag, '%{public}s', 'OpenHarmonyTestRunner OnPrepare'); + } + + async onRun() { + hilog.info(domain, tag, '%{public}s', 'OpenHarmonyTestRunner onRun run'); + abilityDelegatorArguments = abilityDelegatorRegistry.getArguments(); + abilityDelegator = abilityDelegatorRegistry.getAbilityDelegator(); + let moduleName = abilityDelegatorArguments.parameters['-m']; + let context = abilityDelegator.getAppContext().getApplicationContext().createModuleContext(moduleName); + let mResourceManager = context.resourceManager; + await checkMock(abilityDelegator, mResourceManager); + hilog.info(domain, tag, '%{public}s', 'start run testcase!!!'); + Hypium.hypiumTest(abilityDelegator, abilityDelegatorArguments, testsuite); + hilog.info(domain, tag, '%{public}s', 'OpenHarmonyTestRunner onRun end'); + } +} + +async function checkMock(abilityDelegator: abilityDelegatorRegistry.AbilityDelegator, resourceManager: resourceManager.ResourceManager) { + let rawFile: Uint8Array; + try { + rawFile = resourceManager.getRawFileContentSync(jsonPath); + hilog.info(domain, tag, 'MockList file exists'); + let mockStr: string = util.TextDecoder.create("utf-8", { ignoreBOM: true }).decodeWithStream(rawFile); + let mockMap: Record = getMockList(mockStr); + try { + abilityDelegator.setMockList(mockMap); + } catch (error) { + let code = (error as BusinessError).code; + let message = (error as BusinessError).message; + hilog.error(domain, tag, `abilityDelegator.setMockList failed, error code: ${code}, message: ${message}.`); + } + } catch (error) { + let code = (error as BusinessError).code; + let message = (error as BusinessError).message; + hilog.error(domain, tag, `ResourceManager:callback getRawFileContent failed, error code: ${code}, message: ${message}.`); + } +} + +function getMockList(jsonStr: string) { + let jsonObj: Record = JSON.parse(jsonStr); + let map: Map = new Map(Object.entries(jsonObj)); + let mockList: Record = {}; + map.forEach((value: object, key: string) => { + let realValue: string = value['source'].toString(); + mockList[key] = realValue; + }); + hilog.info(domain, tag, '%{public}s', 'mock-json value:' + JSON.stringify(mockList) ?? ''); + return mockList; +} \ No newline at end of file diff --git a/code/UI/CitySearch/casesfeature/citysearch/BuildProfile.ets b/code/UI/CitySearch/casesfeature/citysearch/BuildProfile.ets new file mode 100644 index 0000000000000000000000000000000000000000..3a501e5ddee8ea6d28961648fc7dd314a5304bd4 --- /dev/null +++ b/code/UI/CitySearch/casesfeature/citysearch/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.0'; +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/code/UI/CitySearch/entry/oh-package-lock.json5 b/code/UI/CitySearch/entry/oh-package-lock.json5 new file mode 100644 index 0000000000000000000000000000000000000000..3c0e72c1774e58f616d9bd9244fddf7e5a836f01 --- /dev/null +++ b/code/UI/CitySearch/entry/oh-package-lock.json5 @@ -0,0 +1,18 @@ +{ + "meta": { + "stableOrder": true + }, + "lockfileVersion": 3, + "ATTENTION": "THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.", + "specifiers": { + "citysearch@../casesfeature/citysearch": "citysearch@../casesfeature/citysearch" + }, + "packages": { + "citysearch@../casesfeature/citysearch": { + "name": "citysearch", + "version": "1.0.0", + "resolved": "../casesfeature/citysearch", + "registryType": "local" + } + } +} \ No newline at end of file diff --git a/code/UI/CitySearch/oh-package-lock.json5 b/code/UI/CitySearch/oh-package-lock.json5 new file mode 100644 index 0000000000000000000000000000000000000000..f538ae290f499a46efa12e593420d47f6e9024ff --- /dev/null +++ b/code/UI/CitySearch/oh-package-lock.json5 @@ -0,0 +1,19 @@ +{ + "meta": { + "stableOrder": true + }, + "lockfileVersion": 3, + "ATTENTION": "THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.", + "specifiers": { + "@ohos/hypium@1.0.19": "@ohos/hypium@1.0.19" + }, + "packages": { + "@ohos/hypium@1.0.19": { + "name": "@ohos/hypium", + "version": "1.0.19", + "integrity": "sha512-cEjDgLFCm3cWZDeRXk7agBUkPqjWxUo6AQeiu0gEkb3J8ESqlduQLSIXeo3cCsm8U/asL7iKjF85ZyOuufAGSQ==", + "resolved": "https://ohpm.openharmony.cn/ohpm/@ohos/hypium/-/hypium-1.0.19.har", + "registryType": "ohpm" + } + } +} \ No newline at end of file diff --git a/code/UI/DragAndExchange/.gitignore b/code/UI/DragAndExchange/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..d2ff20141ceed86d87c0ea5d99481973005bab2b --- /dev/null +++ b/code/UI/DragAndExchange/.gitignore @@ -0,0 +1,12 @@ +/node_modules +/oh_modules +/local.properties +/.idea +**/build +/.hvigor +.cxx +/.clangd +/.clang-format +/.clang-tidy +**/.test +/.appanalyzer \ No newline at end of file diff --git a/code/UI/DragAndExchange/AppScope/app.json5 b/code/UI/DragAndExchange/AppScope/app.json5 new file mode 100644 index 0000000000000000000000000000000000000000..df435a8e5bb8278b5b8e85eb482838c885b68919 --- /dev/null +++ b/code/UI/DragAndExchange/AppScope/app.json5 @@ -0,0 +1,10 @@ +{ + "app": { + "bundleName": "com.samples.dragandexchange", + "vendor": "example", + "versionCode": 1000000, + "versionName": "1.0.0", + "icon": "$media:app_icon", + "label": "$string:app_name" + } +} diff --git a/code/UI/DragAndExchange/AppScope/resources/base/element/string.json b/code/UI/DragAndExchange/AppScope/resources/base/element/string.json new file mode 100644 index 0000000000000000000000000000000000000000..6f3663eee48f0f1aca46df80e530e951cbd7dcf3 --- /dev/null +++ b/code/UI/DragAndExchange/AppScope/resources/base/element/string.json @@ -0,0 +1,8 @@ +{ + "string": [ + { + "name": "app_name", + "value": "DragAndExchange" + } + ] +} diff --git a/code/UI/DragAndExchange/AppScope/resources/base/media/app_icon.png b/code/UI/DragAndExchange/AppScope/resources/base/media/app_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..a39445dc87828b76fed6d2ec470dd455c45319e3 Binary files /dev/null and b/code/UI/DragAndExchange/AppScope/resources/base/media/app_icon.png differ diff --git a/code/UI/DragAndExchange/README.md b/code/UI/DragAndExchange/README.md new file mode 100644 index 0000000000000000000000000000000000000000..1179d97c76456ad18c4861e70ee64e053c785bb8 --- /dev/null +++ b/code/UI/DragAndExchange/README.md @@ -0,0 +1,107 @@ +# Grid和List内拖拽交换子组件位置 + +### 介绍 + +本示例分别通过onItemDrop()和onDrop()回调,实现子组件在Grid和List中的子组件位置交换。 + + +### 效果图预览 + + + +**使用说明:** + +1. 拖拽Grid中子组件,到目标Grid子组件位置,进行两者位置互换。 +2. 拖拽List中子组件,到目标List子组件位置,进行两者位置互换。 + +### 实现思路 + +1. 在Grid组件中,通过editMode()打开编辑模式、通过onItemDragStart()指定拖拽时样式、通过onItemDrop()指定拖拽释放时的行为。源码参考[GridSceneView.ets](casesfeature/dragandexchange/src/main/ets/view/GridSceneView.ets)。 + + ```ts + Grid() { ... } + .editMode(true) // 设置Grid进入编辑模式 + .onItemDragStart((event: ItemDragInfo, itemIndex: number) => { // 设置拖拽过程中显示的图形 + this.movedItem = this.appInfoList[itemIndex]; // 记录原位置子组件信息 + return this.itemWhileDrag(); + }) + .onItemDrop((event: ItemDragInfo, itemIndex: number, insertIndex: number, isSuccess: boolean) => { // 拖拽释放时,触发回调 + // isSuccess=false时,说明drop的位置在grid外部;insertIndex > length时,说明有新增元素的事件发生 + if (isSuccess && insertIndex < this.appInfoList.length) { + this.changeIndex(itemIndex, insertIndex); // 互换子组件index值 + } + }) + ``` + + + +2. 在List组件中,通过ListItem的onDragStart()方法指定拖拽开始时的行为,通过List的onTouch()指定拖拽释放时的行为。源码参考[ListSceneView.ets](casesfeature/dragandexchange/src/main/ets/view/ListSceneView.ets)。 + + ```ts + List({ space: LIST_SPACE }) { + ForEach(this.appInfoList, (item: AppInfo, index) => { + ListItem() { ... } + .onDragStart(() => { + item.visible = false; // 拖拽时,设置子组件原位置图标不可见 + }) + .onTouch((event: TouchEvent) => { // 拖拽释放时,记录目标位置子组件index值 + if (event.type === TouchType.Down) { + this.dragIndex = index; + } + }) + }) + } + .onDrop((event: DragEvent, extraParams: string) => { + let jsonString: JsonObjType = JSON.parse(extraParams) as JsonObjType; // 通过参数extraParams获取原位置子组件index值 + this.changeIndex(this.dragIndex, jsonString.insertIndex); // 互换子组件index值 + this.appInfoList[jsonString.insertIndex].visible = true; // 完成互换后,设置子组件原位置图标不可见 + }) + ``` + +### 高性能知识点 + +**不涉及** + +### 工程结构&模块类型 + +``` +dragandexchange // har类型 +|---pages +|---|---Launcher.ets // 页面层-方案主页面 +|---view +|---|---GridSceneView.ets // 视图层-Grid拖拽页面 +|---|---ListSceneView.ets // 视图层-List拖拽页面 +|---model +|---|---AppInfo.ets // 模型层-App信息模型 +``` + + +### 模块依赖 + +不涉及 + +### 参考资料 + +1. [创建网格(Grid/GridItem)](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/arkts-layout-development-create-grid-0000001820999753) +2. [List](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/ts-container-list-0000001774121286) + +### 相关权限 +不涉及 + +### 约束与限制 +1.本示例仅支持在标准系统上运行,支持设备:RK3568。 + +2.本示例为Stage模型,支持API12版本SDK,SDK版本号(API Version 12 Release)。 + +3.本示例需要使用DevEco Studio 5.0.0 Release 才可编译运行。 + +### 下载 +如需单独下载本工程,执行如下命令: + +```javascript +git init +git config core.sparsecheckout true +echo code/UI/DragAndExchange/ > .git/info/sparse-checkout +git remote add origin https://gitee.com/openharmony/applications_app_samples.git +git pull origin master +``` \ No newline at end of file diff --git a/code/UI/DragAndExchange/build-profile.json5 b/code/UI/DragAndExchange/build-profile.json5 new file mode 100644 index 0000000000000000000000000000000000000000..705361c66d8750670fe9d54c43ec5bb2c617e4d7 --- /dev/null +++ b/code/UI/DragAndExchange/build-profile.json5 @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2025 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. + */ + +{ + "app": { + "signingConfigs": [], + "products": [ + { + "name": "default", + "signingConfig": "default", + "compileSdkVersion": 12, + "compatibleSdkVersion": 12, + "runtimeOS": "OpenHarmony", + "buildOption": { + "strictMode": { + "caseSensitiveCheck": true, + "useNormalizedOHMUrl": true + } + } + } + ], + "buildModeSet": [ + { + "name": "debug", + }, + { + "name": "release" + } + ] + }, + "modules": [ + { + "name": "entry", + "srcPath": "./entry", + "targets": [ + { + "name": "default", + "applyToProducts": [ + "default" + ] + } + ] + }, + { + "name": "dragandexchange", + "srcPath": "./casesfeature/dragandexchange" + } + ] +} \ No newline at end of file diff --git a/code/UI/DragAndExchange/casesfeature/dragandexchange/.gitignore b/code/UI/DragAndExchange/casesfeature/dragandexchange/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..e2713a2779c5a3e0eb879efe6115455592caeea5 --- /dev/null +++ b/code/UI/DragAndExchange/casesfeature/dragandexchange/.gitignore @@ -0,0 +1,6 @@ +/node_modules +/oh_modules +/.preview +/build +/.cxx +/.test \ No newline at end of file diff --git a/code/UI/DragAndExchange/casesfeature/dragandexchange/Index.ets b/code/UI/DragAndExchange/casesfeature/dragandexchange/Index.ets new file mode 100644 index 0000000000000000000000000000000000000000..c6be91e07125237d2e442fc921719b4503056aa5 --- /dev/null +++ b/code/UI/DragAndExchange/casesfeature/dragandexchange/Index.ets @@ -0,0 +1,16 @@ +/* + * Copyright (c) 2025 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 { Launcher } from './src/main/ets/pages/Launcher' diff --git a/code/UI/DragAndExchange/casesfeature/dragandexchange/build-profile.json5 b/code/UI/DragAndExchange/casesfeature/dragandexchange/build-profile.json5 new file mode 100644 index 0000000000000000000000000000000000000000..aefa5f6959f0e65b9347a5182401ee9f4873007a --- /dev/null +++ b/code/UI/DragAndExchange/casesfeature/dragandexchange/build-profile.json5 @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2025 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": { + }, + "buildOptionSet": [ + { + "name": "release", + "arkOptions": { + "obfuscation": { + "ruleOptions": { + "enable": false, + "files": [ + "./obfuscation-rules.txt" + ] + }, + "consumerFiles": [ + "./consumer-rules.txt" + ] + } + }, + }, + ], + "targets": [ + { + "name": "default" + }, + { + "name": "ohosTest" + } + ] +} diff --git a/code/UI/DragAndExchange/casesfeature/dragandexchange/drag_and_exchange.gif b/code/UI/DragAndExchange/casesfeature/dragandexchange/drag_and_exchange.gif new file mode 100644 index 0000000000000000000000000000000000000000..f90c29959aea4b6bd6a7a3840c426920f03aecde Binary files /dev/null and b/code/UI/DragAndExchange/casesfeature/dragandexchange/drag_and_exchange.gif differ diff --git a/code/UI/DragAndExchange/casesfeature/dragandexchange/hvigorfile.ts b/code/UI/DragAndExchange/casesfeature/dragandexchange/hvigorfile.ts new file mode 100644 index 0000000000000000000000000000000000000000..0785a511e1f806573e5208d382645d48719a0f06 --- /dev/null +++ b/code/UI/DragAndExchange/casesfeature/dragandexchange/hvigorfile.ts @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2025 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 { 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/code/UI/DragAndExchange/casesfeature/dragandexchange/obfuscation-rules.txt b/code/UI/DragAndExchange/casesfeature/dragandexchange/obfuscation-rules.txt new file mode 100644 index 0000000000000000000000000000000000000000..272efb6ca3f240859091bbbfc7c5802d52793b0b --- /dev/null +++ b/code/UI/DragAndExchange/casesfeature/dragandexchange/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/code/UI/DragAndExchange/casesfeature/dragandexchange/oh-package.json5 b/code/UI/DragAndExchange/casesfeature/dragandexchange/oh-package.json5 new file mode 100644 index 0000000000000000000000000000000000000000..9450129d2361f4c6c667d630e5b8619ee394ebf0 --- /dev/null +++ b/code/UI/DragAndExchange/casesfeature/dragandexchange/oh-package.json5 @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2025 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": "dragandexchange", + "version": "1.0.0", + "description": "Please describe the basic information.", + "main": "Index.ets", + "author": "", + "license": "Apache-2.0", + "dependencies": {} +} diff --git a/code/UI/DragAndExchange/casesfeature/dragandexchange/src/main/ets/components/MainPage.ets b/code/UI/DragAndExchange/casesfeature/dragandexchange/src/main/ets/components/MainPage.ets new file mode 100644 index 0000000000000000000000000000000000000000..7dc1a0d257a948cd6882b47f74253fdf1acaefcb --- /dev/null +++ b/code/UI/DragAndExchange/casesfeature/dragandexchange/src/main/ets/components/MainPage.ets @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2025 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. + */ + +@Component +export struct MainPage { + @State message: string = 'Hello World'; + + build() { + Row() { + Column() { + Text(this.message) + .fontSize(50) + .fontWeight(FontWeight.Bold) + } + .width('100%') + } + .height('100%') + } +} diff --git a/code/UI/DragAndExchange/casesfeature/dragandexchange/src/main/ets/model/AppInfo.ets b/code/UI/DragAndExchange/casesfeature/dragandexchange/src/main/ets/model/AppInfo.ets new file mode 100644 index 0000000000000000000000000000000000000000..00741b11504c3f647eb28092b87667f0c3a5d3cf --- /dev/null +++ b/code/UI/DragAndExchange/casesfeature/dragandexchange/src/main/ets/model/AppInfo.ets @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2025 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. + */ + +/** + * App信息,包括图标、名字、是否可见 + */ +@Observed +export class AppInfo { + icon: ResourceStr = ''; // 应用图标 + name: ResourceStr = ''; // 应用名称 + visible: boolean = true; // 是否可见 + + constructor(icon: ResourceStr = '', name: ResourceStr = '', visible: boolean = true) { + this.icon = icon; + this.name = name; + this.visible = visible; + } +} \ No newline at end of file diff --git a/code/UI/DragAndExchange/casesfeature/dragandexchange/src/main/ets/model/DateSource.ets b/code/UI/DragAndExchange/casesfeature/dragandexchange/src/main/ets/model/DateSource.ets new file mode 100644 index 0000000000000000000000000000000000000000..01d5b941bc55992baedddbaad151bf9651a54029 --- /dev/null +++ b/code/UI/DragAndExchange/casesfeature/dragandexchange/src/main/ets/model/DateSource.ets @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2025 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 { AppInfo } from './AppInfo'; + +export class DataSource implements IDataSource { + // 数据变化监听器 + private listeners: DataChangeListener[] = []; + private appInfoList: AppInfo[] = []; + + totalCount(): number { + return this.appInfoList.length; + } + + setData(index: number, data: AppInfo) { + this.appInfoList[index] = data; + } + + getData(index: number): AppInfo { + return this.appInfoList[index]; + } + + // 该方法为框架侧调用,为LazyForEach组件向其数据源处添加listener监听 + registerDataChangeListener(listener: DataChangeListener): void { + if (this.listeners.indexOf(listener) < 0) { + console.info('add listener'); + this.listeners.push(listener); + } + } + + // 该方法为框架侧调用,为对应的LazyForEach组件在数据源处去除listener监听 + unregisterDataChangeListener(listener: DataChangeListener): void { + const pos = this.listeners.indexOf(listener); + if (pos >= 0) { + console.info('remove listener'); + this.listeners.splice(pos, 1); + } + } + + /** + * 添加数据 + * @param data 虚拟数据 + */ + pushData(data: AppInfo): void { + this.appInfoList.push(data); + this.notifyDataAdd(this.appInfoList.length - 1); + } + + // 通知LazyForEach组件需要重载所有子组件 + notifyDataReload(): void { + this.listeners.forEach(listener => { + listener.onDataReloaded(); + }) + } + + // 通知LazyForEach组件需要在index对应索引处添加子组件 + notifyDataAdd(index: number): void { + this.listeners.forEach(listener => { + listener.onDataAdd(index); + }) + } + + // 通知LazyForEach组件在index对应索引处数据有变化,需要重建该子组件 + notifyDataChange(index: number): void { + this.listeners.forEach(listener => { + listener.onDataChange(index); + }) + } + // 通知LazyForEach组件将from索引和to索引处的子组件进行交换 + notifyDataMove(from: number, to: number): void { + this.listeners.forEach(listener => { + listener.onDataMove(from, to); + }) + } +} \ No newline at end of file diff --git a/code/UI/DragAndExchange/casesfeature/dragandexchange/src/main/ets/pages/Launcher.ets b/code/UI/DragAndExchange/casesfeature/dragandexchange/src/main/ets/pages/Launcher.ets new file mode 100644 index 0000000000000000000000000000000000000000..d79c16cbc6e2fabbfc77f585ec1a188b2b94bd2b --- /dev/null +++ b/code/UI/DragAndExchange/casesfeature/dragandexchange/src/main/ets/pages/Launcher.ets @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2025 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 { GridSceneView } from '../view/GridSceneView'; +import { ListSceneView } from '../view/ListSceneView'; + +/** + * 功能描述: 本示例使用position绝对定位实现应用内悬浮窗,并且通过animateTo结合curves动画曲线实现悬浮窗拖拽跟手和松手吸附边缘的弹性动画效果 + * + * 推荐场景: 悬浮窗显示场景 + * + * 核心组件: + * 1. FloatWindowView + * + * 实现步骤: + * 1. 悬浮窗组件使用Stack嵌套video布局,使用属性position绝对定位使组件悬浮,position使用Edges类型控制悬浮窗到父组件四条边的距离 + * 2. 初始化时悬浮窗的position属性设置top和right,让悬浮窗靠右 + * 3. 父组件添加onAreaChange回调,获取父组件的宽高 + * 4. 悬浮窗组件添加onTouchEvent回调,在手指按下时保存触摸点在窗口中的坐标,用于移动时悬浮窗位置的计算 + * 5. 手指移动时,获取触摸点相对于应用窗口左上角的X和Y坐标,通过计算设置悬浮窗的position坐标实现拖拽,使用默认参数的弹性跟手动画曲线curves.responsiveSpringMotion结合animateTo实现跟手动画效果 + * 6. 手指抬起时,通过判断悬浮窗中心在水平方向位于父组件中心的左侧或右侧设置悬浮窗靠左或靠右,如果悬浮窗超出内容区上下边界,则将悬浮窗设置在边界位置,使用curves.springMotion弹性动画曲线实现吸附边界时的弹性动画效果 + */ +@Component +export struct Launcher { + build() { + Column() { + GridSceneView() // 实现Grid拖拽场景 + ListSceneView() // 实现List拖拽场景 + } + .width($r("app.string.drag_and_exchange_layout_100_percent")) + .height($r("app.string.drag_and_exchange_layout_100_percent")) + .justifyContent(FlexAlign.Center) + .backgroundImage($r('app.media.drag_and_exchange_wallpaper_default')) + .backgroundImageSize(ImageSize.Cover) + } +} diff --git a/code/UI/DragAndExchange/casesfeature/dragandexchange/src/main/ets/view/GridSceneView.ets b/code/UI/DragAndExchange/casesfeature/dragandexchange/src/main/ets/view/GridSceneView.ets new file mode 100644 index 0000000000000000000000000000000000000000..3aef13f8f064650bd19764dc7a5d50f723c48f5f --- /dev/null +++ b/code/UI/DragAndExchange/casesfeature/dragandexchange/src/main/ets/view/GridSceneView.ets @@ -0,0 +1,123 @@ +/* + * Copyright (c) 2025 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 { AppInfo } from '../model/AppInfo'; +import { DataSource } from '../model/DateSource'; + +const ICON_NUM_IN_GRID: number = 15; // 示例Grid中子组件数目 + +/** + * 实现Grid场景,拖拽交换子组件位置: 通过editMode()打开编辑模式、通过onItemDragStart() + * 指定拖拽时样式、通过onItemDrop()指定拖拽释放时的行为 + */ +@Component +export struct GridSceneView { + @State movedItem: AppInfo = new AppInfo(); + @State dataSource: DataSource = new DataSource(); + + aboutToAppear() { + for (let index = 1; index <= ICON_NUM_IN_GRID; index++) { + this.dataSource.pushData(new AppInfo($r(`app.media.drag_and_exchange_ic_public_app${index}`), `App${index}`)); + } + } + + build() { + Column() { + Text($r("app.string.drag_and_exchange_grid_drag_title")) + .fontColor(Color.White) + .textAlign(TextAlign.Center) + .fontSize($r("app.string.drag_and_exchange_opt_title_font_size")) + Row() { + Grid() { + // TODO: 性能知识点:图标一次性完全显示,且禁用滑动,无需懒加载。LazyForEach可以适用在动态添加数据的场景中,参考资料: + // https://docs.openharmony.cn/pages/v4.0/zh-cn/application-dev/performance/lazyforeach_optimization.md/ + LazyForEach(this.dataSource, (item: AppInfo) => { + GridItem() { + IconWithNameView({ app: item }); + } + .width($r("app.string.drag_and_exchange_icon_square_size")) + .height($r("app.string.drag_and_exchange_layout_70")) + }, (item: AppInfo) => item.name.toString()) + } + .columnsTemplate('1fr 1fr 1fr 1fr 1fr') + .rowsTemplate('1fr 1fr 1fr') + .columnsGap($r("app.string.drag_and_exchange_layout_10")) + .rowsGap($r("app.string.drag_and_exchange_layout_10")) + .editMode(true) // TODO:知识点:设置Grid进入编辑模式,方可拖拽Grid组件内部GridItem + .onItemDragStart((event: ItemDragInfo, itemIndex: number) => { // TODO:知识点:在Grid层,通过onItemDragStart实现拖拽开始时的回调行为 + this.movedItem = this.dataSource.getData(itemIndex); + return this.itemWhileDrag(); //设置拖拽过程中显示的图形 + }) + .onItemDrop((event: ItemDragInfo, itemIndex: number, insertIndex: number, + isSuccess: boolean) => { // TODO:知识点:在Grid层,通过onItemDrop实现拖拽结束后的回调行为 + // isSuccess=false时,说明drop的位置在grid外部;insertIndex > length时,说明有新增元素的事件发生 + if (isSuccess && insertIndex < this.dataSource.totalCount()) { + let temp: AppInfo = this.dataSource.getData(itemIndex); + this.dataSource.setData(itemIndex, this.dataSource.getData(insertIndex)); + this.dataSource.setData(insertIndex, temp); + this.dataSource.notifyDataReload(); + } + }) + .padding({ + top: $r("app.string.drag_and_exchange_layout_10"), + bottom: $r("app.string.drag_and_exchange_layout_10"), + left: $r("app.string.drag_and_exchange_layout_5"), + right: $r("app.string.drag_and_exchange_layout_5") + }) + } + .width($r("app.string.drag_and_exchange_layout_90_percent")) + .height($r("app.string.drag_and_exchange_layout_250")) + .borderRadius($r("app.string.drag_and_exchange_layout_20")) + .opacity($r("app.string.drag_and_exchange_background_opacity")) + .backgroundColor($r('app.color.drag_and_exchange_background_color')) + } + } + + /** + * 设置GridItem拖拽过程中显示的图形 + */ + @Builder + itemWhileDrag() { + IconWithNameView({ app: this.movedItem }) + .width($r("app.string.drag_and_exchange_icon_square_size")) + .height($r("app.string.drag_and_exchange_icon_square_size")) + } +} + +/** + * App自定义组件 + */ +@Component +struct IconWithNameView { + @ObjectLink app: AppInfo + + build() { + Column() { + Image(this.app.icon) + .id(`${this.app.name}`) + .width($r("app.string.drag_and_exchange_icon_square_size")) + .height($r("app.string.drag_and_exchange_icon_square_size")) + .objectFit(ImageFit.Cover) + .borderRadius($r("app.string.drag_and_exchange_layout_10")) + .draggable(false) // TODO:知识点:保持默认值true时,图片有默认拖拽效果,会影响Grid子组件拖拽判断,所以修改为false + Text(this.app.name) + .width($r("app.string.drag_and_exchange_icon_square_size")) + .fontColor(Color.White) + .textAlign(TextAlign.Center) + .margin({ top: $r("app.string.drag_and_exchange_layout_1") }) + .fontSize($r("app.string.drag_and_exchange_app_name_font_size")) + } + } +} diff --git a/code/UI/DragAndExchange/casesfeature/dragandexchange/src/main/ets/view/ListSceneView.ets b/code/UI/DragAndExchange/casesfeature/dragandexchange/src/main/ets/view/ListSceneView.ets new file mode 100644 index 0000000000000000000000000000000000000000..19d0477781f6996a0e3444fc9d96949273dd1848 --- /dev/null +++ b/code/UI/DragAndExchange/casesfeature/dragandexchange/src/main/ets/view/ListSceneView.ets @@ -0,0 +1,140 @@ +/* + * Copyright (c) 2025 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 { AppInfo } from '../model/AppInfo'; +import { DataSource } from '../model/DateSource'; + +const ICON_NUM_IN_LIST: number = 4; // 示例List中子组件数目 +const LIST_SPACE: number = 30; // 列表默认间隔 + +/** + * 实现List场景,拖拽交换子组件位置: 通过ListItem的onDragStart()方法指定拖拽开始时的行为,通过List的onTouch()指定拖拽释放时的行为。 + */ +@Component +export struct ListSceneView { + @State dataSource: DataSource = new DataSource(); + @State dragIndex: number = 0; + + aboutToAppear() { + for (let index = 0; index < ICON_NUM_IN_LIST; index++) { + this.dataSource.pushData(new AppInfo($r(`app.media.drag_and_exchange_ic_public_game${index + 1}`), + `Item${index + 1}`, true)); + } + } + + changeIndex(index1: number, index2: number) { + let temp: AppInfo = this.dataSource.getData(index1); + this.dataSource.setData(index1, this.dataSource.getData(index2)); + this.dataSource.setData(index2, temp); + } + + build() { + Column() { + Text($r("app.string.drag_and_exchange_list_drag_title")) + .fontColor(Color.White) + .textAlign(TextAlign.Center) + .fontSize($r("app.string.drag_and_exchange_opt_title_font_size")) + Row() { // 仅靠List实现背景框,padding调整样式后,互换时可能错位 + List({ space: LIST_SPACE }) { + // TODO: 性能知识点:图标一次性完全显示,且禁用滑动,无需懒加载。LazyForEach可以适用在动态添加数据的场景中,参考资料: + // https://docs.openharmony.cn/pages/v4.0/zh-cn/application-dev/performance/lazyforeach_optimization.md/ + LazyForEach(this.dataSource, (item: AppInfo, index) => { + ListItem() { + Column() { + IconNoNameView({ app: item }) + } + } + .onDragStart((event: DragEvent, extraParams: string) => { // TODO:知识点:在ListItem层,通过onDragStart实现拖拽开始时的回调行为 + item.visible = false; // 拖拽时,设置子组件原位置图标不可见 + // 记录目标位置子组件index值 + this.dragIndex = (JSON.parse(extraParams) as JsonObjType).selectedIndex; + }) + .onDragEnd(() => { + item.visible = true; + }) + }, (item: AppInfo) => item.name.toString()) + } + .scrollBar(BarState.Off) + .height($r("app.string.drag_and_exchange_layout_90")) + .listDirection(Axis.Horizontal) + .alignListItem(ListItemAlign.Center) + .onDrop((event: DragEvent, extraParams: string) => { // TODO:知识点:在List层,通过onDrop实现拖拽结束后的回调行为 + let insertIndex: number = (JSON.parse(extraParams) as JsonObjType).insertIndex; // 通过参数extraParams获取原位置子组件index值 + if (insertIndex >= this.dataSource.totalCount()) { + return; + } + this.changeIndex(this.dragIndex, insertIndex); // 互换子组件index值 + this.dataSource.notifyDataReload(); + }) + .enableScrollInteraction(false) // 禁用滑动 + .alignListItem(ListItemAlign.Center) + .padding({ + top: $r("app.string.drag_and_exchange_layout_10"), + bottom: $r("app.string.drag_and_exchange_layout_10"), + left: $r("app.string.drag_and_exchange_layout_15"), + right: $r("app.string.drag_and_exchange_layout_15") + }) + } + .justifyContent(FlexAlign.Center) + .height($r("app.string.drag_and_exchange_layout_90")) + .width($r("app.string.drag_and_exchange_layout_90_percent")) + .borderRadius($r("app.string.drag_and_exchange_layout_20")) + .opacity($r("app.string.drag_and_exchange_background_opacity")) + .backgroundColor($r('app.color.drag_and_exchange_background_color')) + } + .margin({ top: $r("app.string.drag_and_exchange_layout_20") }) + } +} + +/** + * 无名字App自定义组件 + */ +@Component +struct IconNoNameView { + @ObjectLink app: AppInfo; + + build() { + Column() { + Image(this.app.icon) + .id(`${this.app.name}`) + .width($r("app.string.drag_and_exchange_icon_square_size")) + .height($r("app.string.drag_and_exchange_icon_square_size")) + .objectFit(ImageFit.Cover) + .borderRadius($r("app.string.drag_and_exchange_layout_10")) + .draggable(false) // TODO:知识点:保持默认值true时,图片有默认拖拽效果,会影响List子组件拖拽动效,所以修改为false + Text(this.app.name) + .width($r("app.string.drag_and_exchange_icon_square_size")) + .fontColor(Color.White) + .textAlign(TextAlign.Center) + .margin({ top: $r("app.string.drag_and_exchange_layout_1") }) + .fontSize($r("app.string.drag_and_exchange_app_name_font_size")) + } + .visibility(this.app.visible ? Visibility.Visible : + Visibility.Hidden) // 消失时需要占位,所以使用显隐控制而非条件渲染。(条件渲染与显隐控制区别,参考资料:https://docs.openharmony.cn/pages/v4.0/zh-cn/application-dev/performance/proper-choice-between-if-and-visibility.md/) + } +} + +/** + * 封装处理处理JSON对象的类 + */ +class JsonObjType { + public insertIndex: number; + public selectedIndex: number; + + constructor(insertIndex: number, selectedIndex: number) { + this.insertIndex = insertIndex; + this.selectedIndex = selectedIndex; + } +} \ No newline at end of file diff --git a/code/UI/DragAndExchange/casesfeature/dragandexchange/src/main/module.json5 b/code/UI/DragAndExchange/casesfeature/dragandexchange/src/main/module.json5 new file mode 100644 index 0000000000000000000000000000000000000000..82488d1e7a2cf601b805d944bd4dc4ade39679ae --- /dev/null +++ b/code/UI/DragAndExchange/casesfeature/dragandexchange/src/main/module.json5 @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2025 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. + */ + +{ + "module": { + "name": "dragandexchange", + "type": "har", + "deviceTypes": [ + "default", + "tablet" + ] + } +} diff --git a/code/UI/DragAndExchange/casesfeature/dragandexchange/src/main/resources/base/element/color.json b/code/UI/DragAndExchange/casesfeature/dragandexchange/src/main/resources/base/element/color.json new file mode 100644 index 0000000000000000000000000000000000000000..a63243bbdb8bc9206c9f733a42b8b8328695faae --- /dev/null +++ b/code/UI/DragAndExchange/casesfeature/dragandexchange/src/main/resources/base/element/color.json @@ -0,0 +1,8 @@ +{ + "color": [ + { + "name": "drag_and_exchange_background_color", + "value": "#a9c2cc" + } + ] +} \ No newline at end of file diff --git a/code/UI/DragAndExchange/casesfeature/dragandexchange/src/main/resources/base/element/string.json b/code/UI/DragAndExchange/casesfeature/dragandexchange/src/main/resources/base/element/string.json new file mode 100644 index 0000000000000000000000000000000000000000..57c97f100fb07ffa4bb8fd2625cfe907e7bf3877 --- /dev/null +++ b/code/UI/DragAndExchange/casesfeature/dragandexchange/src/main/resources/base/element/string.json @@ -0,0 +1,72 @@ +{ + "string": [ + { + "name": "page_show", + "value": "page from package" + }, + { + "name": "drag_and_exchange_grid_drag_title", + "value": "Grid拖动互换子组件" + }, + { + "name": "drag_and_exchange_list_drag_title", + "value": "List拖动互换子组件" + }, + { + "name": "drag_and_exchange_layout_100_percent", + "value": "100%" + }, + { + "name": "drag_and_exchange_layout_90_percent", + "value": "90%" + }, + { + "name": "drag_and_exchange_layout_250", + "value": "250" + }, + { + "name": "drag_and_exchange_layout_70", + "value": "70" + }, + { + "name": "drag_and_exchange_layout_90", + "value": "90" + }, + { + "name": "drag_and_exchange_icon_square_size", + "value": "50" + }, + { + "name": "drag_and_exchange_layout_20", + "value": "20" + }, + { + "name": "drag_and_exchange_layout_15", + "value": "15" + }, + { + "name": "drag_and_exchange_layout_10", + "value": "10" + }, + { + "name": "drag_and_exchange_layout_5", + "value": "5" + }, + { + "name": "drag_and_exchange_layout_1", + "value": "1" + }, + { + "name": "drag_and_exchange_opt_title_font_size", + "value": "25" + }, + { + "name": "drag_and_exchange_app_name_font_size", + "value": "16" + }, + { + "name": "drag_and_exchange_background_opacity", + "value": "0.8" + } + ] +} diff --git a/code/UI/DragAndExchange/casesfeature/dragandexchange/src/main/resources/base/media/drag_and_exchange_ic_public_app1.png b/code/UI/DragAndExchange/casesfeature/dragandexchange/src/main/resources/base/media/drag_and_exchange_ic_public_app1.png new file mode 100644 index 0000000000000000000000000000000000000000..af06b54d499b84d062e3324d1aac11d1f07253d0 Binary files /dev/null and b/code/UI/DragAndExchange/casesfeature/dragandexchange/src/main/resources/base/media/drag_and_exchange_ic_public_app1.png differ diff --git a/code/UI/DragAndExchange/casesfeature/dragandexchange/src/main/resources/base/media/drag_and_exchange_ic_public_app10.png b/code/UI/DragAndExchange/casesfeature/dragandexchange/src/main/resources/base/media/drag_and_exchange_ic_public_app10.png new file mode 100644 index 0000000000000000000000000000000000000000..be5fbb520460f0d7a84e2504a20618f9a9f7ca3b Binary files /dev/null and b/code/UI/DragAndExchange/casesfeature/dragandexchange/src/main/resources/base/media/drag_and_exchange_ic_public_app10.png differ diff --git a/code/UI/DragAndExchange/casesfeature/dragandexchange/src/main/resources/base/media/drag_and_exchange_ic_public_app11.png b/code/UI/DragAndExchange/casesfeature/dragandexchange/src/main/resources/base/media/drag_and_exchange_ic_public_app11.png new file mode 100644 index 0000000000000000000000000000000000000000..1b653fff3b41b9e47dd37a6409836fe3c43714b5 Binary files /dev/null and b/code/UI/DragAndExchange/casesfeature/dragandexchange/src/main/resources/base/media/drag_and_exchange_ic_public_app11.png differ diff --git a/code/UI/DragAndExchange/casesfeature/dragandexchange/src/main/resources/base/media/drag_and_exchange_ic_public_app12.png b/code/UI/DragAndExchange/casesfeature/dragandexchange/src/main/resources/base/media/drag_and_exchange_ic_public_app12.png new file mode 100644 index 0000000000000000000000000000000000000000..071ce9bb09eb0c0c50a58233ec471bcb4a16c150 Binary files /dev/null and b/code/UI/DragAndExchange/casesfeature/dragandexchange/src/main/resources/base/media/drag_and_exchange_ic_public_app12.png differ diff --git a/code/UI/DragAndExchange/casesfeature/dragandexchange/src/main/resources/base/media/drag_and_exchange_ic_public_app13.png b/code/UI/DragAndExchange/casesfeature/dragandexchange/src/main/resources/base/media/drag_and_exchange_ic_public_app13.png new file mode 100644 index 0000000000000000000000000000000000000000..4b34a1c988d0fdd4eb190c07c87d37dd351d7c05 Binary files /dev/null and b/code/UI/DragAndExchange/casesfeature/dragandexchange/src/main/resources/base/media/drag_and_exchange_ic_public_app13.png differ diff --git a/code/UI/DragAndExchange/casesfeature/dragandexchange/src/main/resources/base/media/drag_and_exchange_ic_public_app14.png b/code/UI/DragAndExchange/casesfeature/dragandexchange/src/main/resources/base/media/drag_and_exchange_ic_public_app14.png new file mode 100644 index 0000000000000000000000000000000000000000..4e2e6caaec42d2eadb165336741be66a212cf553 Binary files /dev/null and b/code/UI/DragAndExchange/casesfeature/dragandexchange/src/main/resources/base/media/drag_and_exchange_ic_public_app14.png differ diff --git a/code/UI/DragAndExchange/casesfeature/dragandexchange/src/main/resources/base/media/drag_and_exchange_ic_public_app15.png b/code/UI/DragAndExchange/casesfeature/dragandexchange/src/main/resources/base/media/drag_and_exchange_ic_public_app15.png new file mode 100644 index 0000000000000000000000000000000000000000..9c346dc57e3db10cd826a12fe21f1bf9d793cb8d Binary files /dev/null and b/code/UI/DragAndExchange/casesfeature/dragandexchange/src/main/resources/base/media/drag_and_exchange_ic_public_app15.png differ diff --git a/code/UI/DragAndExchange/casesfeature/dragandexchange/src/main/resources/base/media/drag_and_exchange_ic_public_app2.png b/code/UI/DragAndExchange/casesfeature/dragandexchange/src/main/resources/base/media/drag_and_exchange_ic_public_app2.png new file mode 100644 index 0000000000000000000000000000000000000000..ed714aa652869dd11dfc88c121b682571b29c8bd Binary files /dev/null and b/code/UI/DragAndExchange/casesfeature/dragandexchange/src/main/resources/base/media/drag_and_exchange_ic_public_app2.png differ diff --git a/code/UI/DragAndExchange/casesfeature/dragandexchange/src/main/resources/base/media/drag_and_exchange_ic_public_app3.png b/code/UI/DragAndExchange/casesfeature/dragandexchange/src/main/resources/base/media/drag_and_exchange_ic_public_app3.png new file mode 100644 index 0000000000000000000000000000000000000000..ddbea05998261e0913a99715f1d40223e2fb4fae Binary files /dev/null and b/code/UI/DragAndExchange/casesfeature/dragandexchange/src/main/resources/base/media/drag_and_exchange_ic_public_app3.png differ diff --git a/code/UI/DragAndExchange/casesfeature/dragandexchange/src/main/resources/base/media/drag_and_exchange_ic_public_app4.png b/code/UI/DragAndExchange/casesfeature/dragandexchange/src/main/resources/base/media/drag_and_exchange_ic_public_app4.png new file mode 100644 index 0000000000000000000000000000000000000000..753e8b834c01afbf81811b94d5f3b9aa62f48698 Binary files /dev/null and b/code/UI/DragAndExchange/casesfeature/dragandexchange/src/main/resources/base/media/drag_and_exchange_ic_public_app4.png differ diff --git a/code/UI/DragAndExchange/casesfeature/dragandexchange/src/main/resources/base/media/drag_and_exchange_ic_public_app5.png b/code/UI/DragAndExchange/casesfeature/dragandexchange/src/main/resources/base/media/drag_and_exchange_ic_public_app5.png new file mode 100644 index 0000000000000000000000000000000000000000..80c6fc06ee1d7f73ace0d65e7145d8cee61886f8 Binary files /dev/null and b/code/UI/DragAndExchange/casesfeature/dragandexchange/src/main/resources/base/media/drag_and_exchange_ic_public_app5.png differ diff --git a/code/UI/DragAndExchange/casesfeature/dragandexchange/src/main/resources/base/media/drag_and_exchange_ic_public_app6.png b/code/UI/DragAndExchange/casesfeature/dragandexchange/src/main/resources/base/media/drag_and_exchange_ic_public_app6.png new file mode 100644 index 0000000000000000000000000000000000000000..7d1d7edd6bb40d01be4d5de5b6fe83a553b1e54d Binary files /dev/null and b/code/UI/DragAndExchange/casesfeature/dragandexchange/src/main/resources/base/media/drag_and_exchange_ic_public_app6.png differ diff --git a/code/UI/DragAndExchange/casesfeature/dragandexchange/src/main/resources/base/media/drag_and_exchange_ic_public_app7.png b/code/UI/DragAndExchange/casesfeature/dragandexchange/src/main/resources/base/media/drag_and_exchange_ic_public_app7.png new file mode 100644 index 0000000000000000000000000000000000000000..2b73f2db2199d39017a953bac19f2a62a682d988 Binary files /dev/null and b/code/UI/DragAndExchange/casesfeature/dragandexchange/src/main/resources/base/media/drag_and_exchange_ic_public_app7.png differ diff --git a/code/UI/DragAndExchange/casesfeature/dragandexchange/src/main/resources/base/media/drag_and_exchange_ic_public_app8.png b/code/UI/DragAndExchange/casesfeature/dragandexchange/src/main/resources/base/media/drag_and_exchange_ic_public_app8.png new file mode 100644 index 0000000000000000000000000000000000000000..f01981332e7d08dfcec5a4dbd70f3a1599e8c2db Binary files /dev/null and b/code/UI/DragAndExchange/casesfeature/dragandexchange/src/main/resources/base/media/drag_and_exchange_ic_public_app8.png differ diff --git a/code/UI/DragAndExchange/casesfeature/dragandexchange/src/main/resources/base/media/drag_and_exchange_ic_public_app9.png b/code/UI/DragAndExchange/casesfeature/dragandexchange/src/main/resources/base/media/drag_and_exchange_ic_public_app9.png new file mode 100644 index 0000000000000000000000000000000000000000..251fe00b3c6fb4a356ba23d071d7c9d627d5d1d2 Binary files /dev/null and b/code/UI/DragAndExchange/casesfeature/dragandexchange/src/main/resources/base/media/drag_and_exchange_ic_public_app9.png differ diff --git a/code/UI/DragAndExchange/casesfeature/dragandexchange/src/main/resources/base/media/drag_and_exchange_ic_public_game1.png b/code/UI/DragAndExchange/casesfeature/dragandexchange/src/main/resources/base/media/drag_and_exchange_ic_public_game1.png new file mode 100644 index 0000000000000000000000000000000000000000..97d743fb0ed625811021d3dc2b11cd9c551ef55c Binary files /dev/null and b/code/UI/DragAndExchange/casesfeature/dragandexchange/src/main/resources/base/media/drag_and_exchange_ic_public_game1.png differ diff --git a/code/UI/DragAndExchange/casesfeature/dragandexchange/src/main/resources/base/media/drag_and_exchange_ic_public_game2.png b/code/UI/DragAndExchange/casesfeature/dragandexchange/src/main/resources/base/media/drag_and_exchange_ic_public_game2.png new file mode 100644 index 0000000000000000000000000000000000000000..9284d4ea522b82304306b58caa0fc8a3b390e128 Binary files /dev/null and b/code/UI/DragAndExchange/casesfeature/dragandexchange/src/main/resources/base/media/drag_and_exchange_ic_public_game2.png differ diff --git a/code/UI/DragAndExchange/casesfeature/dragandexchange/src/main/resources/base/media/drag_and_exchange_ic_public_game3.png b/code/UI/DragAndExchange/casesfeature/dragandexchange/src/main/resources/base/media/drag_and_exchange_ic_public_game3.png new file mode 100644 index 0000000000000000000000000000000000000000..0405eaa7f7177515eead0de38ac0d4dcfe34e2f7 Binary files /dev/null and b/code/UI/DragAndExchange/casesfeature/dragandexchange/src/main/resources/base/media/drag_and_exchange_ic_public_game3.png differ diff --git a/code/UI/DragAndExchange/casesfeature/dragandexchange/src/main/resources/base/media/drag_and_exchange_ic_public_game4.png b/code/UI/DragAndExchange/casesfeature/dragandexchange/src/main/resources/base/media/drag_and_exchange_ic_public_game4.png new file mode 100644 index 0000000000000000000000000000000000000000..23a5cfbd6b53fd96197be042dfd92e1c86104914 Binary files /dev/null and b/code/UI/DragAndExchange/casesfeature/dragandexchange/src/main/resources/base/media/drag_and_exchange_ic_public_game4.png differ diff --git a/code/UI/DragAndExchange/casesfeature/dragandexchange/src/main/resources/base/media/drag_and_exchange_wallpaper_default.jpg b/code/UI/DragAndExchange/casesfeature/dragandexchange/src/main/resources/base/media/drag_and_exchange_wallpaper_default.jpg new file mode 100644 index 0000000000000000000000000000000000000000..54b99a3a86507a98b248d417f4222c61f0a12971 Binary files /dev/null and b/code/UI/DragAndExchange/casesfeature/dragandexchange/src/main/resources/base/media/drag_and_exchange_wallpaper_default.jpg differ diff --git a/code/UI/DragAndExchange/casesfeature/dragandexchange/src/main/resources/rawfile/routerMap/dragandexchange.json b/code/UI/DragAndExchange/casesfeature/dragandexchange/src/main/resources/rawfile/routerMap/dragandexchange.json new file mode 100644 index 0000000000000000000000000000000000000000..6148ae29ddd40bb058162651a429e25e9429bb31 --- /dev/null +++ b/code/UI/DragAndExchange/casesfeature/dragandexchange/src/main/resources/rawfile/routerMap/dragandexchange.json @@ -0,0 +1,10 @@ +{ + "routerMap": [ + { + "name": "dragandexchange/LauncherComponent", + "pageModule": "dragandexchange", + "pageSourceFile": "src/main/ets/generated/RouterBuilder.ets", + "registerFunction": "launcherComponentRegister" + } + ] +} \ No newline at end of file diff --git a/code/UI/DragAndExchange/casesfeature/dragandexchange/src/ohosTest/ets/test/Ability.test.ets b/code/UI/DragAndExchange/casesfeature/dragandexchange/src/ohosTest/ets/test/Ability.test.ets new file mode 100644 index 0000000000000000000000000000000000000000..0f8ce9a2c012f8fe36114cef65216ef0b6254f41 --- /dev/null +++ b/code/UI/DragAndExchange/casesfeature/dragandexchange/src/ohosTest/ets/test/Ability.test.ets @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2025 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 { 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/code/UI/DragAndExchange/casesfeature/dragandexchange/src/ohosTest/ets/test/List.test.ets b/code/UI/DragAndExchange/casesfeature/dragandexchange/src/ohosTest/ets/test/List.test.ets new file mode 100644 index 0000000000000000000000000000000000000000..1eac52fcebe8958e19a7b8fed2e8f39c520a3e42 --- /dev/null +++ b/code/UI/DragAndExchange/casesfeature/dragandexchange/src/ohosTest/ets/test/List.test.ets @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2025 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 abilityTest from './Ability.test'; + +export default function testsuite() { + abilityTest(); +} \ No newline at end of file diff --git a/code/UI/DragAndExchange/casesfeature/dragandexchange/src/ohosTest/module.json5 b/code/UI/DragAndExchange/casesfeature/dragandexchange/src/ohosTest/module.json5 new file mode 100644 index 0000000000000000000000000000000000000000..0d2052f0ef91a710622e32d9e4d3b635764866ca --- /dev/null +++ b/code/UI/DragAndExchange/casesfeature/dragandexchange/src/ohosTest/module.json5 @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2025 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. + */ + +{ + "module": { + "name": "dragandexchange_test", + "type": "feature", + "deviceTypes": [ + "default", + "tablet" + ], + "deliveryWithInstall": true, + "installationFree": false + } +} diff --git a/code/UI/DragAndExchange/casesfeature/dragandexchange/src/test/List.test.ets b/code/UI/DragAndExchange/casesfeature/dragandexchange/src/test/List.test.ets new file mode 100644 index 0000000000000000000000000000000000000000..f1186b1f53c3a70930921c5dbd1417332bec56c9 --- /dev/null +++ b/code/UI/DragAndExchange/casesfeature/dragandexchange/src/test/List.test.ets @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2025 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 localUnitTest from './LocalUnit.test'; + +export default function testsuite() { + localUnitTest(); +} \ No newline at end of file diff --git a/code/UI/DragAndExchange/casesfeature/dragandexchange/src/test/LocalUnit.test.ets b/code/UI/DragAndExchange/casesfeature/dragandexchange/src/test/LocalUnit.test.ets new file mode 100644 index 0000000000000000000000000000000000000000..7fc57c77dbf76d8df08a2b802a55b948e3fcf968 --- /dev/null +++ b/code/UI/DragAndExchange/casesfeature/dragandexchange/src/test/LocalUnit.test.ets @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2025 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 { describe, beforeAll, beforeEach, afterEach, afterAll, it, expect } from '@ohos/hypium'; + +export default function localUnitTest() { + describe('localUnitTest', () => { + // 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. + 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/code/UI/DragAndExchange/code-linter.json5 b/code/UI/DragAndExchange/code-linter.json5 new file mode 100644 index 0000000000000000000000000000000000000000..28586467ee7a761c737d8654a73aed6fddbc3c71 --- /dev/null +++ b/code/UI/DragAndExchange/code-linter.json5 @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2025 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. + */ + +{ + "files": [ + "**/*.ets" + ], + "ignore": [ + "**/src/ohosTest/**/*", + "**/src/test/**/*", + "**/src/mock/**/*", + "**/node_modules/**/*", + "**/oh_modules/**/*", + "**/build/**/*", + "**/.preview/**/*" + ], + "ruleSet": [ + "plugin:@performance/recommended", + "plugin:@typescript-eslint/recommended" + ], + "rules": { + } +} \ No newline at end of file diff --git a/code/UI/DragAndExchange/entry/.gitignore b/code/UI/DragAndExchange/entry/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..e2713a2779c5a3e0eb879efe6115455592caeea5 --- /dev/null +++ b/code/UI/DragAndExchange/entry/.gitignore @@ -0,0 +1,6 @@ +/node_modules +/oh_modules +/.preview +/build +/.cxx +/.test \ No newline at end of file diff --git a/code/UI/DragAndExchange/entry/build-profile.json5 b/code/UI/DragAndExchange/entry/build-profile.json5 new file mode 100644 index 0000000000000000000000000000000000000000..e7569e3056e27af38e9991b7ea73ec10f3ba8a05 --- /dev/null +++ b/code/UI/DragAndExchange/entry/build-profile.json5 @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2025 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": { + }, + "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/code/UI/DragAndExchange/entry/hvigorfile.ts b/code/UI/DragAndExchange/entry/hvigorfile.ts new file mode 100644 index 0000000000000000000000000000000000000000..e4f43d54667f8327c367c8096bd08bb8c75aff54 --- /dev/null +++ b/code/UI/DragAndExchange/entry/hvigorfile.ts @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2025 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 { 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/code/UI/DragAndExchange/entry/obfuscation-rules.txt b/code/UI/DragAndExchange/entry/obfuscation-rules.txt new file mode 100644 index 0000000000000000000000000000000000000000..272efb6ca3f240859091bbbfc7c5802d52793b0b --- /dev/null +++ b/code/UI/DragAndExchange/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/code/UI/DragAndExchange/entry/oh-package-lock.json5 b/code/UI/DragAndExchange/entry/oh-package-lock.json5 new file mode 100644 index 0000000000000000000000000000000000000000..325ba95f285e4132a2f48f1cdfc1f7faf3332f07 --- /dev/null +++ b/code/UI/DragAndExchange/entry/oh-package-lock.json5 @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2025 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. + */ + +{ + "meta": { + "stableOrder": true + }, + "lockfileVersion": 3, + "ATTENTION": "THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.", + "specifiers": { + "dragandexchange@../casesfeature/dragandexchange": "dragandexchange@../casesfeature/dragandexchange" + }, + "packages": { + "dragandexchange@../casesfeature/dragandexchange": { + "name": "dragandexchange", + "version": "1.0.0", + "resolved": "../casesfeature/dragandexchange", + "registryType": "local" + } + } +} \ No newline at end of file diff --git a/code/UI/DragAndExchange/entry/oh-package.json5 b/code/UI/DragAndExchange/entry/oh-package.json5 new file mode 100644 index 0000000000000000000000000000000000000000..d3c9181b773041b3e04c4167b1b3ab605d5300f4 --- /dev/null +++ b/code/UI/DragAndExchange/entry/oh-package.json5 @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2025 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": "entry", + "version": "1.0.0", + "description": "Please describe the basic information.", + "main": "", + "author": "", + "license": "", + "dependencies": { + "dragandexchange": "file:../casesfeature/dragandexchange" + } +} + diff --git a/code/UI/DragAndExchange/entry/src/main/ets/entryability/EntryAbility.ets b/code/UI/DragAndExchange/entry/src/main/ets/entryability/EntryAbility.ets new file mode 100644 index 0000000000000000000000000000000000000000..edc2839f203ba057c186e19b0cbbbf80c8faa8b3 --- /dev/null +++ b/code/UI/DragAndExchange/entry/src/main/ets/entryability/EntryAbility.ets @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2025 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 { AbilityConstant, ConfigurationConstant, UIAbility, Want } from '@kit.AbilityKit'; +import { hilog } from '@kit.PerformanceAnalysisKit'; +import { window } from '@kit.ArkUI'; + +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(0x0000, 'testTag', '%{public}s', 'Ability onCreate'); + } + + onDestroy(): void { + hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onDestroy'); + } + + onWindowStageCreate(windowStage: window.WindowStage): void { + // Main window is created, set main page for this ability + hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onWindowStageCreate'); + + windowStage.loadContent('pages/Index', (err) => { + if (err.code) { + hilog.error(0x0000, 'testTag', 'Failed to load the content. Cause: %{public}s', JSON.stringify(err) ?? ''); + return; + } + hilog.info(0x0000, 'testTag', 'Succeeded in loading the content.'); + }); + } + + onWindowStageDestroy(): void { + // Main window is destroyed, release UI related resources + hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onWindowStageDestroy'); + } + + onForeground(): void { + // Ability has brought to foreground + hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onForeground'); + } + + onBackground(): void { + // Ability has back to background + hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onBackground'); + } +} \ No newline at end of file diff --git a/code/UI/DragAndExchange/entry/src/main/ets/entrybackupability/EntryBackupAbility.ets b/code/UI/DragAndExchange/entry/src/main/ets/entrybackupability/EntryBackupAbility.ets new file mode 100644 index 0000000000000000000000000000000000000000..b1e212947256c5533c7b06285a597c94f840a6e3 --- /dev/null +++ b/code/UI/DragAndExchange/entry/src/main/ets/entrybackupability/EntryBackupAbility.ets @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2025 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 { hilog } from '@kit.PerformanceAnalysisKit'; +import { BackupExtensionAbility, BundleVersion } from '@kit.CoreFileKit'; + +export default class EntryBackupAbility extends BackupExtensionAbility { + async onBackup() { + hilog.info(0x0000, 'testTag', 'onBackup ok'); + } + + async onRestore(bundleVersion: BundleVersion) { + hilog.info(0x0000, 'testTag', 'onRestore ok %{public}s', JSON.stringify(bundleVersion)); + } +} \ No newline at end of file diff --git a/code/UI/DragAndExchange/entry/src/main/ets/pages/Index.ets b/code/UI/DragAndExchange/entry/src/main/ets/pages/Index.ets new file mode 100644 index 0000000000000000000000000000000000000000..209336138b94635b2aa6963ab4fdd1a44eb6969a --- /dev/null +++ b/code/UI/DragAndExchange/entry/src/main/ets/pages/Index.ets @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2025 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 { Launcher } from 'dragandexchange'; + +@Entry +@Component +struct Index { + @State message: string = 'Hello World'; + + build() { + Column() { + /** + * 功能描述:本示例介绍Grid和List内拖拽交换子组件位置的使用:通过onItemDrop()和onDrop()回调,实现子组件在Grid和List中的子组件位置交换。 + * 参数介绍:无 + */ + Launcher() + } + .height('100%') + .width('100%') + } +} \ No newline at end of file diff --git a/code/UI/DragAndExchange/entry/src/main/module.json5 b/code/UI/DragAndExchange/entry/src/main/module.json5 new file mode 100644 index 0000000000000000000000000000000000000000..4144486d1af4c03b0d767cce1cda86fc0d697f91 --- /dev/null +++ b/code/UI/DragAndExchange/entry/src/main/module.json5 @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2025 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. + */ + +{ + "module": { + "name": "entry", + "type": "entry", + "description": "$string:module_desc", + "mainElement": "EntryAbility", + "deviceTypes": [ + "default", + "tablet" + ], + "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/code/UI/DragAndExchange/entry/src/main/resources/base/element/color.json b/code/UI/DragAndExchange/entry/src/main/resources/base/element/color.json new file mode 100644 index 0000000000000000000000000000000000000000..3c712962da3c2751c2b9ddb53559afcbd2b54a02 --- /dev/null +++ b/code/UI/DragAndExchange/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/code/UI/DragAndExchange/entry/src/main/resources/base/element/string.json b/code/UI/DragAndExchange/entry/src/main/resources/base/element/string.json new file mode 100644 index 0000000000000000000000000000000000000000..f94595515a99e0c828807e243494f57f09251930 --- /dev/null +++ b/code/UI/DragAndExchange/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/code/UI/DragAndExchange/entry/src/main/resources/base/media/background.png b/code/UI/DragAndExchange/entry/src/main/resources/base/media/background.png new file mode 100644 index 0000000000000000000000000000000000000000..f939c9fa8cc8914832e602198745f592a0dfa34d Binary files /dev/null and b/code/UI/DragAndExchange/entry/src/main/resources/base/media/background.png differ diff --git a/code/UI/DragAndExchange/entry/src/main/resources/base/media/foreground.png b/code/UI/DragAndExchange/entry/src/main/resources/base/media/foreground.png new file mode 100644 index 0000000000000000000000000000000000000000..4483ddad1f079e1089d685bd204ee1cfe1d01902 Binary files /dev/null and b/code/UI/DragAndExchange/entry/src/main/resources/base/media/foreground.png differ diff --git a/code/UI/DragAndExchange/entry/src/main/resources/base/media/layered_image.json b/code/UI/DragAndExchange/entry/src/main/resources/base/media/layered_image.json new file mode 100644 index 0000000000000000000000000000000000000000..fb49920440fb4d246c82f9ada275e26123a2136a --- /dev/null +++ b/code/UI/DragAndExchange/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/code/UI/DragAndExchange/entry/src/main/resources/base/media/startIcon.png b/code/UI/DragAndExchange/entry/src/main/resources/base/media/startIcon.png new file mode 100644 index 0000000000000000000000000000000000000000..205ad8b5a8a42e8762fbe4899b8e5e31ce822b8b Binary files /dev/null and b/code/UI/DragAndExchange/entry/src/main/resources/base/media/startIcon.png differ diff --git a/code/UI/DragAndExchange/entry/src/main/resources/base/profile/backup_config.json b/code/UI/DragAndExchange/entry/src/main/resources/base/profile/backup_config.json new file mode 100644 index 0000000000000000000000000000000000000000..78f40ae7c494d71e2482278f359ec790ca73471a --- /dev/null +++ b/code/UI/DragAndExchange/entry/src/main/resources/base/profile/backup_config.json @@ -0,0 +1,3 @@ +{ + "allowToBackupRestore": true +} \ No newline at end of file diff --git a/code/UI/DragAndExchange/entry/src/main/resources/base/profile/main_pages.json b/code/UI/DragAndExchange/entry/src/main/resources/base/profile/main_pages.json new file mode 100644 index 0000000000000000000000000000000000000000..1898d94f58d6128ab712be2c68acc7c98e9ab9ce --- /dev/null +++ b/code/UI/DragAndExchange/entry/src/main/resources/base/profile/main_pages.json @@ -0,0 +1,5 @@ +{ + "src": [ + "pages/Index" + ] +} diff --git a/code/UI/DragAndExchange/entry/src/main/resources/dark/element/color.json b/code/UI/DragAndExchange/entry/src/main/resources/dark/element/color.json new file mode 100644 index 0000000000000000000000000000000000000000..79b11c2747aec33e710fd3a7b2b3c94dd9965499 --- /dev/null +++ b/code/UI/DragAndExchange/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/code/UI/DragAndExchange/entry/src/mock/mock-config.json5 b/code/UI/DragAndExchange/entry/src/mock/mock-config.json5 new file mode 100644 index 0000000000000000000000000000000000000000..7a73a41bfdf76d6f793007240d80983a52f15f97 --- /dev/null +++ b/code/UI/DragAndExchange/entry/src/mock/mock-config.json5 @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/code/UI/DragAndExchange/entry/src/ohosTest/ets/test/Ability.test.ets b/code/UI/DragAndExchange/entry/src/ohosTest/ets/test/Ability.test.ets new file mode 100644 index 0000000000000000000000000000000000000000..bc1553f7ff3a895d7e37631b98dea51acf98ae27 --- /dev/null +++ b/code/UI/DragAndExchange/entry/src/ohosTest/ets/test/Ability.test.ets @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2025 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 { describe, it, expect } from '@ohos/hypium'; +import { Driver, ON } from '@ohos.UiTest'; +import { logger } from '../utils/logger'; +import AbilityDelegatorRegistry from '@ohos.app.ability.abilityDelegatorRegistry'; + +const BUNDLE: string = 'DragAndExchange'; +const TAG: string = '[DragAndExchange_Test]'; +const DELAY_MS: number = 1000; // 指定1000ms的延迟 + +export default function abilityTest() { + describe('ActsAbilityTest', () => { + /** + * 打开应用 + */ + it(BUNDLE + '_StartAbility', 0, async (done: Function) => { + logger.info(TAG, BUNDLE + '_StartAbility start'); + let abilityDelegator = AbilityDelegatorRegistry.getAbilityDelegator(); + try { + await abilityDelegator.startAbility({ + bundleName: 'com.samples.dragandexchange', + abilityName: 'EntryAbility' + }); + } catch (exception) { + logger.error(TAG, BUNDLE + '_StartAbility error'); + expect().assertFail(); + } + logger.info(TAG, BUNDLE + '_StartAbility end'); + done(); + }) + + /** + * 拖拽Grid中子组件,到目标Grid子组件位置,进行两者位置互换。 + */ + it(BUNDLE + '拖拽Grid中子组件,到目标Grid子组件位置,进行两者位置互换。', 0, async (done: Function) => { + let driver = Driver.create(); + await driver.assertComponentExist(ON.id('App1')); + let App1 = await driver.findComponent(ON.id('App1')); + let point1 = await App1.getBoundsCenter(); + await driver.assertComponentExist(ON.id('App3')); + let App3 = await driver.findComponent(ON.id('App3')); + let point3 = await App3.getBoundsCenter(); + await App1.dragTo(App3); + await driver.delayMs(DELAY_MS); + let App12 = await driver.findComponent(ON.id('App1')); + let point12 = await App12.getBoundsCenter(); + expect(point1.x !== point12.x).assertTrue(); + expect(point12.x === point3.x).assertTrue(); + done(); + }) + + /** + * 拖拽List中子组件,到目标List子组件位置,进行两者位置互换。 + */ + it(BUNDLE + '拖拽List中子组件,到目标List子组件位置,进行两者位置互换。', 0, async (done: Function) => { + let driver = Driver.create(); + await driver.assertComponentExist(ON.id('Item1')); + let Item1 = await driver.findComponent(ON.id('Item1')); + let point1 = await Item1.getBoundsCenter(); + await driver.assertComponentExist(ON.id('Item3')); + let Item3 = await driver.findComponent(ON.id('Item3')); + let point3 = await Item3.getBoundsCenter(); + await Item1.dragTo(Item3); + await driver.delayMs(DELAY_MS); + let Item12 = await driver.findComponent(ON.id('Item1')); + let point12 = await Item12.getBoundsCenter(); + expect(point1.x !== point12.x).assertTrue(); + expect(point12.x === point3.x).assertTrue(); + done(); + }) + }) +} \ No newline at end of file diff --git a/code/UI/DragAndExchange/entry/src/ohosTest/ets/test/List.test.ets b/code/UI/DragAndExchange/entry/src/ohosTest/ets/test/List.test.ets new file mode 100644 index 0000000000000000000000000000000000000000..1eac52fcebe8958e19a7b8fed2e8f39c520a3e42 --- /dev/null +++ b/code/UI/DragAndExchange/entry/src/ohosTest/ets/test/List.test.ets @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2025 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 abilityTest from './Ability.test'; + +export default function testsuite() { + abilityTest(); +} \ No newline at end of file diff --git a/code/UI/DragAndExchange/entry/src/ohosTest/ets/utils/logger.ets b/code/UI/DragAndExchange/entry/src/ohosTest/ets/utils/logger.ets new file mode 100644 index 0000000000000000000000000000000000000000..3aba453bab30b2f120f10d0c0c91e7e666ac5dfd --- /dev/null +++ b/code/UI/DragAndExchange/entry/src/ohosTest/ets/utils/logger.ets @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2025 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 hilog from '@ohos.hilog'; + +/** + * 日志打印类 + */ +class Logger { + private domain: number; + private prefix: string; + private format: string = '%{public}s, %{public}s'; + + constructor(prefix: string) { + this.prefix = prefix; + this.domain = 0xFF00; + this.format.toUpperCase(); + } + + debug(...args: string[]) { + hilog.debug(this.domain, this.prefix, this.format, args); + } + + info(...args: string[]) { + hilog.info(this.domain, this.prefix, this.format, args); + } + + warn(...args: string[]) { + hilog.warn(this.domain, this.prefix, this.format, args); + } + + error(...args: string[]) { + hilog.error(this.domain, this.prefix, this.format, args); + } +} + +export let logger = new Logger('[Samples_dragandexchange]'); \ No newline at end of file diff --git a/code/UI/DragAndExchange/entry/src/ohosTest/module.json5 b/code/UI/DragAndExchange/entry/src/ohosTest/module.json5 new file mode 100644 index 0000000000000000000000000000000000000000..69026872775eebd0844900b225c411959ae5608b --- /dev/null +++ b/code/UI/DragAndExchange/entry/src/ohosTest/module.json5 @@ -0,0 +1,12 @@ +{ + "module": { + "name": "entry_test", + "type": "feature", + "deviceTypes": [ + "default", + "tablet" + ], + "deliveryWithInstall": true, + "installationFree": false + } +} diff --git a/code/UI/DragAndExchange/entry/src/test/List.test.ets b/code/UI/DragAndExchange/entry/src/test/List.test.ets new file mode 100644 index 0000000000000000000000000000000000000000..f1186b1f53c3a70930921c5dbd1417332bec56c9 --- /dev/null +++ b/code/UI/DragAndExchange/entry/src/test/List.test.ets @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2025 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 localUnitTest from './LocalUnit.test'; + +export default function testsuite() { + localUnitTest(); +} \ No newline at end of file diff --git a/code/UI/DragAndExchange/entry/src/test/LocalUnit.test.ets b/code/UI/DragAndExchange/entry/src/test/LocalUnit.test.ets new file mode 100644 index 0000000000000000000000000000000000000000..7fc57c77dbf76d8df08a2b802a55b948e3fcf968 --- /dev/null +++ b/code/UI/DragAndExchange/entry/src/test/LocalUnit.test.ets @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2025 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 { describe, beforeAll, beforeEach, afterEach, afterAll, it, expect } from '@ohos/hypium'; + +export default function localUnitTest() { + describe('localUnitTest', () => { + // 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. + 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/code/UI/DragAndExchange/hvigor/hvigor-config.json5 b/code/UI/DragAndExchange/hvigor/hvigor-config.json5 new file mode 100644 index 0000000000000000000000000000000000000000..d584c19c247db9a7caee4b606bb931aa9279c637 --- /dev/null +++ b/code/UI/DragAndExchange/hvigor/hvigor-config.json5 @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2025 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. + */ + +{ + "modelVersion": "5.0.1", + "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/code/UI/DragAndExchange/hvigorfile.ts b/code/UI/DragAndExchange/hvigorfile.ts new file mode 100644 index 0000000000000000000000000000000000000000..2a5e543f190732c159beb574dfc9fa37bc94e156 --- /dev/null +++ b/code/UI/DragAndExchange/hvigorfile.ts @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2025 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 { 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/code/UI/DragAndExchange/oh-package.json5 b/code/UI/DragAndExchange/oh-package.json5 new file mode 100644 index 0000000000000000000000000000000000000000..e41bae026aab3b50d0abb42fece08ba43b4a772b --- /dev/null +++ b/code/UI/DragAndExchange/oh-package.json5 @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2025 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. + */ + +{ + "modelVersion": "5.0.1", + "description": "Please describe the basic information.", + "dependencies": { + }, + "devDependencies": { + "@ohos/hypium": "1.0.19", + "@ohos/hamock": "1.0.0" + } +} diff --git a/code/UI/DragAndExchange/ohosTest.md b/code/UI/DragAndExchange/ohosTest.md new file mode 100644 index 0000000000000000000000000000000000000000..192034cbaafaea1d3212ed41fba9f2b93d906805 --- /dev/null +++ b/code/UI/DragAndExchange/ohosTest.md @@ -0,0 +1,8 @@ +## Grid和List内拖拽交换子组件位置案例测试用例 + +### 用例表 + +| 测试功能 | 预置条件 | 输入 | 预期输出 | 是否自动 | 测试结果 | +|:---------------------------------:|:---------------------:|:-----------------------:|:------:|:----:|:----:| +| 拖拽Grid中子组件,到目标Grid子组件位置,进行两者位置互换。 | 启动Grid和List内拖拽交换子组件位置 | 拖拽Grid中子组件,到目标Grid子组件位置 | 两者位置互换 | 否 | Pass | +| 拖拽List中子组件,到目标List子组件位置,进行两者位置互换。 | 启动Grid和List内拖拽交换子组件位置 | 拖拽List中子组件,到目标List子组件位置 | 两者位置互换 | 否 | Pass |