From 3db1d0a732f0929a0a97eb23e872fe051bde96d4 Mon Sep 17 00:00:00 2001 From: Sergey Malenkov Date: Tue, 4 Feb 2025 14:49:52 +0300 Subject: [PATCH 01/15] Add new harness module for TS, OHOS, and ArkTS Signed-off-by: Sergey Malenkov --- incremental/harness/.gitlab-ci.yml | 70 ++ incremental/harness/README.md | 1 + incremental/harness/arktsconfig.json | 17 + incremental/harness/js/golden.js | 41 + incremental/harness/package.json | 54 ++ incremental/harness/src/arkts/chai.ts | 791 ++++++++++++++++++++ incremental/harness/src/arkts/index.ts | 17 + incremental/harness/src/arkts/mocha.ts | 96 +++ incremental/harness/src/index.ts | 16 + incremental/harness/src/ohos/chai.ts | 782 +++++++++++++++++++ incremental/harness/src/ohos/index.ts | 29 + incremental/harness/src/ohos/mocha.ts | 63 ++ incremental/harness/src/typescript/index.ts | 21 + incremental/harness/tsconfig-ohos.json | 18 + incremental/harness/tsconfig.json | 18 + 15 files changed, 2034 insertions(+) create mode 100644 incremental/harness/.gitlab-ci.yml create mode 100644 incremental/harness/README.md create mode 100644 incremental/harness/arktsconfig.json create mode 100644 incremental/harness/js/golden.js create mode 100644 incremental/harness/package.json create mode 100644 incremental/harness/src/arkts/chai.ts create mode 100644 incremental/harness/src/arkts/index.ts create mode 100644 incremental/harness/src/arkts/mocha.ts create mode 100644 incremental/harness/src/index.ts create mode 100644 incremental/harness/src/ohos/chai.ts create mode 100644 incremental/harness/src/ohos/index.ts create mode 100644 incremental/harness/src/ohos/mocha.ts create mode 100644 incremental/harness/src/typescript/index.ts create mode 100644 incremental/harness/tsconfig-ohos.json create mode 100644 incremental/harness/tsconfig.json diff --git a/incremental/harness/.gitlab-ci.yml b/incremental/harness/.gitlab-ci.yml new file mode 100644 index 000000000..8c4a4e2cb --- /dev/null +++ b/incremental/harness/.gitlab-ci.yml @@ -0,0 +1,70 @@ +# Copyright (c) 2022-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. + +build harness: + stage: build + interruptible: true + extends: .linux-vm-shell-task + before_script: + - !reference [.setup, script] + - cd incremental/harness + script: + - npm run compile:all + needs: + - install node modules (arkoala) + - install node modules (arkoala-arkts) + - install node modules (incremental) + - install node modules (interop) + - build compat + - build common + artifacts: + expire_in: 2 days + paths: + - incremental/harness/build + +build harness: + stage: build + interruptible: true + extends: .linux-vm-shell-task + needs: + - install node modules (arkoala) + - install node modules (arkoala-arkts) + - install node modules (incremental) + - install node modules (interop) + before_script: + - !reference [.setup, script] + - cd incremental/harness + script: + - npm run compile:all + artifacts: + expire_in: 2 days + paths: + - incremental/harness/build + +pack harness: + extends: + - .npm-pack + - .linux-vm-shell-task + variables: + PACKAGE_DIR: incremental/harness + needs: + - build harness + +publish harness: + extends: + - .npm-publish + - .linux-vm-shell-task + variables: + PACKAGE_DIR: incremental/harness + dependencies: + - build harness \ No newline at end of file diff --git a/incremental/harness/README.md b/incremental/harness/README.md new file mode 100644 index 000000000..97118e43c --- /dev/null +++ b/incremental/harness/README.md @@ -0,0 +1 @@ +This is a harness library compatible with OHOS and ArkTS. diff --git a/incremental/harness/arktsconfig.json b/incremental/harness/arktsconfig.json new file mode 100644 index 000000000..5a3a24910 --- /dev/null +++ b/incremental/harness/arktsconfig.json @@ -0,0 +1,17 @@ +{ + "compilerOptions": { + "package": "@koalaui/harness", + "outDir": "build/abc", + "rootDir": "./src/arkts", + "baseUrl": "./src/arkts", + "paths": { + "@koalaui/compat": ["../../../compat/src/arkts"], + "@koalaui/common": ["../../../common/src"] + } + }, + "include": ["./src/arkts/**/*.ts"], + "references": [ + { "path": "../common" }, + { "path": "../compat" } + ] +} \ No newline at end of file diff --git a/incremental/harness/js/golden.js b/incremental/harness/js/golden.js new file mode 100644 index 000000000..52c48305e --- /dev/null +++ b/incremental/harness/js/golden.js @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2022-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. + */ + +const path = require("path") +const process = require("process") + +function goldenSetup(resDir, testDir) { + const root = path.resolve(".", resDir) + const testRoot = path.resolve(testDir) + if (root != testRoot) { + process.env.KOALAUI_RESOURCE_ROOT = root + } + process.env.KOALAUI_TEST_ROOT = path.relative(root, testRoot) + const argGenGolden = "--gen-golden" + for (let str of process.argv) { + if (str.startsWith(argGenGolden)) { + process.env.KOALAUI_TEST_GOLDEN_GEN_DIR = path.relative(root, path.resolve(testRoot, "test", "resources", "golden")) + if (str.length > argGenGolden.length + 1) { + const gdir = str.substring(argGenGolden.length + 1); + if (gdir.length > 0 && gdir != "true") { + process.env.KOALAUI_TEST_GOLDEN_GEN_DIR = path.relative(root, path.resolve(gdir)) + } + } + break + } + } +} + +exports.goldenSetup = goldenSetup diff --git a/incremental/harness/package.json b/incremental/harness/package.json new file mode 100644 index 000000000..b2950d4c9 --- /dev/null +++ b/incremental/harness/package.json @@ -0,0 +1,54 @@ +{ + "name": "@koalaui/harness", + "version": "1.4.4+devel", + "description": "A harness library compatible with OHOS and ArkTS", + "main": "build/src/index.js", + "types": "build/src/index.d.ts", + "files": [ + "js/golden.js", + "build/src/**/*.js", + "build/src/**/*.d.ts" + ], + "imports": { + "#harness": { + "ark": "./build/src/ohos/index.js", + "ios": "./build/src/typescript/index.js", + "browser": "./build/src/typescript/index.js", + "node": "./build/src/typescript/index.js", + "default": "./build/src/typescript/index.js" + } + }, + "exports": { + "./golden": "./js/golden.js", + ".": "./build/src/index.js" + }, + "scripts": { + "clean": "rimraf build dist", + "compile": "ets-tsc -b .", + "compile:ohos": "ets-tsc -b ./tsconfig-ohos.json", + "compile:all": "npm run compile && npm run compile:ohos", + "compile:arkts": "fast-arktsc --input-files ./arktsconfig.json --output-dir ./build --link-name harness --compiler ../tools/panda/arkts/arktsc", + "compile:arkts:capi": "fast-arktsc --input-files ./arktsconfig.json --output-dir ./build --link-name harness --compiler ../tools/panda/arkts/arktsc-capi --file-option", + "build:harness": "npm run build:harness:inc", + "build:harness:inc": "npm run compile:arkts && ninja ${NINJA_OPTIONS} -f build/build.ninja", + "build:harness:inc:capi": "npm run compile:arkts:capi && PANDA_SDK_PATH=../tools/panda/node_modules/@panda/sdk ninja ${NINJA_OPTIONS} -f build/build.ninja" + }, + "keywords": [], + "dependencies": { + "@koalaui/common": "1.4.4+devel", + "@koalaui/compat": "1.4.4+devel" + }, + "devDependencies": { + "@ohos/hypium": "1.0.6", + "@types/chai": "^4.3.1", + "@types/mocha": "^9.1.0", + "@typescript-eslint/eslint-plugin": "^5.20.0", + "@typescript-eslint/parser": "^5.20.0", + "chai": "^4.3.6", + "eslint": "^8.13.0", + "eslint-plugin-unused-imports": "^2.0.0", + "mocha": "^9.2.2", + "rimraf": "^3.0.2", + "source-map-support": "^0.5.21" + } +} \ No newline at end of file diff --git a/incremental/harness/src/arkts/chai.ts b/incremental/harness/src/arkts/chai.ts new file mode 100644 index 000000000..a8722d72d --- /dev/null +++ b/incremental/harness/src/arkts/chai.ts @@ -0,0 +1,791 @@ +/* + * Copyright (c) 2022-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 { int32 } from "@koalaui/common" + +export class Assert { + /** + * Throws a failure. + * @param message - message to display on error + */ + static fail(message?: string): never { + throw new AssertionError(message ?? "Assert.fail") + } + + /** + * Asserts that object is truthy. + * + * T Type of object. + * @param object Object to test. + * @param message Message to display on error. + */ + static isOk(value: T, message?: string): void { + Assert.fail("Assert.isOk unsupported") + } + + /** + * Asserts that object is truthy. + * + * T Type of object. + * @param object Object to test. + * @param message Message to display on error. + */ + static ok(value: T, message?: string): void { + Assert.fail("Assert.ok unsupported") + } + + /** + * Asserts that object is falsy. + * + * T Type of object. + * @param object Object to test. + * @param message Message to display on error. + */ + static isNotOk(value: T, message?: string): void { + Assert.fail("Assert.isNotOk unsupported") + } + + /** + * Asserts that object is falsy. + * + * T Type of object. + * @param object Object to test. + * @param message Message to display on error. + */ + static notOk(value: T, message?: string): void { + Assert.fail("Assert.notOk unsupported") + } + + /** + * Asserts non-strict equality (==) of actual and expected. + * @param actual - actual value + * @param expected - potential expected value + * @param message - message to display on error + */ + static equal(actual: T, expected: T, message?: string): void { + if (actual == expected) return + Assert.fail(message ?? `actual '${actual}' is not equal to expected '${expected}'`) + } + + /** + * Asserts non-strict inequality (!=) of actual and expected. + * @param actual - actual value + * @param expected - potential expected value + * @param message - message to display on error + */ + static notEqual(actual: T, expected: T, message?: string): void { + if (actual != expected) return + Assert.fail(message ?? `actual '${actual}' is equal to expected '${expected}'`) + } + + /** + * Asserts strict equality (===) of actual and expected. + * @param actual - actual value + * @param expected - potential expected value + * @param message - message to display on error + */ + static strictEqual(actual: T, expected: T, message?: string): void { + if (actual === expected) return + Assert.fail(message ?? `actual '${actual}' is not strictly equal to expected '${expected}'`) + } + + /** + * Asserts strict inequality (!==) of actual and expected. + * @param actual - actual value + * @param expected - potential expected value + * @param message - message to display on error + */ + static notStrictEqual(actual: T, expected: T, message?: string): void { + if (actual !== expected) return + Assert.fail(message ?? `actual '${actual}' is strictly equal to expected '${expected}'`) + } + + /** + * Asserts that actual is deeply equal to expected. + * @param actual - actual value array + * @param expected - potential expected value array + * @param message - message to display on error + */ + static deepEqual(actual: Array, expected: Array, message?: string): void { + if (equalArrayContent(actual, expected)) return + Assert.fail(message ?? `actual '${actual}' is not deeply equal to expected '${expected}'`) + } + + /** + * Asserts that actual is not deeply equal to expected. + * @param actual - actual value array + * @param expected - potential expected value array + * @param message - message to display on error + */ + static notDeepEqual(actual: Array, expected: Array, message?: string): void { + if (!equalArrayContent(actual, expected)) return + Assert.fail(message ?? `actual '${actual}' is deeply equal to expected '${expected}'`) + } + + /** + * Asserts valueToCheck is strictly greater than (>) valueToBeAbove. + * + * @param valueToCheck Actual value. + * @param valueToBeAbove Minimum Potential expected value. + * @param message Message to display on error. + */ + static isAbove(valueToCheck: number, valueToBeAbove: number, message?: string): void { + Assert.fail("Assert.isAbove unsupported") + } + + /** + * Asserts valueToCheck is greater than or equal to (>=) valueToBeAtLeast. + * + * @param valueToCheck Actual value. + * @param valueToBeAtLeast Minimum Potential expected value. + * @param message Message to display on error. + */ + static isAtLeast(valueToCheck: number, valueToBeAtLeast: number, message?: string): void { + Assert.fail("Assert.isAtLeast unsupported") + } + + /** + * Asserts valueToCheck is strictly less than (<) valueToBeBelow. + * + * @param valueToCheck Actual value. + * @param valueToBeBelow Minimum Potential expected value. + * @param message Message to display on error. + */ + static isBelow(valueToCheck: number, valueToBeBelow: number, message?: string): void { + Assert.fail("Assert.isBelow unsupported") + } + + /** + * Asserts valueToCheck is less than or equal to (<=) valueToBeAtMost. + * + * @param valueToCheck Actual value. + * @param valueToBeAtMost Minimum Potential expected value. + * @param message Message to display on error. + */ + static isAtMost(valueToCheck: number, valueToBeAtMost: number, message?: string): void { + Assert.fail("Assert.isAtMost unsupported") + } + + /** + * Asserts that value is true. + * @param value - actual value + * @param message - message to display on error + */ + static isTrue(value?: boolean, message?: string): void { + if (value == true) return + Assert.fail(message ?? `actual '${value}' is not true unexpectedly`) + } + + /** + * Asserts that value is not true. + * @param value - actual value + * @param message - message to display on error + */ + static isNotTrue(value?: boolean, message?: string): void { + if (value != true) return + Assert.fail(message ?? `actual '${value}' is true unexpectedly`) + } + + /** + * Asserts that value is false. + * @param value - actual value + * @param message - message to display on error + */ + static isFalse(value?: boolean, message?: string): void { + if (value == false) return + Assert.fail(message ?? `actual '${value}' is not false unexpectedly`) + } + + /** + * Asserts that value is not false. + * @param value - actual value + * @param message - message to display on error + */ + static isNotFalse(value?: boolean, message?: string): void { + if (value != false) return + Assert.fail(message ?? `actual '${value}' is false unexpectedly`) + } + + /** + * Asserts that value is null. + * @param value - actual value + * @param message - message to display on error + */ + static isNull(value: T, message?: string): void { + if (value == null) return // replace with '===' when panda-issue-19588 is fixed + Assert.fail(message ?? `actual '${value}' is not null unexpectedly`) + } + + /** + * Asserts that value is not null. + * @param value - actual value + * @param message - message to display on error + */ + static isNotNull(value: T, message?: string): void { + if (value != null) return // replace with '!==' when panda-issue-19588 is fixed + Assert.fail(message ?? `actual '${value}' is null unexpectedly`) + } + + /** + * Asserts that value is NaN. + * + * T Type of value. + * @param value Actual value. + * @param message Message to display on error. + */ + static isNaN(value: T, message?: string): void { + Assert.fail("Assert.isNaN unsupported") + } + + /** + * Asserts that value is not NaN. + * + * T Type of value. + * @param value Actual value. + * @param message Message to display on error. + */ + static isNotNaN(value: T, message?: string): void { + Assert.fail("Assert.isNotNaN unsupported") + } + + /** + * Asserts that the target is neither null nor undefined. + * @param value - actual value + * @param message - message to display on error + */ + static exists(value: T, message?: string): void { + if (value == undefined || value == null) return // replace with '===' when panda-issue-19588 is fixed + Assert.fail(message ?? `actual '${value}' does not exist unexpectedly`) + } + + /** + * Asserts that the target is either null or undefined. + * @param value - actual value + * @param message - message to display on error + */ + static notExists(value: T, message?: string): void { + if (value != undefined && value != null) return // replace with '!==' when panda-issue-19588 is fixed + Assert.fail(message ?? `actual '${value}' exists unexpectedly`) + } + + /** + * Asserts that value is undefined. + * @param value - actual value + * @param message - message to display on error + */ + static isUndefined(value: T, message?: string): void { + if (value == undefined) return // replace with '===' when panda-issue-19588 is fixed + Assert.fail(message ?? `actual '${value}' is defined unexpectedly`) + } + + /** + * Asserts that value is not undefined. + * @param value - actual value + * @param message - message to display on error + */ + static isDefined(value: T, message?: string): void { + if (value != undefined) return // replace with '!==' when panda-issue-19588 is fixed + Assert.fail(message ?? `actual '${value}' is undefined unexpectedly`) + } + + /** + * Asserts that value is a function. + * + * T Type of value. + * @param value Actual value. + * @param message Message to display on error. + */ + static isFunction(value: T, message?: string): void { + Assert.fail("Assert.isFunction unsupported") + } + + /** + * Asserts that value is not a function. + * + * T Type of value. + * @param value Actual value. + * @param message Message to display on error. + */ + static isNotFunction(value: T, message?: string): void { + Assert.fail("Assert.isNotFunction unsupported") + } + + /** + * Asserts that value is an object of type 'Object' + * (as revealed by Object.prototype.toString). + * + * T Type of value. + * @param value Actual value. + * @param message Message to display on error. + * @remarks The assertion does not match subclassed objects. + */ + static isObject(value: T, message?: string): void { + Assert.fail("Assert.isObject unsupported") + } + + /** + * Asserts that value is not an object of type 'Object' + * (as revealed by Object.prototype.toString). + * + * T Type of value. + * @param value Actual value. + * @param message Message to display on error. + */ + static isNotObject(value: T, message?: string): void { + Assert.fail("Assert.isNotObject unsupported") + } + + /** + * Asserts that value is an array. + * + * T Type of value. + * @param value Actual value. + * @param message Message to display on error. + */ + static isArray(value: T, message?: string): void { + Assert.fail("Assert.isArray unsupported") + } + + /** + * Asserts that value is not an array. + * + * T Type of value. + * @param value Actual value. + * @param message Message to display on error. + */ + static isNotArray(value: T, message?: string): void { + Assert.fail("Assert.isNotArray unsupported") + } + + /** + * Asserts that value is a string. + * + * T Type of value. + * @param value Actual value. + * @param message Message to display on error. + */ + static isString(value: T, message?: string): void { + Assert.fail("Assert.isString unsupported") + } + + /** + * Asserts that value is not a string. + * + * T Type of value. + * @param value Actual value. + * @param message Message to display on error. + */ + static isNotString(value: T, message?: string): void { + Assert.fail("Assert.isNotString unsupported") + } + + /** + * Asserts that value is a number. + * + * T Type of value. + * @param value Actual value. + * @param message Message to display on error. + */ + static isNumber(value: T, message?: string): void { + Assert.fail("Assert.isNumber unsupported") + } + + /** + * Asserts that value is not a number. + * + * T Type of value. + * @param value Actual value. + * @param message Message to display on error. + */ + static isNotNumber(value: T, message?: string): void { + Assert.fail("Assert.isNotNumber unsupported") + } + + /** + * Asserts that value is a finite number. + * Unlike `.isNumber`, this will fail for `NaN` and `Infinity`. + * + * T Type of value + * @param value Actual value + * @param message Message to display on error. + */ + static isFinite(value: T, message?: string): void { + Assert.fail("Assert.isFinite unsupported") + } + + /** + * Asserts that value is a boolean. + * + * T Type of value. + * @param value Actual value. + * @param message Message to display on error. + */ + static isBoolean(value: T, message?: string): void { + Assert.fail("Assert.isBoolean unsupported") + } + + /** + * Asserts that value is not a boolean. + * + * T Type of value. + * @param value Actual value. + * @param message Message to display on error. + */ + static isNotBoolean(value: T, message?: string): void { + Assert.fail("Assert.isNotBoolean unsupported") + } + + /** + * Asserts that value's type is name, as determined by Object.prototype.toString. + * + * T Type of value. + * @param value Actual value. + * @param name Potential expected type name of value. + * @param message Message to display on error. + */ + static typeOf(value: T, name: string, message?: string): void { + Assert.fail("Assert.typeOf unsupported") + } + + /** + * Asserts that value's type is not name, as determined by Object.prototype.toString. + * + * T Type of value. + * @param value Actual value. + * @param name Potential expected type name of value. + * @param message Message to display on error. + */ + static notTypeOf(value: T, name: string, message?: string): void { + Assert.fail("Assert.notTypeOf unsupported") + } + + /** + * Asserts that value is an instance of constructor. + * + * T Type of value. + * @param value Actual value. + * @param construct Potential expected contructor of value. + * @param message Message to display on error. + * / + static instanceOf(value: T, construct: Function, message?: string): void*/ + + /** + * Asserts that value is not an instance of constructor. + * + * T Type of value. + * @param value Actual value. + * @param constructor Potential expected contructor of value. + * @param message Message to display on error. + * / + static notInstanceOf(value: T, type: Function, message?: string): void*/ + + /** + * Asserts that haystack includes needle. + * + * @param haystack Container string. + * @param needle Potential substring of haystack. + * @param message Message to display on error. + * / + static include(haystack: string, needle: string, message?: string): void*/ + + /** + * Asserts that haystack includes needle. + * + * T Type of values in haystack. + * @param haystack Container array, set or map. + * @param needle Potential value contained in haystack. + * @param message Message to display on error. + * / + static include( + haystack: ReadonlyArray | ReadonlySet | ReadonlyMap, + needle: T, + message?: string, + ): void;*/ + + /** + * Asserts that haystack includes needle. + * + * T Type of values in haystack. + * @param haystack WeakSet container. + * @param needle Potential value contained in haystack. + * @param message Message to display on error. + * / + static include(haystack: WeakSet, needle: T, message?: string): void;*/ + + /** + * Asserts that haystack includes needle. + * + * T Type of haystack. + * @param haystack Object. + * @param needle Potential subset of the haystack's properties. + * @param message Message to display on error. + * / + static include(haystack: T, needle: Partial, message?: string): void;*/ + + /** + * Asserts that haystack does not includes needle. + * + * @param haystack Container string. + * @param needle Potential substring of haystack. + * @param message Message to display on error. + * / + static notInclude(haystack: string, needle: string, message?: string): void;*/ + + /** + * Asserts that haystack does not includes needle. + * + * T Type of values in haystack. + * @param haystack Container array, set or map. + * @param needle Potential value contained in haystack. + * @param message Message to display on error. + * / + static notInclude( + haystack: ReadonlyArray | ReadonlySet | ReadonlyMap, + needle: T, + message?: string, + ): void;*/ + + /** + * Asserts that haystack does not includes needle. + * + * T Type of values in haystack. + * @param haystack WeakSet container. + * @param needle Potential value contained in haystack. + * @param message Message to display on error. + * / + static notInclude(haystack: WeakSet, needle: T, message?: string): void;*/ + + /** + * Asserts that haystack does not includes needle. + * + * T Type of haystack. + * @param haystack Object. + * @param needle Potential subset of the haystack's properties. + * @param message Message to display on error. + * / + static notInclude(haystack: T, needle: Partial, message?: string): void;*/ + + /** + * Asserts that value matches the regular expression regexp. + * + * @param value Actual value. + * @param regexp Potential match of value. + * @param message Message to display on error. + */ + static match(value: string, regexp: RegExp, message?: string): void { + Assert.fail("Assert.match unsupported") + } + + /** + * Asserts that value does not match the regular expression regexp. + * + * @param value Actual value. + * @param regexp Potential match of value. + * @param message Message to display on error. + */ + static notMatch(expected: string, regexp: RegExp, message?: string): void { + Assert.fail("Assert.notMatch unsupported") + } + + /** + * Asserts that fn will throw an error. + * + * @param fn Function that may throw. + * @param errMsgMatcher Expected error message matcher. + * @param ignored Ignored parameter. + * @param message Message to display on error. + * / + static throw(fn: () => void, errMsgMatcher?: RegExp | string, ignored?: any, message?: string): void;*/ + + /** + * Asserts that the given function will throw an error. + * @param func - a function that may throw an error + * / + static throw(func: () => void): void { + Assert.throws(func) + } + + /** + * Asserts that the given function will throw an error. + * @param func - a function that may throw an error + */ + static throws(func: () => void): void { + let expected: Error | undefined = undefined + try { + func() + } catch (error) { + expected = error as Error + } + if (expected) return + Assert.fail("expected error is not thrown") + } + + /** + * Asserts that fn will throw an error. + * + * @param fn Function that may throw. + * @param errorLike Expected error constructor or error instance. + * @param errMsgMatcher Expected error message matcher. + * @param message Message to display on error. + * / + static throws( + fn: () => void, + errorLike?: ErrorConstructor | Error | null, + errMsgMatcher?: RegExp | string | null, + message?: string, + ): void;*/ + + /** + * Asserts that fn will throw an error. + * + * @param fn Function that may throw. + * @param errMsgMatcher Expected error message matcher. + * @param ignored Ignored parameter. + * @param message Message to display on error. + * / + static Throw(fn: () => void, errMsgMatcher?: RegExp | string, ignored?: any, message?: string): void;*/ + + /** + * Asserts that fn will throw an error. + * + * @param fn Function that may throw. + * @param errorLike Expected error constructor or error instance. + * @param errMsgMatcher Expected error message matcher. + * @param message Message to display on error. + * / + static Throw( + fn: () => void, + errorLike?: ErrorConstructor | Error | null, + errMsgMatcher?: RegExp | string | null, + message?: string, + ): void;*/ + + /** + * Asserts that fn will not throw an error. + * + * @param fn Function that may throw. + * @param errMsgMatcher Expected error message matcher. + * @param ignored Ignored parameter. + * @param message Message to display on error. + * / + static doesNotThrow(fn: () => void, errMsgMatcher?: RegExp | string, ignored?: any, message?: string): void;*/ + + /** + * Asserts that fn will not throw an error. + * + * @param fn Function that may throw. + * @param errorLike Expected error constructor or error instance. + * @param errMsgMatcher Expected error message matcher. + * @param message Message to display on error. + * / + static doesNotThrow( + fn: () => void, + errorLike?: ErrorConstructor | Error | null, + errMsgMatcher?: RegExp | string | null, + message?: string, + ): void;*/ + + /** + * Compares two values using operator. + * + * @param val1 Left value during comparison. + * @param operator Comparison operator. + * @param val2 Right value during comparison. + * @param message Message to display on error. + * / + static operator(val1: OperatorComparable, operator: Operator, val2: OperatorComparable, message?: string): void;*/ + + /** + * Asserts that the target is equal to expected, to within a +/- delta range. + * + * @param actual Actual value + * @param expected Potential expected value. + * @param delta Maximum differenced between values. + * @param message Message to display on error. + * / + static closeTo(actual: number, expected: number, delta: number, message?: string): void;*/ + + /** + * Asserts that the target is equal to expected, to within a +/- delta range. + * + * @param actual Actual value + * @param expected Potential expected value. + * @param delta Maximum differenced between values. + * @param message Message to display on error. + * / + static approximately(act: number, exp: number, delta: number, message?: string): void;*/ + + /** + * Asserts that non-object, non-array value inList appears in the flat array list. + * + * T Type of list values. + * @param inList Value expected to be in the list. + * @param list List of values. + * @param message Message to display on error. + */ + static oneOf(inList: T, list: T[], message?: string): void { + Assert.fail("Assert.oneOf unsupported") + } + + /** + * Asserts that the target does not contain any values. For arrays and + * strings, it checks the length property. For Map and Set instances, it + * checks the size property. For non-function objects, it gets the count + * of own enumerable string keys. + * + * T Type of object + * @param object Actual value. + * @param message Message to display on error. + */ + static isEmpty(object: T, message?: string): void { + const size = getContainerSize(object) + if (size != 0) Assert.fail(message ?? (size < 0 ? "unsupported container" : "container is not empty unexpectedly")) + } + + /** + * Asserts that the target contains values. For arrays and strings, it checks + * the length property. For Map and Set instances, it checks the size property. + * For non-function objects, it gets the count of own enumerable string keys. + * + * T Type of object. + * @param object Object to test. + * @param message Message to display on error. + */ + static isNotEmpty(object: T, message?: string): void { + const size = getContainerSize(object) + if (size <= 0) Assert.fail(message ?? (size < 0 ? "unsupported container" : "container is empty unexpectedly")) + } +} + +function equalArrayContent(actual: Array, expected: Array): boolean { + const length = actual.length + if (expected.length != length) return false + for (let i = 0; i < length; i++) { + if (expected[i] != actual[i]) return false + } + return true +} + +/** + * @param object - a container to check + * @returns a container size, or -1 if the given container is not supported + */ +function getContainerSize(container: T): int32 { + if (container instanceof Map) return container.size as int32 + if (container instanceof Set) return container.size as int32 + if (container instanceof Array) return container.length as int32 + if (container instanceof String) return container.length as int32 + return -1 +} diff --git a/incremental/harness/src/arkts/index.ts b/incremental/harness/src/arkts/index.ts new file mode 100644 index 000000000..546f0878a --- /dev/null +++ b/incremental/harness/src/arkts/index.ts @@ -0,0 +1,17 @@ +/* + * Copyright (c) 2022-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 { Assert } from "./chai" +export { startTests, suite, suiteSetup, test } from "./mocha" diff --git a/incremental/harness/src/arkts/mocha.ts b/incremental/harness/src/arkts/mocha.ts new file mode 100644 index 000000000..a7e575ed9 --- /dev/null +++ b/incremental/harness/src/arkts/mocha.ts @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2022-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 { int32 } from "@koalaui/common" + +const stack = new Array() + +function getCurrentSuite(): Suite | undefined { + const index = stack.length - 1 + if (index < 0) return undefined + const test = stack[index] + if (test instanceof Suite) return test as Suite + throw new Error("test is already running") +} + +class Test { + public error: Error | undefined = undefined + readonly name: string + readonly content: () => void + constructor(name: string, content?: () => void) { + this.name = name + this.content = () => { + const suite = getCurrentSuite() + try { + stack.push(this) + content?.() + } catch (error) { + this.error = error as Error + console.log(this.error!.toString()) + } finally { + stack.pop() + console.log(" ".repeat(stack.length) + this.toString()) + if (suite) suite.tests.push(this) + else if (!this.passed) throw new Error("TEST FAILED") + } + } + } + get passed(): boolean { + return this.error === undefined + } + toString(): string { + let result = this.passed ? "passed" : "failed" + return `${result} test: "${this.name}"` + } +} + +class Suite extends Test { + readonly tests = new Array() + constructor(name: string, content?: () => void) { + super(name, content) + } + private get failures(): int32 { + let failed = 0 + for (let i = 0; i < this.tests.length; i++) { + const test = this.tests[i] + if (!test.passed) failed++ + } + return failed + } + override get passed(): boolean { + return super.passed && this.failures == 0 + } + override toString(): string { + const failures = this.failures + const result = failures > 0 ? `failed tests '${failures}' in` : this.error ? "failed" : "passed" + return `${result} suite: "${this.name}"` + } +} + +export function test(name: string, content?: () => void) { + new Test(name, content).content() +} + +export function suite(name: string, content?: () => void) { + new Suite(name, content).content() +} + +export function suiteSetup(name: string, content?: () => void) { + throw new Error("unsupported suiteSetup: " + name) +} + +export function startTests(generateGolden: boolean = false) { + throw new Error("unsupported startTests: " + generateGolden) +} diff --git a/incremental/harness/src/index.ts b/incremental/harness/src/index.ts new file mode 100644 index 000000000..992ddac32 --- /dev/null +++ b/incremental/harness/src/index.ts @@ -0,0 +1,16 @@ +/* + * Copyright (c) 2022-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 { Assert, startTests, suite, suiteSetup, test } from "#harness" diff --git a/incremental/harness/src/ohos/chai.ts b/incremental/harness/src/ohos/chai.ts new file mode 100644 index 000000000..734e55342 --- /dev/null +++ b/incremental/harness/src/ohos/chai.ts @@ -0,0 +1,782 @@ +/* + * Copyright (c) 2022-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 { expect } from "@ohos/hypium" + +export class Assert { + /** + * Throws a failure. + * @param message - message to display on error + */ + static fail(message?: string): never { + expect().assertFail() + throw new Error("OHOS failed: " + message) + } + + /** + * Asserts that object is truthy. + * + * T Type of object. + * @param object Object to test. + * @param message Message to display on error. + */ + static isOk(value: T, message?: string): void { + Assert.fail("Assert.isOk unsupported") + } + + /** + * Asserts that object is truthy. + * + * T Type of object. + * @param object Object to test. + * @param message Message to display on error. + */ + static ok(value: T, message?: string): void { + Assert.fail("Assert.ok unsupported") + } + + /** + * Asserts that object is falsy. + * + * T Type of object. + * @param object Object to test. + * @param message Message to display on error. + */ + static isNotOk(value: T, message?: string): void { + Assert.fail("Assert.isNotOk unsupported") + } + + /** + * Asserts that object is falsy. + * + * T Type of object. + * @param object Object to test. + * @param message Message to display on error. + */ + static notOk(value: T, message?: string): void { + Assert.fail("Assert.notOk unsupported") + } + + /** + * Asserts non-strict equality (==) of actual and expected. + * @param actual - actual value + * @param expected - potential expected value + * @param message - message to display on error + */ + static equal(actual: T, expected: T, message?: string): void { + expect(actual).assertEqual(expected) + } + + /** + * Asserts non-strict inequality (!=) of actual and expected. + * @param actual - actual value + * @param expected - potential expected value + * @param message - message to display on error + */ + static notEqual(actual: T, expected: T, message?: string): void { + // todo: not accurate impl, because compared values are not printed + expect(actual != expected).assertTrue() + } + + /** + * Asserts strict equality (===) of actual and expected. + * @param actual - actual value + * @param expected - potential expected value + * @param message - message to display on error + */ + static strictEqual(actual: T, expected: T, message?: string): void { + // todo: not accurate impl, because compared values are not printed + expect(actual === expected).assertTrue() + } + + /** + * Asserts strict inequality (!==) of actual and expected. + * @param actual - actual value + * @param expected - potential expected value + * @param message - message to display on error + */ + static notStrictEqual(actual: T, expected: T, message?: string): void { + // todo: not accurate impl, because compared values are not printed + expect(actual !== expected).assertTrue() + } + + /** + * Asserts that actual is deeply equal to expected. + * @param actual - actual value array + * @param expected - potential expected value array + * @param message - message to display on error + */ + static deepEqual(actual: Array, expected: Array, message?: string): void { + // todo: implement + expect(actual).assertEqual(actual/*expected*/) + } + + /** + * Asserts that actual is not deeply equal to expected. + * @param actual - actual value array + * @param expected - potential expected value array + * @param message - message to display on error + */ + static notDeepEqual(actual: Array, expected: Array, message?: string): void { + // todo: implement + expect(actual).assertEqual(actual/*expected*/) + } + + /** + * Asserts valueToCheck is strictly greater than (>) valueToBeAbove. + * + * @param valueToCheck Actual value. + * @param valueToBeAbove Minimum Potential expected value. + * @param message Message to display on error. + */ + static isAbove(valueToCheck: number, valueToBeAbove: number, message?: string): void { + expect(valueToCheck).assertLarger(valueToBeAbove) + } + + /** + * Asserts valueToCheck is greater than or equal to (>=) valueToBeAtLeast. + * + * @param valueToCheck Actual value. + * @param valueToBeAtLeast Minimum Potential expected value. + * @param message Message to display on error. + */ + static isAtLeast(valueToCheck: number, valueToBeAtLeast: number, message?: string): void { + if (valueToCheck == valueToBeAtLeast) + expect(valueToCheck).assertEqual(valueToBeAtLeast) + else + expect(valueToCheck).assertLarger(valueToBeAtLeast) + } + + /** + * Asserts valueToCheck is strictly less than (<) valueToBeBelow. + * + * @param valueToCheck Actual value. + * @param valueToBeBelow Minimum Potential expected value. + * @param message Message to display on error. + */ + static isBelow(valueToCheck: number, valueToBeBelow: number, message?: string): void { + expect(valueToCheck).assertLess(valueToBeBelow) + } + + /** + * Asserts valueToCheck is less than or equal to (<=) valueToBeAtMost. + * + * @param valueToCheck Actual value. + * @param valueToBeAtMost Minimum Potential expected value. + * @param message Message to display on error. + */ + static isAtMost(valueToCheck: number, valueToBeAtMost: number, message?: string): void { + Assert.fail("Assert.isAtMost unsupported") + } + + /** + * Asserts that value is true. + * @param value - actual value + * @param message - message to display on error + */ + static isTrue(value?: boolean, message?: string): void { + expect(value).assertTrue() + } + + /** + * Asserts that value is not true. + * @param value - actual value + * @param message - message to display on error + */ + static isNotTrue(value?: boolean, message?: string): void { + if (value != true) return + Assert.fail(message ?? `actual '${value}' is true unexpectedly`) + } + + /** + * Asserts that value is false. + * @param value - actual value + * @param message - message to display on error + */ + static isFalse(value?: boolean, message?: string): void { + expect(value).assertFalse() + } + + /** + * Asserts that value is not false. + * @param value - actual value + * @param message - message to display on error + */ + static isNotFalse(value?: boolean, message?: string): void { + if (value != false) return + Assert.fail(message ?? `actual '${value}' is false unexpectedly`) + } + + /** + * Asserts that value is null. + * @param value - actual value + * @param message - message to display on error + */ + static isNull(value: T | null, message?: string): void { + expect(value).assertNull() + } + + /** + * Asserts that value is not null. + * @param value - actual value + * @param message - message to display on error + */ + static isNotNull(value: T | null, message?: string): void { + expect(value ? null : value).assertNull() + } + + /** + * Asserts that value is NaN. + * + * T Type of value. + * @param value Actual value. + * @param message Message to display on error. + */ + static isNaN(value: T, message?: string): void { + Assert.fail("Assert.isNaN unsupported") + } + + /** + * Asserts that value is not NaN. + * + * T Type of value. + * @param value Actual value. + * @param message Message to display on error. + */ + static isNotNaN(value: T, message?: string): void { + Assert.fail("Assert.isNotNaN unsupported") + } + + /** + * Asserts that the target is neither null nor undefined. + * @param value - actual value + * @param message - message to display on error + */ + static exists(value?: T | null, message?: string): void { + // todo: not accurate impl + expect(value == null).assertFalse() + } + + /** + * Asserts that the target is either null or undefined. + * @param value - actual value + * @param message - message to display on error + */ + static notExists(value?: T | null, message?: string): void { + if (value !== undefined && value !== null) return + Assert.fail(message ?? `actual '${value}' exists unexpectedly`) + } + + /** + * Asserts that value is undefined. + * @param value - actual value + * @param message - message to display on error + */ + static isUndefined(value?: T, message?: string): void { + expect(value).assertUndefined() + } + + /** + * Asserts that value is not undefined. + * @param value - actual value + * @param message - message to display on error + */ + static isDefined(value?: T, message?: string): void { + // todo: not accurate impl + expect(value === undefined).assertFalse() + } + + /** + * Asserts that value is a function. + * + * T Type of value. + * @param value Actual value. + * @param message Message to display on error. + */ + static isFunction(value: T, message?: string): void { + Assert.fail("Assert.isFunction unsupported") + } + + /** + * Asserts that value is not a function. + * + * T Type of value. + * @param value Actual value. + * @param message Message to display on error. + */ + static isNotFunction(value: T, message?: string): void { + Assert.fail("Assert.isNotFunction unsupported") + } + + /** + * Asserts that value is an object of type 'Object' + * (as revealed by Object.prototype.toString). + * + * T Type of value. + * @param value Actual value. + * @param message Message to display on error. + * @remarks The assertion does not match subclassed objects. + */ + static isObject(value: T, message?: string): void { + Assert.fail("Assert.isObject unsupported") + } + + /** + * Asserts that value is not an object of type 'Object' + * (as revealed by Object.prototype.toString). + * + * T Type of value. + * @param value Actual value. + * @param message Message to display on error. + */ + static isNotObject(value: T, message?: string): void { + Assert.fail("Assert.isNotObject unsupported") + } + + /** + * Asserts that value is an array. + * + * T Type of value. + * @param value Actual value. + * @param message Message to display on error. + */ + static isArray(value: T, message?: string): void { + Assert.fail("Assert.isArray unsupported") + } + + /** + * Asserts that value is not an array. + * + * T Type of value. + * @param value Actual value. + * @param message Message to display on error. + */ + static isNotArray(value: T, message?: string): void { + Assert.fail("Assert.isNotArray unsupported") + } + + /** + * Asserts that value is a string. + * + * T Type of value. + * @param value Actual value. + * @param message Message to display on error. + */ + static isString(value: T, message?: string): void { + Assert.fail("Assert.isString unsupported") + } + + /** + * Asserts that value is not a string. + * + * T Type of value. + * @param value Actual value. + * @param message Message to display on error. + */ + static isNotString(value: T, message?: string): void { + Assert.fail("Assert.isNotString unsupported") + } + + /** + * Asserts that value is a number. + * + * T Type of value. + * @param value Actual value. + * @param message Message to display on error. + */ + static isNumber(value: T, message?: string): void { + Assert.fail("Assert.isNumber unsupported") + } + + /** + * Asserts that value is not a number. + * + * T Type of value. + * @param value Actual value. + * @param message Message to display on error. + */ + static isNotNumber(value: T, message?: string): void { + Assert.fail("Assert.isNotNumber unsupported") + } + + /** + * Asserts that value is a finite number. + * Unlike `.isNumber`, this will fail for `NaN` and `Infinity`. + * + * T Type of value + * @param value Actual value + * @param message Message to display on error. + */ + static isFinite(value: T, message?: string): void { + Assert.fail("Assert.isFinite unsupported") + } + + /** + * Asserts that value is a boolean. + * + * T Type of value. + * @param value Actual value. + * @param message Message to display on error. + */ + static isBoolean(value: T, message?: string): void { + Assert.fail("Assert.isBoolean unsupported") + } + + /** + * Asserts that value is not a boolean. + * + * T Type of value. + * @param value Actual value. + * @param message Message to display on error. + */ + static isNotBoolean(value: T, message?: string): void { + Assert.fail("Assert.isNotBoolean unsupported") + } + + /** + * Asserts that value's type is name, as determined by Object.prototype.toString. + * + * T Type of value. + * @param value Actual value. + * @param name Potential expected type name of value. + * @param message Message to display on error. + */ + static typeOf(value: T, name: string, message?: string): void { + Assert.fail("Assert.typeOf unsupported") + } + + /** + * Asserts that value's type is not name, as determined by Object.prototype.toString. + * + * T Type of value. + * @param value Actual value. + * @param name Potential expected type name of value. + * @param message Message to display on error. + */ + static notTypeOf(value: T, name: string, message?: string): void { + Assert.fail("Assert.notTypeOf unsupported") + } + + /** + * Asserts that value is an instance of constructor. + * + * T Type of value. + * @param value Actual value. + * @param construct Potential expected contructor of value. + * @param message Message to display on error. + */ + static instanceOf(value: T, construct: Function, message?: string): void { + // todo: not accurate impl + // expect(value).assertInstanceOf(construct.name) + expect(value instanceof construct).assertTrue() + } + + /** + * Asserts that value is not an instance of constructor. + * + * T Type of value. + * @param value Actual value. + * @param constructor Potential expected contructor of value. + * @param message Message to display on error. + * / + static notInstanceOf(value: T, type: Function, message?: string): void*/ + + /** + * Asserts that haystack includes needle. + * + * @param haystack Container string. + * @param needle Potential substring of haystack. + * @param message Message to display on error. + * / + static include(haystack: string, needle: string, message?: string): void*/ + + /** + * Asserts that haystack includes needle. + * + * T Type of values in haystack. + * @param haystack Container array, set or map. + * @param needle Potential value contained in haystack. + * @param message Message to display on error. + * / + static include( + haystack: ReadonlyArray | ReadonlySet | ReadonlyMap, + needle: T, + message?: string, + ): void;*/ + + /** + * Asserts that haystack includes needle. + * + * T Type of values in haystack. + * @param haystack WeakSet container. + * @param needle Potential value contained in haystack. + * @param message Message to display on error. + * / + static include(haystack: WeakSet, needle: T, message?: string): void;*/ + + /** + * Asserts that haystack includes needle. + * + * T Type of haystack. + * @param haystack Object. + * @param needle Potential subset of the haystack's properties. + * @param message Message to display on error. + * / + static include(haystack: T, needle: Partial, message?: string): void;*/ + + /** + * Asserts that haystack does not includes needle. + * + * @param haystack Container string. + * @param needle Potential substring of haystack. + * @param message Message to display on error. + * / + static notInclude(haystack: string, needle: string, message?: string): void;*/ + + /** + * Asserts that haystack does not includes needle. + * + * T Type of values in haystack. + * @param haystack Container array, set or map. + * @param needle Potential value contained in haystack. + * @param message Message to display on error. + * / + static notInclude( + haystack: ReadonlyArray | ReadonlySet | ReadonlyMap, + needle: T, + message?: string, + ): void;*/ + + /** + * Asserts that haystack does not includes needle. + * + * T Type of values in haystack. + * @param haystack WeakSet container. + * @param needle Potential value contained in haystack. + * @param message Message to display on error. + * / + static notInclude(haystack: WeakSet, needle: T, message?: string): void;*/ + + /** + * Asserts that haystack does not includes needle. + * + * T Type of haystack. + * @param haystack Object. + * @param needle Potential subset of the haystack's properties. + * @param message Message to display on error. + * / + static notInclude(haystack: T, needle: Partial, message?: string): void;*/ + + /** + * Asserts that value matches the regular expression regexp. + * + * @param value Actual value. + * @param regexp Potential match of value. + * @param message Message to display on error. + */ + static match(value: string, regexp: RegExp, message?: string): void { + // todo: not accurate impl + expect(regexp.test(value)).assertTrue() + } + + /** + * Asserts that value does not match the regular expression regexp. + * + * @param value Actual value. + * @param regexp Potential match of value. + * @param message Message to display on error. + */ + static notMatch(expected: string, regexp: RegExp, message?: string): void { + Assert.fail("Assert.notMatch unsupported") + } + + /** + * Asserts that fn will throw an error. + * + * @param fn Function that may throw. + * @param errMsgMatcher Expected error message matcher. + * @param ignored Ignored parameter. + * @param message Message to display on error. + * / + static throw(fn: () => void, errMsgMatcher?: RegExp | string, ignored?: any, message?: string): void;*/ + + /** + * Asserts that the given function will throw an error. + * @param func - a function that may throw an error + */ + static throw(func: () => void): void { + Assert.throws(func) + } + + /** + * Asserts that the given function will throw an error. + * @param func - a function that may throw an error + */ + static throws(func: () => void): void { + let fnWrapper = () => { + try { + func() + } catch (e) { + throw new Error("fn thrown exception") + } + } + expect(fnWrapper).assertThrowError("fn thrown exception") + } + + /** + * Asserts that fn will throw an error. + * + * @param fn Function that may throw. + * @param errorLike Expected error constructor or error instance. + * @param errMsgMatcher Expected error message matcher. + * @param message Message to display on error. + * / + static throws( + fn: () => void, + errorLike?: ErrorConstructor | Error | null, + errMsgMatcher?: RegExp | string | null, + message?: string, + ): void;*/ + + /** + * Asserts that fn will throw an error. + * + * @param fn Function that may throw. + * @param errMsgMatcher Expected error message matcher. + * @param ignored Ignored parameter. + * @param message Message to display on error. + * / + static Throw(fn: () => void, errMsgMatcher?: RegExp | string, ignored?: any, message?: string): void;*/ + + /** + * Asserts that fn will throw an error. + * + * @param fn Function that may throw. + * @param errorLike Expected error constructor or error instance. + * @param errMsgMatcher Expected error message matcher. + * @param message Message to display on error. + * / + static Throw( + fn: () => void, + errorLike?: ErrorConstructor | Error | null, + errMsgMatcher?: RegExp | string | null, + message?: string, + ): void;*/ + + /** + * Asserts that fn will not throw an error. + * + * @param fn Function that may throw. + * @param errMsgMatcher Expected error message matcher. + * @param ignored Ignored parameter. + * @param message Message to display on error. + * / + static doesNotThrow(fn: () => void, errMsgMatcher?: RegExp | string, ignored?: any, message?: string): void;*/ + + /** + * Asserts that fn will not throw an error. + * + * @param fn Function that may throw. + * @param errorLike Expected error constructor or error instance. + * @param errMsgMatcher Expected error message matcher. + * @param message Message to display on error. + * / + static doesNotThrow( + fn: () => void, + errorLike?: ErrorConstructor | Error | null, + errMsgMatcher?: RegExp | string | null, + message?: string, + ): void;*/ + + /** + * Compares two values using operator. + * + * @param val1 Left value during comparison. + * @param operator Comparison operator. + * @param val2 Right value during comparison. + * @param message Message to display on error. + * / + static operator(val1: OperatorComparable, operator: Operator, val2: OperatorComparable, message?: string): void;*/ + + /** + * Asserts that the target is equal to expected, to within a +/- delta range. + * + * @param actual Actual value + * @param expected Potential expected value. + * @param delta Maximum differenced between values. + * @param message Message to display on error. + */ + static closeTo(actual: number, expected: number, delta: number, message?: string): void { + // implementation of 'assertClose' does not fit: + // expect(actual).assertClose(expected, delta) + + const diff = Math.abs(actual - expected) + if (diff == delta) + expect(diff).assertEqual(delta) + else + expect(diff).assertLess(delta) + } + + /** + * Asserts that the target is equal to expected, to within a +/- delta range. + * + * @param actual Actual value + * @param expected Potential expected value. + * @param delta Maximum differenced between values. + * @param message Message to display on error. + * / + static approximately(act: number, exp: number, delta: number, message?: string): void;*/ + + /** + * Asserts that non-object, non-array value inList appears in the flat array list. + * + * T Type of list values. + * @param inList Value expected to be in the list. + * @param list List of values. + * @param message Message to display on error. + */ + static oneOf(inList: T, list: T[], message?: string): void { + Assert.fail("Assert.oneOf unsupported") + } + + /** + * Asserts that the target does not contain any values. For arrays and + * strings, it checks the length property. For Map and Set instances, it + * checks the size property. For non-function objects, it gets the count + * of own enumerable string keys. + * + * T Type of object + * @param object Actual value. + * @param message Message to display on error. + */ + static isEmpty(object: T, message?: string): void { + // todo: implement + expect(object !== undefined).assertTrue() + } + + /** + * Asserts that the target contains values. For arrays and strings, it checks + * the length property. For Map and Set instances, it checks the size property. + * For non-function objects, it gets the count of own enumerable string keys. + * + * T Type of object. + * @param object Object to test. + * @param message Message to display on error. + */ + static isNotEmpty(object: T, message?: string): void { + // todo: implement + expect(object !== undefined).assertTrue() + } +} diff --git a/incremental/harness/src/ohos/index.ts b/incremental/harness/src/ohos/index.ts new file mode 100644 index 000000000..a7fe54fe3 --- /dev/null +++ b/incremental/harness/src/ohos/index.ts @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2022-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 { Assert } from "./chai" +export { startTests } from "./mocha" + +export function test(title: any, fn?: any) { + throw new Error("unsupported test: " + title) +} + +export function suite(title: any, fn?: any) { + throw new Error("unsupported suite: " + title) +} + +export function suiteSetup(title: any, fn?: any) { + throw new Error("unsupported suiteSetup: " + title) +} diff --git a/incremental/harness/src/ohos/mocha.ts b/incremental/harness/src/ohos/mocha.ts new file mode 100644 index 000000000..ba874380c --- /dev/null +++ b/incremental/harness/src/ohos/mocha.ts @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2022-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, beforeEach, it, Size } from "@ohos/hypium" + +declare namespace globalThis { + let __OpenHarmony: boolean + let __generateGolden: boolean +} + +globalThis.__OpenHarmony = true + +type Fn = () => void + +const suiteMap = new Map() + +export function startTests(generateGolden: boolean = false) { + globalThis.__generateGolden = generateGolden + suiteMap.forEach((fn: Fn, title: string) => { + describe(title, function () { + fn() + }) + }) +} + +(suiteSetup as any) = (title: string, fn: Fn): void => { + beforeEach(fn) +} + +(suite as any) = (title: string, fn: Fn): void => { + suiteMap.set(title, fn) +} + +(test as any) = (title: string, fn?: Fn): void => { + it(fn ? title : `[SKIP] ${title}`, Size.MEDIUMTEST, fn ? fn : () => { }) +} + +(test as any).skip = (title: string, fn?: Fn): void => { + it(`[SKIP] ${title}`, Size.MEDIUMTEST, () => { }) +} + + +interface TimeFn { + now: () => number +} + +(performance as TimeFn) = { + now: (): number => { + return Date.now() + } +} diff --git a/incremental/harness/src/typescript/index.ts b/incremental/harness/src/typescript/index.ts new file mode 100644 index 000000000..a27db9dfd --- /dev/null +++ b/incremental/harness/src/typescript/index.ts @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2022-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 { assert as Assert } from "chai" +export { suite, suiteSetup, test } from "mocha" + +export function startTests(generateGolden: boolean = false) { + throw new Error("unsupported startTests: " + generateGolden) +} diff --git a/incremental/harness/tsconfig-ohos.json b/incremental/harness/tsconfig-ohos.json new file mode 100644 index 000000000..43f1e0e46 --- /dev/null +++ b/incremental/harness/tsconfig-ohos.json @@ -0,0 +1,18 @@ +{ + "extends": "@koalaui/build-common/tsconfig.json", + "compilerOptions": { + "baseUrl": ".", + "rootDir": ".", + "outDir": "build", + "module": "CommonJS", + "paths": { + "@koalaui/compat": ["../compat/src/ohos"], + "@koalaui/common": ["../common/src"], + "#harness": ["./src/ohos"] + } + }, + "include": [ + "./src/index.ts", + "./src/ohos/**/*" + ] +} \ No newline at end of file diff --git a/incremental/harness/tsconfig.json b/incremental/harness/tsconfig.json new file mode 100644 index 000000000..eda1e80d3 --- /dev/null +++ b/incremental/harness/tsconfig.json @@ -0,0 +1,18 @@ +{ + "extends": "@koalaui/build-common/tsconfig.json", + "compilerOptions": { + "baseUrl": ".", + "rootDir": ".", + "outDir": "build", + "module": "CommonJS", + "paths": { + "@koalaui/compat": ["../compat/src/typescript"], + "@koalaui/common": ["../common/src"], + "#harness": ["./src/typescript"] + } + }, + "include": [ + "./src/index.ts", + "./src/typescript/**/*" + ] +} \ No newline at end of file -- Gitee From 370bc1badf272960ba90cff075bb3b3012641866 Mon Sep 17 00:00:00 2001 From: Sergey Malenkov Date: Tue, 4 Feb 2025 16:40:29 +0300 Subject: [PATCH 02/15] Add new module to CI Signed-off-by: Sergey Malenkov --- .gitlab-ci.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index febaee23f..3a21196fe 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -23,6 +23,7 @@ include: - incremental/build-common/.gitlab-ci.yml - incremental/compat/.gitlab-ci.yml - incremental/common/.gitlab-ci.yml + - incremental/harness/.gitlab-ci.yml - incremental/compiler-plugin/.gitlab-ci.yml - incremental/runtime/.gitlab-ci.yml - incremental/demo-playground/.gitlab-ci.yml -- Gitee From 6476db16e7ff9cf80a8ca17205dcb7cab5aace95 Mon Sep 17 00:00:00 2001 From: Sergey Malenkov Date: Tue, 4 Feb 2025 18:14:59 +0300 Subject: [PATCH 03/15] Use harness module instead of trivial implementation Signed-off-by: Sergey Malenkov --- incremental/package.json | 1 + .../runtime/arktsconfig-test-unmemoized.json | 3 +- .../runtime/arktsconfig-unmemoize.json | 7 +- incremental/runtime/package.json | 6 +- .../test-arkts/animation/Easing.test.ts | 2 +- .../test-arkts/common/MarkableQueue.test.ts | 2 +- incremental/runtime/test-arkts/harness.ts | 871 ------------------ .../runtime/test-arkts/memo/bind.test.ts | 2 +- .../test-arkts/memo/changeListener.test.ts | 2 +- .../test-arkts/memo/contextLocal.test.ts | 2 +- .../runtime/test-arkts/memo/remember.test.ts | 2 +- .../runtime/test-arkts/memo/repeat.test.ts | 2 +- .../runtime/test-arkts/states/Journal.test.ts | 10 +- .../runtime/test-arkts/states/State.test.ts | 34 +- .../test-arkts/states/state_basics.test.ts | 2 +- incremental/runtime/test-arkts/tests.ts | 2 +- .../runtime/test-arkts/tree/TreeNode.test.ts | 10 +- .../runtime/test-arkts/tree/TreePath.test.ts | 2 +- 18 files changed, 50 insertions(+), 912 deletions(-) delete mode 100644 incremental/runtime/test-arkts/harness.ts diff --git a/incremental/package.json b/incremental/package.json index aa7ae3006..824bca843 100644 --- a/incremental/package.json +++ b/incremental/package.json @@ -5,6 +5,7 @@ "build-common", "compat", "common", + "harness", "runtime", "demo-playground", "compiler-plugin" diff --git a/incremental/runtime/arktsconfig-test-unmemoized.json b/incremental/runtime/arktsconfig-test-unmemoized.json index 963667073..68b798a1c 100644 --- a/incremental/runtime/arktsconfig-test-unmemoized.json +++ b/incremental/runtime/arktsconfig-test-unmemoized.json @@ -5,7 +5,8 @@ "baseUrl": "./build/unmemoized", "paths": { "@koalaui/compat": [ "../../../compat/src/arkts" ], - "@koalaui/common": [ "../../../common/src" ] + "@koalaui/common": [ "../../../common/src" ], + "@koalaui/harness": [ "../../../harness/src/arkts" ] } }, "include": [ diff --git a/incremental/runtime/arktsconfig-unmemoize.json b/incremental/runtime/arktsconfig-unmemoize.json index 7926a9e94..d55f38fba 100644 --- a/incremental/runtime/arktsconfig-unmemoize.json +++ b/incremental/runtime/arktsconfig-unmemoize.json @@ -13,7 +13,12 @@ "only_unmemoize": true, "unmemoizeDir": "./build/unmemoized" } - ] + ], + "paths": { + "@koalaui/common": ["../../common/src"], + "@koalaui/compat": ["../../compat/src/arkts"], + "@koalaui/harness": ["../../harness/src/arkts"] + } }, "include": [ "src/**/*.ts", diff --git a/incremental/runtime/package.json b/incremental/runtime/package.json index 988b66736..9db5a7009 100644 --- a/incremental/runtime/package.json +++ b/incremental/runtime/package.json @@ -17,14 +17,15 @@ "test": "mocha", "test:coverage": "nyc mocha", "panda:sdk:install": "cd ../tools/panda && npm run panda:sdk:install", - "arkts:test:run": "bash ../tools/panda/arkts/ark build/runtime-tests.abc --ark-boot-files ../compat/build/compat.abc:../common/build/common.abc --ark-entry-point @koalaui.runtime-tests.test-arkts.tests.ETSGLOBAL::main", - "arkts:test": "npm run panda:sdk:install && npm run build:compat && npm run build:common && npm run build:runtime:with:tests && npm run arkts:test:run", + "arkts:test:run": "bash ../tools/panda/arkts/ark build/runtime-tests.abc --ark-boot-files ../compat/build/compat.abc:../common/build/common.abc:../harness/build/harness.abc --ark-entry-point @koalaui.runtime-tests.test-arkts.tests.ETSGLOBAL::main", + "arkts:test": "npm run panda:sdk:install && npm run build:compat && npm run build:common && npm run build:harness && npm run build:runtime:with:tests && npm run arkts:test:run", "unmemoize": "mkdir -p build/unmemoized/abc && mkdir -p build/test/lib && ets-tsc -b arktsconfig-unmemoize.json && mkdir -p build/test/lib", "compile:arkts": "npm run unmemoize && ../tools/panda/arkts/arktsc --arktsconfig arktsconfig-run-unmemoized.json", "build:compat": "npm run build:compat --prefix ../compat", "build:compat:inc": "npm run build:compat:inc --prefix ../compat", "build:common": "npm run build:common --prefix ../common", "build:common:inc": "npm run build:common:inc --prefix ../common", + "build:harness": "npm run build:harness --prefix ../harness", "build:runtime": "npm run build:runtime:inc", "build:runtime:inc": "npm run unmemoize && fast-arktsc --input-files ./arktsconfig-run-unmemoized.json --output-dir ./build --compiler ../tools/panda/arkts/arktsc --link-name runtime && ninja ${NINJA_OPTIONS} -f build/build.ninja", "build:runtime:inc:capi": "npm run unmemoize && fast-arktsc --input-files ./arktsconfig-run-unmemoized.json --output-dir ./build --compiler ../tools/panda/arkts/arktsc-capi --link-name runtime --file-option && PANDA_SDK_PATH=../tools/panda/node_modules/@panda/sdk ninja ${NINJA_OPTIONS} -f build/build.ninja", @@ -42,6 +43,7 @@ "@koalaui/compat": "1.4.7+devel" }, "devDependencies": { + "@koalaui/harness": "1.4.4+devel", "@types/chai": "^4.3.1", "@types/mocha": "^9.1.0", "@typescript-eslint/eslint-plugin": "^5.20.0", diff --git a/incremental/runtime/test-arkts/animation/Easing.test.ts b/incremental/runtime/test-arkts/animation/Easing.test.ts index 5ef8d3bc6..dacb7cf49 100644 --- a/incremental/runtime/test-arkts/animation/Easing.test.ts +++ b/incremental/runtime/test-arkts/animation/Easing.test.ts @@ -14,7 +14,7 @@ */ // TODO: the real chai exports 'assert', but 'assert' is still a keyword in ArkTS -import { Assert, suite, test } from "../harness" +import { Assert, suite, test } from "@koalaui/harness" import { float64, int32 } from "@koalaui/common" import { Easing, EasingCurve, EasingStepJump } from "../../src/animation/Easing" export const FileName = "animation/Easing.test" diff --git a/incremental/runtime/test-arkts/common/MarkableQueue.test.ts b/incremental/runtime/test-arkts/common/MarkableQueue.test.ts index c53180bad..fa519859f 100644 --- a/incremental/runtime/test-arkts/common/MarkableQueue.test.ts +++ b/incremental/runtime/test-arkts/common/MarkableQueue.test.ts @@ -14,7 +14,7 @@ */ // TODO: the real chai exports 'assert', but 'assert' is still a keyword in ArkTS -import { Assert, suite, test } from "../harness" +import { Assert, suite, test } from "@koalaui/harness" import { MarkableQueue, markableQueue } from "@koalaui/common" export const FileName = "common/MarkableQueue.test" diff --git a/incremental/runtime/test-arkts/harness.ts b/incremental/runtime/test-arkts/harness.ts deleted file mode 100644 index a4340899a..000000000 --- a/incremental/runtime/test-arkts/harness.ts +++ /dev/null @@ -1,871 +0,0 @@ -import { int32 } from "@koalaui/common" - -// TODO: need to mimic to "chai" -class AnAssertionError extends Error { - // public name: string - // public showDiff: boolean - // public stack: string - constructor(message: string) { - super(message) - } - toString(): string { - let text = "Assert: " + this.message - return text - } -} - -// TODO: should be compatible with "chai" -export class Assert { - /** - * Throws a failure. - * @param message - message to display on error - */ - static fail(message?: string): void { - throw new AnAssertionError(message ?? "Assert.fail") - } - - /** - * Asserts that object is truthy. - * - * T Type of object. - * @param object Object to test. - * @param message Message to display on error. - */ - static isOk(value: T, message?: string): void { - Assert.fail("Assert.isOk unsupported") - } - - /** - * Asserts that object is truthy. - * - * T Type of object. - * @param object Object to test. - * @param message Message to display on error. - */ - static ok(value: T, message?: string): void { - Assert.fail("Assert.ok unsupported") - } - - /** - * Asserts that object is falsy. - * - * T Type of object. - * @param object Object to test. - * @param message Message to display on error. - */ - static isNotOk(value: T, message?: string): void { - Assert.fail("Assert.isNotOk unsupported") - } - - /** - * Asserts that object is falsy. - * - * T Type of object. - * @param object Object to test. - * @param message Message to display on error. - */ - static notOk(value: T, message?: string): void { - Assert.fail("Assert.notOk unsupported") - } - - /** - * Asserts non-strict equality (==) of actual and expected. - * @param actual - actual value - * @param expected - potential expected value - * @param message - message to display on error - */ - static equal(actual: T, expected: T, message?: string): void { - if (actual == expected) return - Assert.fail(message ?? `actual '${actual}' is not equal to expected '${expected}'`) - } - - /** - * Asserts non-strict inequality (!=) of actual and expected. - * @param actual - actual value - * @param expected - potential expected value - * @param message - message to display on error - */ - static notEqual(actual: T, expected: T, message?: string): void { - if (actual != expected) return - Assert.fail(message ?? `actual '${actual}' is equal to expected '${expected}'`) - } - - /** - * Asserts strict equality (===) of actual and expected. - * @param actual - actual value - * @param expected - potential expected value - * @param message - message to display on error - */ - static strictEqual(actual: T, expected: T, message?: string): void { - if (actual === expected) return - Assert.fail(message ?? `actual '${actual}' is not strictly equal to expected '${expected}'`) - } - - /** - * Asserts strict inequality (!==) of actual and expected. - * @param actual - actual value - * @param expected - potential expected value - * @param message - message to display on error - */ - static notStrictEqual(actual: T, expected: T, message?: string): void { - if (actual !== expected) return - Assert.fail(message ?? `actual '${actual}' is strictly equal to expected '${expected}'`) - } - - /** - * Asserts that actual is deeply equal to expected. - * @param actual - actual value array - * @param expected - potential expected value array - * @param message - message to display on error - */ - static deepEqual(actual: Array, expected: Array, message?: string): void { - if (equalArrayContent(actual, expected)) return - Assert.fail(message ?? `actual '${actual}' is not deeply equal to expected '${expected}'`) - } - - /** - * Asserts that actual is not deeply equal to expected. - * @param actual - actual value array - * @param expected - potential expected value array - * @param message - message to display on error - */ - static notDeepEqual(actual: Array, expected: Array, message?: string): void { - if (!equalArrayContent(actual, expected)) return - Assert.fail(message ?? `actual '${actual}' is deeply equal to expected '${expected}'`) - } - - /** - * Asserts valueToCheck is strictly greater than (>) valueToBeAbove. - * - * @param valueToCheck Actual value. - * @param valueToBeAbove Minimum Potential expected value. - * @param message Message to display on error. - */ - static isAbove(valueToCheck: number, valueToBeAbove: number, message?: string): void { - Assert.fail("Assert.isAbove unsupported") - } - - /** - * Asserts valueToCheck is greater than or equal to (>=) valueToBeAtLeast. - * - * @param valueToCheck Actual value. - * @param valueToBeAtLeast Minimum Potential expected value. - * @param message Message to display on error. - */ - static isAtLeast(valueToCheck: number, valueToBeAtLeast: number, message?: string): void { - Assert.fail("Assert.isAtLeast unsupported") - } - - /** - * Asserts valueToCheck is strictly less than (<) valueToBeBelow. - * - * @param valueToCheck Actual value. - * @param valueToBeBelow Minimum Potential expected value. - * @param message Message to display on error. - */ - static isBelow(valueToCheck: number, valueToBeBelow: number, message?: string): void { - Assert.fail("Assert.isBelow unsupported") - } - - /** - * Asserts valueToCheck is less than or equal to (<=) valueToBeAtMost. - * - * @param valueToCheck Actual value. - * @param valueToBeAtMost Minimum Potential expected value. - * @param message Message to display on error. - */ - static isAtMost(valueToCheck: number, valueToBeAtMost: number, message?: string): void { - Assert.fail("Assert.isAtMost unsupported") - } - - /** - * Asserts that value is true. - * @param value - actual value - * @param message - message to display on error - */ - static isTrue(value?: boolean, message?: string): void { - if (value == true) return - Assert.fail(message ?? `actual '${value}' is not true unexpectedly`) - } - - /** - * Asserts that value is not true. - * @param value - actual value - * @param message - message to display on error - */ - static isNotTrue(value?: boolean, message?: string): void { - if (value != true) return - Assert.fail(message ?? `actual '${value}' is true unexpectedly`) - } - - /** - * Asserts that value is false. - * @param value - actual value - * @param message - message to display on error - */ - static isFalse(value?: boolean, message?: string): void { - if (value == false) return - Assert.fail(message ?? `actual '${value}' is not false unexpectedly`) - } - - /** - * Asserts that value is not false. - * @param value - actual value - * @param message - message to display on error - */ - static isNotFalse(value?: boolean, message?: string): void { - if (value != false) return - Assert.fail(message ?? `actual '${value}' is false unexpectedly`) - } - - /** - * Asserts that value is null. - * @param value - actual value - * @param message - message to display on error - */ - static isNull(value: T | null, message?: string): void { - if (value === null) return - Assert.fail(message ?? `actual '${value}' is not null unexpectedly`) - } - - /** - * Asserts that value is not null. - * @param value - actual value - * @param message - message to display on error - */ - static isNotNull(value: T | null, message?: string): void { - if (value !== null) return - Assert.fail(message ?? `actual '${value}' is null unexpectedly`) - } - - /** - * Asserts that value is NaN. - * - * T Type of value. - * @param value Actual value. - * @param message Message to display on error. - */ - static isNaN(value: T, message?: string): void { - Assert.fail("Assert.isNaN unsupported") - } - - /** - * Asserts that value is not NaN. - * - * T Type of value. - * @param value Actual value. - * @param message Message to display on error. - */ - static isNotNaN(value: T, message?: string): void { - Assert.fail("Assert.isNotNaN unsupported") - } - - /** - * Asserts that the target is neither null nor undefined. - * @param value - actual value - * @param message - message to display on error - */ - static exists(value?: T | null, message?: string): void { - if (value === undefined || value === null) return - Assert.fail(message ?? `actual '${value}' does not exist unexpectedly`) - } - - /** - * Asserts that the target is either null or undefined. - * @param value - actual value - * @param message - message to display on error - */ - static notExists(value?: T | null, message?: string): void { - if (value !== undefined && value !== null) return - Assert.fail(message ?? `actual '${value}' exists unexpectedly`) - } - - /** - * Asserts that value is undefined. - * @param value - actual value - * @param message - message to display on error - */ - static isUndefined(value?: T, message?: string): void { - if (value === undefined) return - Assert.fail(message ?? `actual '${value}' is defined unexpectedly`) - } - - /** - * Asserts that value is not undefined. - * @param value - actual value - * @param message - message to display on error - */ - static isDefined(value?: T, message?: string): void { - if (value !== undefined) return - Assert.fail(message ?? `actual '${value}' is undefined unexpectedly`) - } - - /** - * Asserts that value is a function. - * - * T Type of value. - * @param value Actual value. - * @param message Message to display on error. - */ - static isFunction(value: T, message?: string): void { - Assert.fail("Assert.isFunction unsupported") - } - - /** - * Asserts that value is not a function. - * - * T Type of value. - * @param value Actual value. - * @param message Message to display on error. - */ - static isNotFunction(value: T, message?: string): void { - Assert.fail("Assert.isNotFunction unsupported") - } - - /** - * Asserts that value is an object of type 'Object' - * (as revealed by Object.prototype.toString). - * - * T Type of value. - * @param value Actual value. - * @param message Message to display on error. - * @remarks The assertion does not match subclassed objects. - */ - static isObject(value: T, message?: string): void { - Assert.fail("Assert.isObject unsupported") - } - - /** - * Asserts that value is not an object of type 'Object' - * (as revealed by Object.prototype.toString). - * - * T Type of value. - * @param value Actual value. - * @param message Message to display on error. - */ - static isNotObject(value: T, message?: string): void { - Assert.fail("Assert.isNotObject unsupported") - } - - /** - * Asserts that value is an array. - * - * T Type of value. - * @param value Actual value. - * @param message Message to display on error. - */ - static isArray(value: T, message?: string): void { - Assert.fail("Assert.isArray unsupported") - } - - /** - * Asserts that value is not an array. - * - * T Type of value. - * @param value Actual value. - * @param message Message to display on error. - */ - static isNotArray(value: T, message?: string): void { - Assert.fail("Assert.isNotArray unsupported") - } - - /** - * Asserts that value is a string. - * - * T Type of value. - * @param value Actual value. - * @param message Message to display on error. - */ - static isString(value: T, message?: string): void { - Assert.fail("Assert.isString unsupported") - } - - /** - * Asserts that value is not a string. - * - * T Type of value. - * @param value Actual value. - * @param message Message to display on error. - */ - static isNotString(value: T, message?: string): void { - Assert.fail("Assert.isNotString unsupported") - } - - /** - * Asserts that value is a number. - * - * T Type of value. - * @param value Actual value. - * @param message Message to display on error. - */ - static isNumber(value: T, message?: string): void { - Assert.fail("Assert.isNumber unsupported") - } - - /** - * Asserts that value is not a number. - * - * T Type of value. - * @param value Actual value. - * @param message Message to display on error. - */ - static isNotNumber(value: T, message?: string): void { - Assert.fail("Assert.isNotNumber unsupported") - } - - /** - * Asserts that value is a finite number. - * Unlike `.isNumber`, this will fail for `NaN` and `Infinity`. - * - * T Type of value - * @param value Actual value - * @param message Message to display on error. - */ - static isFinite(value: T, message?: string): void { - Assert.fail("Assert.isFinite unsupported") - } - - /** - * Asserts that value is a boolean. - * - * T Type of value. - * @param value Actual value. - * @param message Message to display on error. - */ - static isBoolean(value: T, message?: string): void { - Assert.fail("Assert.isBoolean unsupported") - } - - /** - * Asserts that value is not a boolean. - * - * T Type of value. - * @param value Actual value. - * @param message Message to display on error. - */ - static isNotBoolean(value: T, message?: string): void { - Assert.fail("Assert.isNotBoolean unsupported") - } - - /** - * Asserts that value's type is name, as determined by Object.prototype.toString. - * - * T Type of value. - * @param value Actual value. - * @param name Potential expected type name of value. - * @param message Message to display on error. - */ - static typeOf(value: T, name: string, message?: string): void { - Assert.fail("Assert.typeOf unsupported") - } - - /** - * Asserts that value's type is not name, as determined by Object.prototype.toString. - * - * T Type of value. - * @param value Actual value. - * @param name Potential expected type name of value. - * @param message Message to display on error. - */ - static notTypeOf(value: T, name: string, message?: string): void { - Assert.fail("Assert.notTypeOf unsupported") - } - - /** - * Asserts that value is an instance of constructor. - * - * T Type of value. - * @param value Actual value. - * @param construct Potential expected contructor of value. - * @param message Message to display on error. - * / - static instanceOf(value: T, construct: Function, message?: string): void*/ - - /** - * Asserts that value is not an instance of constructor. - * - * T Type of value. - * @param value Actual value. - * @param constructor Potential expected contructor of value. - * @param message Message to display on error. - * / - static notInstanceOf(value: T, type: Function, message?: string): void*/ - - /** - * Asserts that haystack includes needle. - * - * @param haystack Container string. - * @param needle Potential substring of haystack. - * @param message Message to display on error. - * / - static include(haystack: string, needle: string, message?: string): void*/ - - /** - * Asserts that haystack includes needle. - * - * T Type of values in haystack. - * @param haystack Container array, set or map. - * @param needle Potential value contained in haystack. - * @param message Message to display on error. - * / - static include( - haystack: ReadonlyArray | ReadonlySet | ReadonlyMap, - needle: T, - message?: string, - ): void;*/ - - /** - * Asserts that haystack includes needle. - * - * T Type of values in haystack. - * @param haystack WeakSet container. - * @param needle Potential value contained in haystack. - * @param message Message to display on error. - * / - static include(haystack: WeakSet, needle: T, message?: string): void;*/ - - /** - * Asserts that haystack includes needle. - * - * T Type of haystack. - * @param haystack Object. - * @param needle Potential subset of the haystack's properties. - * @param message Message to display on error. - * / - static include(haystack: T, needle: Partial, message?: string): void;*/ - - /** - * Asserts that haystack does not includes needle. - * - * @param haystack Container string. - * @param needle Potential substring of haystack. - * @param message Message to display on error. - * / - static notInclude(haystack: string, needle: string, message?: string): void;*/ - - /** - * Asserts that haystack does not includes needle. - * - * T Type of values in haystack. - * @param haystack Container array, set or map. - * @param needle Potential value contained in haystack. - * @param message Message to display on error. - * / - static notInclude( - haystack: ReadonlyArray | ReadonlySet | ReadonlyMap, - needle: T, - message?: string, - ): void;*/ - - /** - * Asserts that haystack does not includes needle. - * - * T Type of values in haystack. - * @param haystack WeakSet container. - * @param needle Potential value contained in haystack. - * @param message Message to display on error. - * / - static notInclude(haystack: WeakSet, needle: T, message?: string): void;*/ - - /** - * Asserts that haystack does not includes needle. - * - * T Type of haystack. - * @param haystack Object. - * @param needle Potential subset of the haystack's properties. - * @param message Message to display on error. - * / - static notInclude(haystack: T, needle: Partial, message?: string): void;*/ - - /** - * Asserts that value matches the regular expression regexp. - * - * @param value Actual value. - * @param regexp Potential match of value. - * @param message Message to display on error. - */ - static match(value: string, regexp: RegExp, message?: string): void { - Assert.fail("Assert.match unsupported") - } - - /** - * Asserts that value does not match the regular expression regexp. - * - * @param value Actual value. - * @param regexp Potential match of value. - * @param message Message to display on error. - */ - static notMatch(expected: string, regexp: RegExp, message?: string): void { - Assert.fail("Assert.notMatch unsupported") - } - - /** - * Asserts that fn will throw an error. - * - * @param fn Function that may throw. - * @param errMsgMatcher Expected error message matcher. - * @param ignored Ignored parameter. - * @param message Message to display on error. - * / - static throw(fn: () => void, errMsgMatcher?: RegExp | string, ignored?: any, message?: string): void;*/ - - /** - * Asserts that fn will throw an error. - * - * @param fn Function that may throw. - * @param errorLike Expected error constructor or error instance. - * @param errMsgMatcher Expected error message matcher. - * @param message Message to display on error. - * / - static throw( - fn: () => void, - errorLike?: ErrorConstructor | Error | null, - errMsgMatcher?: RegExp | string | null, - message?: string, - ): void;*/ - - /** - * Asserts that the given function will throw an error. - * @param func - a function that may throw an error - */ - static throws(func: () => void): void { - let expected: Error | undefined = undefined - try { - func() - } catch (error) { - expected = error as Error - } - if (expected) return - Assert.fail("expected error is not thrown") - } - - /** - * Asserts that fn will throw an error. - * - * @param fn Function that may throw. - * @param errorLike Expected error constructor or error instance. - * @param errMsgMatcher Expected error message matcher. - * @param message Message to display on error. - * / - static throws( - fn: () => void, - errorLike?: ErrorConstructor | Error | null, - errMsgMatcher?: RegExp | string | null, - message?: string, - ): void;*/ - - /** - * Asserts that fn will throw an error. - * - * @param fn Function that may throw. - * @param errMsgMatcher Expected error message matcher. - * @param ignored Ignored parameter. - * @param message Message to display on error. - * / - static Throw(fn: () => void, errMsgMatcher?: RegExp | string, ignored?: any, message?: string): void;*/ - - /** - * Asserts that fn will throw an error. - * - * @param fn Function that may throw. - * @param errorLike Expected error constructor or error instance. - * @param errMsgMatcher Expected error message matcher. - * @param message Message to display on error. - * / - static Throw( - fn: () => void, - errorLike?: ErrorConstructor | Error | null, - errMsgMatcher?: RegExp | string | null, - message?: string, - ): void;*/ - - /** - * Asserts that fn will not throw an error. - * - * @param fn Function that may throw. - * @param errMsgMatcher Expected error message matcher. - * @param ignored Ignored parameter. - * @param message Message to display on error. - * / - static doesNotThrow(fn: () => void, errMsgMatcher?: RegExp | string, ignored?: any, message?: string): void;*/ - - /** - * Asserts that fn will not throw an error. - * - * @param fn Function that may throw. - * @param errorLike Expected error constructor or error instance. - * @param errMsgMatcher Expected error message matcher. - * @param message Message to display on error. - * / - static doesNotThrow( - fn: () => void, - errorLike?: ErrorConstructor | Error | null, - errMsgMatcher?: RegExp | string | null, - message?: string, - ): void;*/ - - /** - * Compares two values using operator. - * - * @param val1 Left value during comparison. - * @param operator Comparison operator. - * @param val2 Right value during comparison. - * @param message Message to display on error. - * / - static operator(val1: OperatorComparable, operator: Operator, val2: OperatorComparable, message?: string): void;*/ - - /** - * Asserts that the target is equal to expected, to within a +/- delta range. - * - * @param actual Actual value - * @param expected Potential expected value. - * @param delta Maximum differenced between values. - * @param message Message to display on error. - * / - static closeTo(actual: number, expected: number, delta: number, message?: string): void;*/ - - /** - * Asserts that the target is equal to expected, to within a +/- delta range. - * - * @param actual Actual value - * @param expected Potential expected value. - * @param delta Maximum differenced between values. - * @param message Message to display on error. - * / - static approximately(act: number, exp: number, delta: number, message?: string): void;*/ - - /** - * Asserts that non-object, non-array value inList appears in the flat array list. - * - * T Type of list values. - * @param inList Value expected to be in the list. - * @param list List of values. - * @param message Message to display on error. - */ - static oneOf(inList: T, list: T[], message?: string): void { - Assert.fail("Assert.oneOf unsupported") - } - - /** - * Asserts that the target does not contain any values. For arrays and - * strings, it checks the length property. For Map and Set instances, it - * checks the size property. For non-function objects, it gets the count - * of own enumerable string keys. - * - * T Type of object - * @param object Actual value. - * @param message Message to display on error. - */ - static isEmpty(object: T, message?: string): void { - const size = getContainerSize(object) - if (size != 0) Assert.fail(message ?? (size < 0 ? "unsupported container" : "container is not empty unexpectedly")) - } - - /** - * Asserts that the target contains values. For arrays and strings, it checks - * the length property. For Map and Set instances, it checks the size property. - * For non-function objects, it gets the count of own enumerable string keys. - * - * T Type of object. - * @param object Object to test. - * @param message Message to display on error. - */ - static isNotEmpty(object: T, message?: string): void { - const size = getContainerSize(object) - if (size <= 0) Assert.fail(message ?? (size < 0 ? "unsupported container" : "container is empty unexpectedly")) - } -} - -const stack = new Array() - -function getCurrentSuite(): Suite | undefined { - const index = stack.length - 1 - if (index < 0) return undefined - const test = stack[index] - if (test instanceof Suite) return test as Suite - throw new Error("test is already running") -} - -class Test { - public error: Error | undefined = undefined - readonly name: string - readonly content: () => void - constructor(name: string, content?: () => void) { - this.name = name - this.content = () => { - const suite = getCurrentSuite() - try { - stack.push(this) - content?.() - } catch (error) { - this.error = error as Error - console.log(this.error!.toString()) - console.log(this.error!.stack) - } finally { - stack.pop() - console.log(" ".repeat(stack.length) + this.toString()) - if (suite) suite.tests.push(this) - else if (!this.passed) throw new Error("TEST FAILED") - } - } - } - get passed(): boolean { - return this.error === undefined - } - toString(): string { - let result = this.passed ? "passed" : "failed" - return `${result} test: "${this.name}"` - } -} - -class Suite extends Test { - readonly tests = new Array() - constructor(name: string, content?: () => void) { - super(name, content) - } - private get failures(): int32 { - let failed = 0 - for (let i = 0; i < this.tests.length; i++) { - const test = this.tests[i] - if (!test.passed) failed++ - } - return failed - } - override get passed(): boolean { - return super.passed && this.failures == 0 - } - override toString(): string { - const failures = this.failures - const result = failures > 0 ? `failed tests '${failures}' in` : this.error ? "failed" : "passed" - return `${result} suite: "${this.name}"` - } -} - -export function test(name: string, content?: () => void) { - new Test(name, content).content() -} - -export function suite(name: string, content?: () => void) { - new Suite(name, content).content() -} - -function equalArrayContent(actual: Array, expected: Array): boolean { - const length = actual.length - if (expected.length != length) return false - for (let i = 0; i < length; i++) { - if (expected[i] != actual[i]) return false - } - return true -} - -/** - * @param object - a container to check - * @returns a container size, or -1 if the given container is not supported - */ -function getContainerSize(container: T): int32 { - if (container instanceof Map) return container.size as int32 - if (container instanceof Set) return container.size as int32 - if (container instanceof Array) return container.length as int32 - if (container instanceof String) return container.length as int32 - return -1 -} diff --git a/incremental/runtime/test-arkts/memo/bind.test.ts b/incremental/runtime/test-arkts/memo/bind.test.ts index 01df7ad81..1e443e2e2 100644 --- a/incremental/runtime/test-arkts/memo/bind.test.ts +++ b/incremental/runtime/test-arkts/memo/bind.test.ts @@ -14,7 +14,7 @@ */ // TODO: the real chai exports 'assert', but 'assert' is still a keyword in ArkTS -import { Assert, suite, test } from "../harness" +import { Assert, suite, test } from "@koalaui/harness" import { asArray } from "@koalaui/common" import { GlobalStateManager, diff --git a/incremental/runtime/test-arkts/memo/changeListener.test.ts b/incremental/runtime/test-arkts/memo/changeListener.test.ts index b474a6851..1f9081e14 100644 --- a/incremental/runtime/test-arkts/memo/changeListener.test.ts +++ b/incremental/runtime/test-arkts/memo/changeListener.test.ts @@ -14,7 +14,7 @@ */ // TODO: the real chai exports 'assert', but 'assert' is still a keyword in ArkTS -import { Assert, suite, test } from "../harness" +import { Assert, suite, test } from "@koalaui/harness" import { GlobalStateManager, OnChange, diff --git a/incremental/runtime/test-arkts/memo/contextLocal.test.ts b/incremental/runtime/test-arkts/memo/contextLocal.test.ts index 8fcf17edd..86eb28416 100644 --- a/incremental/runtime/test-arkts/memo/contextLocal.test.ts +++ b/incremental/runtime/test-arkts/memo/contextLocal.test.ts @@ -14,7 +14,7 @@ */ // TODO: the real chai exports 'assert', but 'assert' is still a keyword in ArkTS -import { Assert, suite, test } from "../harness" +import { Assert, suite, test } from "@koalaui/harness" import { asArray } from "@koalaui/common" import { State, diff --git a/incremental/runtime/test-arkts/memo/remember.test.ts b/incremental/runtime/test-arkts/memo/remember.test.ts index 2fb07379a..98829e95d 100644 --- a/incremental/runtime/test-arkts/memo/remember.test.ts +++ b/incremental/runtime/test-arkts/memo/remember.test.ts @@ -14,7 +14,7 @@ */ // TODO: the real chai exports 'assert', but 'assert' is still a keyword in ArkTS -import { Assert, suite, test } from "../harness" +import { Assert, suite, test } from "@koalaui/harness" import { asArray, float64, int32 } from "@koalaui/common" import { GlobalStateManager, diff --git a/incremental/runtime/test-arkts/memo/repeat.test.ts b/incremental/runtime/test-arkts/memo/repeat.test.ts index a7a07cb51..4ecf99e58 100644 --- a/incremental/runtime/test-arkts/memo/repeat.test.ts +++ b/incremental/runtime/test-arkts/memo/repeat.test.ts @@ -14,7 +14,7 @@ */ // TODO: the real chai exports 'assert', but 'assert' is still a keyword in ArkTS -import { Assert, suite, test } from "../harness" +import { Assert, suite, test } from "@koalaui/harness" import { asArray, int32, KoalaCallsiteKey } from "@koalaui/common" import { GlobalStateManager, diff --git a/incremental/runtime/test-arkts/states/Journal.test.ts b/incremental/runtime/test-arkts/states/Journal.test.ts index 90c1abcd5..b9aba4053 100644 --- a/incremental/runtime/test-arkts/states/Journal.test.ts +++ b/incremental/runtime/test-arkts/states/Journal.test.ts @@ -14,23 +14,23 @@ */ // TODO: the real chai exports 'assert', but 'assert' is still a keyword in ArkTS -import { Assert, suite, test } from "../harness" +import { Assert, suite, test } from "@koalaui/harness" import { Changes, Journal } from "../../src/states/Journal" export const FileName = "states/Journal.test" function assertChange(changes: Changes | undefined, state: Object, expected: Value) { const change = changes?.getChange(state) - Assert.isDefined(change) + Assert.isDefined(change) Assert.equal(change?.value, expected) } function assertNoChange(changes: Changes | undefined, state: Object) { const change = changes?.getChange(state) - Assert.isUndefined(change) + Assert.isUndefined(change) } function assertNoChanges(journal: Journal) { - Assert.isUndefined(journal.getChanges()) + Assert.isUndefined(journal.getChanges()) } suite("Journal tests", () => { @@ -93,7 +93,7 @@ suite("Journal tests", () => { journal.addChange(state, "value3") journal.addChange(state, "value4") const changes = journal.getChanges() - Assert.isDefined(changes) + Assert.isDefined(changes) changes?.clear() assertChange(journal, state, "value4") assertNoChanges(journal) diff --git a/incremental/runtime/test-arkts/states/State.test.ts b/incremental/runtime/test-arkts/states/State.test.ts index ee1c54c19..a738b2590 100644 --- a/incremental/runtime/test-arkts/states/State.test.ts +++ b/incremental/runtime/test-arkts/states/State.test.ts @@ -14,7 +14,7 @@ */ // TODO: the real chai exports 'assert', but 'assert' is still a keyword in ArkTS -import { Assert, suite, test } from "../harness" +import { Assert, suite, test } from "@koalaui/harness" import { KoalaCallsiteKey, float64, int32 } from "@koalaui/common" import { IncrementalNode, State, StateContext, TestNode, testUpdate, ValueTracker } from "../../src" import { createStateManager } from "../../src/states/State" @@ -102,11 +102,11 @@ suite("State", () => { let manager = createStateManager() const state = manager.namedState("named", (): float64 => 200) Assert.equal(state, manager.stateBy("named")!) - Assert.isDefined>(manager.stateBy("named")) + Assert.isDefined(manager.stateBy("named")) state.dispose() - Assert.isUndefined>(manager.stateBy("named")) + Assert.isUndefined(manager.stateBy("named")) state.dispose() - Assert.isUndefined>(manager.stateBy("named")) + Assert.isUndefined(manager.stateBy("named")) }) test("managed named state is not modified immediately", () => { let manager = createStateManager() @@ -400,8 +400,8 @@ suite("State", () => { computing.push("compute:inner:true") return "true" }, (old: string | undefined) => { - Assert.isUndefined>(context.stateBy("false")) - Assert.isDefined>(context.stateBy("true")) + Assert.isUndefined(context.stateBy("false")) + Assert.isDefined(context.stateBy("true")) Assert.isTrue(context.valueBy("true")) Assert.equal(old, "true") computing.push("cleanup:inner:true") @@ -414,8 +414,8 @@ suite("State", () => { computing.push("compute:inner:false") return "false" }, (old: string | undefined) => { - Assert.isUndefined>(context.stateBy("true")) - Assert.isDefined>(context.stateBy("false")) + Assert.isUndefined(context.stateBy("true")) + Assert.isDefined(context.stateBy("false")) Assert.isFalse(context.valueBy("false")) Assert.equal(old, "false") computing.push("cleanup:inner:false") @@ -736,8 +736,8 @@ suite("State", () => { const computable = manager.computableState((context: StateContext) => { const state = context.namedState("global", (): int32 => 0, true) Assert.equal(state, manager.stateBy("global")!) - Assert.isDefined>(manager.stateBy("global", true)) - Assert.isUndefined>(manager.stateBy("global", false)) + Assert.isDefined(manager.stateBy("global", true)) + Assert.isUndefined(manager.stateBy("global", false)) return state }) const globalState = computable.value @@ -757,16 +757,16 @@ suite("State", () => { const globalState = manager.namedState("global", (): float64 => Number.MAX_SAFE_INTEGER + 1) const computable = manager.computableState((context: StateContext) => { Assert.equal(globalState, manager.stateBy("global")!) - Assert.isDefined>(manager.stateBy("global", true)) - Assert.isUndefined>(manager.stateBy("global", false)) + Assert.isDefined(manager.stateBy("global", true)) + Assert.isUndefined(manager.stateBy("global", false)) const state = context.namedState("local", (): float64 => 0, false) Assert.equal(state, manager.stateBy("local")!) - Assert.isUndefined>(manager.stateBy("local", true)) - Assert.isDefined>(manager.stateBy("local", false)) + Assert.isUndefined(manager.stateBy("local", true)) + Assert.isDefined(manager.stateBy("local", false)) return state }) const localState = computable.value - Assert.isUndefined>(manager.stateBy("local")) + Assert.isUndefined(manager.stateBy("local")) Assert.equal("LocalState(local)=0", localState.toString()) localState.value = 1 Assert.equal(testUpdate(false, manager), 1) @@ -1566,7 +1566,7 @@ suite("ArrayState", () => { Assert.equal(result.value, "<= =>") assertStringsAndCleanup(computing, "outer ; center ; inner") // compute state only when snapshot updated - Assert.isUndefined(array.pop()) + Assert.isUndefined(array.pop()) Assert.equal(testUpdate(false, manager), 0) Assert.equal(result.value, "<= =>") Assert.isEmpty(computing) @@ -1700,7 +1700,7 @@ suite("ArrayState", () => { Assert.equal(result.value, "<= =>") assertStringsAndCleanup(computing, "outer ; center ; inner") // compute state only when snapshot updated - Assert.isUndefined(array.shift()) + Assert.isUndefined(array.shift()) Assert.equal(testUpdate(false, manager), 0) Assert.equal(result.value, "<= =>") Assert.isEmpty(computing) diff --git a/incremental/runtime/test-arkts/states/state_basics.test.ts b/incremental/runtime/test-arkts/states/state_basics.test.ts index d1a1d1dde..e068d0ac1 100644 --- a/incremental/runtime/test-arkts/states/state_basics.test.ts +++ b/incremental/runtime/test-arkts/states/state_basics.test.ts @@ -14,7 +14,7 @@ */ // TODO: the real chai exports 'assert', but 'assert' is still a keyword in ArkTS -import { Assert, suite, test } from "../harness" +import { Assert, suite, test } from "@koalaui/harness" import { asArray, int32, uint32 } from "@koalaui/common" import { GlobalStateManager, diff --git a/incremental/runtime/test-arkts/tests.ts b/incremental/runtime/test-arkts/tests.ts index e865029d1..9f8aad799 100644 --- a/incremental/runtime/test-arkts/tests.ts +++ b/incremental/runtime/test-arkts/tests.ts @@ -13,7 +13,7 @@ * limitations under the License. */ -import { suite } from "./harness" +import { suite } from "@koalaui/harness" import { FileName as Easing } from "./animation/Easing.test" import { FileName as MarkableQueue } from "./common/MarkableQueue.test" import { FileName as bind } from "./memo/bind.test" diff --git a/incremental/runtime/test-arkts/tree/TreeNode.test.ts b/incremental/runtime/test-arkts/tree/TreeNode.test.ts index fa836bfc6..7eee0e511 100644 --- a/incremental/runtime/test-arkts/tree/TreeNode.test.ts +++ b/incremental/runtime/test-arkts/tree/TreeNode.test.ts @@ -14,7 +14,7 @@ */ // TODO: the real chai exports 'assert', but 'assert' is still a keyword in ArkTS -import { Assert, suite, test } from "../harness" +import { Assert, suite, test } from "@koalaui/harness" import { float64, int32, uint32 } from "@koalaui/common" import { TreeNode } from "../../src/tree/TreeNode" export const FileName = "tree/TreeNode.test" @@ -42,7 +42,7 @@ function assertContent(node: TreeNode, content: string) { } function assertRoot(node: TreeNode) { - Assert.isUndefined(node.parent) + Assert.isUndefined(node.parent) Assert.equal(node.depth, 0) Assert.equal(node.index, -1) } @@ -52,12 +52,12 @@ function assertLeaf(node: TreeNode) { } function assertNoChildAt(parent: TreeNode, index: int32) { - Assert.isUndefined(parent.childAt(index)) + Assert.isUndefined(parent.childAt(index)) } function assertChildAt(parent: TreeNode, index: int32): TreeNode { let child = parent.childAt(index) - Assert.isDefined(child) + Assert.isDefined(child) Assert.equal(child?.parent, parent) Assert.equal(child?.index, index) return child! @@ -238,7 +238,7 @@ suite("TreeNode", () => { " first\n" + " third") let first = root.removeChildAt(1) - Assert.isDefined(first) + Assert.isDefined(first) assertRoot(first!) assertContent(first!, "first") root.appendChild(first!) diff --git a/incremental/runtime/test-arkts/tree/TreePath.test.ts b/incremental/runtime/test-arkts/tree/TreePath.test.ts index 2b760081e..a2b0ec406 100644 --- a/incremental/runtime/test-arkts/tree/TreePath.test.ts +++ b/incremental/runtime/test-arkts/tree/TreePath.test.ts @@ -14,7 +14,7 @@ */ // TODO: the real chai exports 'assert', but 'assert' is still a keyword in ArkTS -import { Assert, suite, test } from "../harness" +import { Assert, suite, test } from "@koalaui/harness" import { TreePath } from "../../src/tree/TreePath" export const FileName = "tree/TreePath.test" -- Gitee From 0b9c1ad7f681d2c24816e94be2d3cb050dc9a816 Mon Sep 17 00:00:00 2001 From: Sergey Malenkov Date: Tue, 4 Feb 2025 18:32:48 +0300 Subject: [PATCH 04/15] newline at end of file Signed-off-by: Sergey Malenkov --- incremental/harness/.gitlab-ci.yml | 2 +- incremental/harness/arktsconfig.json | 2 +- incremental/harness/package.json | 2 +- incremental/harness/tsconfig-ohos.json | 2 +- incremental/harness/tsconfig.json | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/incremental/harness/.gitlab-ci.yml b/incremental/harness/.gitlab-ci.yml index 8c4a4e2cb..c3c91a63e 100644 --- a/incremental/harness/.gitlab-ci.yml +++ b/incremental/harness/.gitlab-ci.yml @@ -67,4 +67,4 @@ publish harness: variables: PACKAGE_DIR: incremental/harness dependencies: - - build harness \ No newline at end of file + - build harness diff --git a/incremental/harness/arktsconfig.json b/incremental/harness/arktsconfig.json index 5a3a24910..f94282f31 100644 --- a/incremental/harness/arktsconfig.json +++ b/incremental/harness/arktsconfig.json @@ -14,4 +14,4 @@ { "path": "../common" }, { "path": "../compat" } ] -} \ No newline at end of file +} diff --git a/incremental/harness/package.json b/incremental/harness/package.json index b2950d4c9..907389be0 100644 --- a/incremental/harness/package.json +++ b/incremental/harness/package.json @@ -51,4 +51,4 @@ "rimraf": "^3.0.2", "source-map-support": "^0.5.21" } -} \ No newline at end of file +} diff --git a/incremental/harness/tsconfig-ohos.json b/incremental/harness/tsconfig-ohos.json index 43f1e0e46..128c234b6 100644 --- a/incremental/harness/tsconfig-ohos.json +++ b/incremental/harness/tsconfig-ohos.json @@ -15,4 +15,4 @@ "./src/index.ts", "./src/ohos/**/*" ] -} \ No newline at end of file +} diff --git a/incremental/harness/tsconfig.json b/incremental/harness/tsconfig.json index eda1e80d3..5d83ff3a5 100644 --- a/incremental/harness/tsconfig.json +++ b/incremental/harness/tsconfig.json @@ -15,4 +15,4 @@ "./src/index.ts", "./src/typescript/**/*" ] -} \ No newline at end of file +} -- Gitee From d08c498f6b39a1b9b51bb68d3b8de3233335ecc5 Mon Sep 17 00:00:00 2001 From: Sergey Malenkov Date: Tue, 4 Feb 2025 19:21:49 +0300 Subject: [PATCH 05/15] temporary fix for runtime:unmemoize Signed-off-by: Sergey Malenkov --- incremental/runtime/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/incremental/runtime/package.json b/incremental/runtime/package.json index 9db5a7009..f58c8f7bb 100644 --- a/incremental/runtime/package.json +++ b/incremental/runtime/package.json @@ -19,7 +19,7 @@ "panda:sdk:install": "cd ../tools/panda && npm run panda:sdk:install", "arkts:test:run": "bash ../tools/panda/arkts/ark build/runtime-tests.abc --ark-boot-files ../compat/build/compat.abc:../common/build/common.abc:../harness/build/harness.abc --ark-entry-point @koalaui.runtime-tests.test-arkts.tests.ETSGLOBAL::main", "arkts:test": "npm run panda:sdk:install && npm run build:compat && npm run build:common && npm run build:harness && npm run build:runtime:with:tests && npm run arkts:test:run", - "unmemoize": "mkdir -p build/unmemoized/abc && mkdir -p build/test/lib && ets-tsc -b arktsconfig-unmemoize.json && mkdir -p build/test/lib", + "unmemoize": "npm run compile:all --prefix ../harness && mkdir -p build/unmemoized/abc && mkdir -p build/test/lib && ets-tsc -b arktsconfig-unmemoize.json && mkdir -p build/test/lib", "compile:arkts": "npm run unmemoize && ../tools/panda/arkts/arktsc --arktsconfig arktsconfig-run-unmemoized.json", "build:compat": "npm run build:compat --prefix ../compat", "build:compat:inc": "npm run build:compat:inc --prefix ../compat", -- Gitee From 4e4825031bab12afffa15a60b871711bfcf05321 Mon Sep 17 00:00:00 2001 From: Sergey Malenkov Date: Wed, 5 Feb 2025 13:09:30 +0300 Subject: [PATCH 06/15] remove unused cast Signed-off-by: Sergey Malenkov --- incremental/harness/src/arkts/mocha.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/incremental/harness/src/arkts/mocha.ts b/incremental/harness/src/arkts/mocha.ts index a7e575ed9..88fe84a26 100644 --- a/incremental/harness/src/arkts/mocha.ts +++ b/incremental/harness/src/arkts/mocha.ts @@ -21,7 +21,7 @@ function getCurrentSuite(): Suite | undefined { const index = stack.length - 1 if (index < 0) return undefined const test = stack[index] - if (test instanceof Suite) return test as Suite + if (test instanceof Suite) return test throw new Error("test is already running") } -- Gitee From 2a7628254f36d1a743ada8ad53a66c2fb53ec926 Mon Sep 17 00:00:00 2001 From: Sergey Malenkov Date: Wed, 5 Feb 2025 13:24:01 +0300 Subject: [PATCH 07/15] unmemoize tests separately Signed-off-by: Sergey Malenkov --- incremental/runtime/package.json | 7 ++--- ...son => tsconfig-unmemoize-with-tests.json} | 20 +++++++------- incremental/runtime/tsconfig-unmemoize.json | 26 ++++++++++++------- 3 files changed, 30 insertions(+), 23 deletions(-) rename incremental/runtime/{arktsconfig-unmemoize.json => tsconfig-unmemoize-with-tests.json} (47%) diff --git a/incremental/runtime/package.json b/incremental/runtime/package.json index f58c8f7bb..2dda15f74 100644 --- a/incremental/runtime/package.json +++ b/incremental/runtime/package.json @@ -11,7 +11,6 @@ ], "scripts": { "compile": "ets-tsc -p .", - "compile:unmemoize": "ets-tsc -p tsconfig-unmemoize.json", "compile:test": "ets-tsc -b tsconfig-test.json", "clean": "rimraf build", "test": "mocha", @@ -19,17 +18,19 @@ "panda:sdk:install": "cd ../tools/panda && npm run panda:sdk:install", "arkts:test:run": "bash ../tools/panda/arkts/ark build/runtime-tests.abc --ark-boot-files ../compat/build/compat.abc:../common/build/common.abc:../harness/build/harness.abc --ark-entry-point @koalaui.runtime-tests.test-arkts.tests.ETSGLOBAL::main", "arkts:test": "npm run panda:sdk:install && npm run build:compat && npm run build:common && npm run build:harness && npm run build:runtime:with:tests && npm run arkts:test:run", - "unmemoize": "npm run compile:all --prefix ../harness && mkdir -p build/unmemoized/abc && mkdir -p build/test/lib && ets-tsc -b arktsconfig-unmemoize.json && mkdir -p build/test/lib", + "unmemoize": "mkdir -p build/unmemoized/abc && mkdir -p build/test/lib && ets-tsc -b tsconfig-unmemoize.json && mkdir -p build/test/lib", + "unmemoize:with:tests": "npm run compile:all --prefix ../harness && mkdir -p build/unmemoized/abc && mkdir -p build/test/lib && ets-tsc -b tsconfig-unmemoize-with-tests.json && mkdir -p build/test/lib", "compile:arkts": "npm run unmemoize && ../tools/panda/arkts/arktsc --arktsconfig arktsconfig-run-unmemoized.json", "build:compat": "npm run build:compat --prefix ../compat", "build:compat:inc": "npm run build:compat:inc --prefix ../compat", "build:common": "npm run build:common --prefix ../common", "build:common:inc": "npm run build:common:inc --prefix ../common", "build:harness": "npm run build:harness --prefix ../harness", + "build:harness:inc": "npm run build:harness:inc --prefix ../harness", "build:runtime": "npm run build:runtime:inc", "build:runtime:inc": "npm run unmemoize && fast-arktsc --input-files ./arktsconfig-run-unmemoized.json --output-dir ./build --compiler ../tools/panda/arkts/arktsc --link-name runtime && ninja ${NINJA_OPTIONS} -f build/build.ninja", "build:runtime:inc:capi": "npm run unmemoize && fast-arktsc --input-files ./arktsconfig-run-unmemoized.json --output-dir ./build --compiler ../tools/panda/arkts/arktsc-capi --link-name runtime --file-option && PANDA_SDK_PATH=../tools/panda/node_modules/@panda/sdk ninja ${NINJA_OPTIONS} -f build/build.ninja", - "build:runtime:with:tests": "npm run unmemoize && fast-arktsc --input-files ./arktsconfig-test-unmemoized.json --output-dir ./build --compiler ../tools/panda/arkts/arktsc --link-name runtime-tests && ninja ${NINJA_OPTIONS} -f build/build.ninja", + "build:runtime:with:tests": "npm run unmemoize:with:tests && fast-arktsc --input-files ./arktsconfig-test-unmemoized.json --output-dir ./build --compiler ../tools/panda/arkts/arktsc --link-name runtime-tests && ninja ${NINJA_OPTIONS} -f build/build.ninja", "build:incremental:components": "npm run build:compat && npm run build:common && npm run build:runtime", "build:incremental:components:inc": "npm run build:compat:inc && npm run build:common:inc && npm run build:runtime:inc", "link:incremental": "../tools/panda/arkts/arklink --output build/incremental.abc -- ../compat/build/compat.abc ../common/build/common.abc build/runtime.abc", diff --git a/incremental/runtime/arktsconfig-unmemoize.json b/incremental/runtime/tsconfig-unmemoize-with-tests.json similarity index 47% rename from incremental/runtime/arktsconfig-unmemoize.json rename to incremental/runtime/tsconfig-unmemoize-with-tests.json index d55f38fba..8a63c18b4 100644 --- a/incremental/runtime/arktsconfig-unmemoize.json +++ b/incremental/runtime/tsconfig-unmemoize-with-tests.json @@ -3,21 +3,20 @@ "compilerOptions": { "outDir": "build/junk", "rootDir": ".", - "rootDirs": ["src", "test", "test-arkts"], "module": "CommonJS", "plugins": [ { - "transform": "@koalaui/compiler-plugin/build/lib/src/koala-transformer.js", - "contextImport": "../../src/internals", - "trace": false, - "only_unmemoize": true, - "unmemoizeDir": "./build/unmemoized" + "transform": "@koalaui/compiler-plugin/build/lib/src/koala-transformer.js", + "contextImport": "../../src/internals", + "unmemoizeDir": "./build/unmemoized", + "only_unmemoize": true, + "trace": false } ], "paths": { - "@koalaui/common": ["../../common/src"], - "@koalaui/compat": ["../../compat/src/arkts"], - "@koalaui/harness": ["../../harness/src/arkts"] + "@koalaui/compat": [ "../../compat/src/arkts" ], + "@koalaui/common": [ "../../common/src" ], + "@koalaui/harness": [ "../../harness/src/arkts" ] } }, "include": [ @@ -27,7 +26,8 @@ ], "references": [ { "path": "../compiler-plugin" }, + { "path": "../compat" }, { "path": "../common" }, - { "path": "../compat" } + { "path": "../harness" } ] } diff --git a/incremental/runtime/tsconfig-unmemoize.json b/incremental/runtime/tsconfig-unmemoize.json index 3851cd79e..7c71cf541 100644 --- a/incremental/runtime/tsconfig-unmemoize.json +++ b/incremental/runtime/tsconfig-unmemoize.json @@ -3,21 +3,27 @@ "compilerOptions": { "outDir": "build/junk", "rootDir": ".", - "rootDirs": ["src", "test"], "module": "CommonJS", "plugins": [ - { "transform": "@koalaui/compiler-plugin/build/lib/src/koala-transformer.js", - "contextImport": "../../src/internals", - "trace": false, - "only_unmemoize": true, - "unmemoizeDir": "build/unmemoized" + { + "transform": "@koalaui/compiler-plugin/build/lib/src/koala-transformer.js", + "contextImport": "../../src/internals", + "unmemoizeDir": "./build/unmemoized", + "only_unmemoize": true, + "trace": false } - ] + ], + "paths": { + "@koalaui/compat": [ "../../compat/src/arkts" ], + "@koalaui/common": [ "../../common/src" ] + } }, - "include": ["src/**/*"], + "include": [ + "src/**/*.ts" + ], "references": [ { "path": "../compiler-plugin" }, - { "path": "../common" }, - { "path": "../compat" } + { "path": "../compat" }, + { "path": "../common" } ] } -- Gitee From f4b8779fb68f3f91da78cc7ee52cfdec0be2fcdf Mon Sep 17 00:00:00 2001 From: Sergey Malenkov Date: Wed, 5 Feb 2025 17:23:12 +0300 Subject: [PATCH 08/15] simplify tsconfigs Signed-off-by: Sergey Malenkov --- incremental/runtime/.nycrc.json | 8 ++---- .../tsconfig-unmemoize-with-tests.json | 28 ++----------------- incremental/runtime/tsconfig-unmemoize.json | 13 ++------- ...fig-test.json => tsconfig-with-tests.json} | 5 ++-- incremental/runtime/tsconfig.json | 12 ++++---- 5 files changed, 18 insertions(+), 48 deletions(-) rename incremental/runtime/{tsconfig-test.json => tsconfig-with-tests.json} (47%) diff --git a/incremental/runtime/.nycrc.json b/incremental/runtime/.nycrc.json index 79eaf4d3c..b1bfe80ef 100644 --- a/incremental/runtime/.nycrc.json +++ b/incremental/runtime/.nycrc.json @@ -1,8 +1,6 @@ { - "extends": "@istanbuljs/nyc-config-typescript", + "extends": "tsconfig-with-tests.json", "reporter": ["html", "text-summary"], - "include": "src/**/*.ts", - "extension": [".ts"], - "report-dir": "./build/coverage/node", - "temp-dir": "./build/nyc" + "report-dir": "./build/coverage/report", + "temp-dir": "./build/coverage/temp" } diff --git a/incremental/runtime/tsconfig-unmemoize-with-tests.json b/incremental/runtime/tsconfig-unmemoize-with-tests.json index 8a63c18b4..4fdea441b 100644 --- a/incremental/runtime/tsconfig-unmemoize-with-tests.json +++ b/incremental/runtime/tsconfig-unmemoize-with-tests.json @@ -1,32 +1,8 @@ { - "extends": "@koalaui/build-common/tsconfig.json", - "compilerOptions": { - "outDir": "build/junk", - "rootDir": ".", - "module": "CommonJS", - "plugins": [ - { - "transform": "@koalaui/compiler-plugin/build/lib/src/koala-transformer.js", - "contextImport": "../../src/internals", - "unmemoizeDir": "./build/unmemoized", - "only_unmemoize": true, - "trace": false - } - ], - "paths": { - "@koalaui/compat": [ "../../compat/src/arkts" ], - "@koalaui/common": [ "../../common/src" ], - "@koalaui/harness": [ "../../harness/src/arkts" ] - } - }, - "include": [ - "src/**/*.ts", - "test/**/*.ts", - "test-arkts/**/*.ts" - ], + "extends": "./tsconfig-unmemoize.json", + "include": [ "src/**/*.ts", "test/**/*.ts", "test-arkts/**/*.ts" ], "references": [ { "path": "../compiler-plugin" }, - { "path": "../compat" }, { "path": "../common" }, { "path": "../harness" } ] diff --git a/incremental/runtime/tsconfig-unmemoize.json b/incremental/runtime/tsconfig-unmemoize.json index 7c71cf541..27497fbb2 100644 --- a/incremental/runtime/tsconfig-unmemoize.json +++ b/incremental/runtime/tsconfig-unmemoize.json @@ -1,7 +1,7 @@ { "extends": "@koalaui/build-common/tsconfig.json", "compilerOptions": { - "outDir": "build/junk", + "outDir": "build/unmemoized/lib", "rootDir": ".", "module": "CommonJS", "plugins": [ @@ -12,18 +12,11 @@ "only_unmemoize": true, "trace": false } - ], - "paths": { - "@koalaui/compat": [ "../../compat/src/arkts" ], - "@koalaui/common": [ "../../common/src" ] - } + ] }, - "include": [ - "src/**/*.ts" - ], + "include": [ "src/**/*.ts" ], "references": [ { "path": "../compiler-plugin" }, - { "path": "../compat" }, { "path": "../common" } ] } diff --git a/incremental/runtime/tsconfig-test.json b/incremental/runtime/tsconfig-with-tests.json similarity index 47% rename from incremental/runtime/tsconfig-test.json rename to incremental/runtime/tsconfig-with-tests.json index 8aca3becf..e347105d2 100644 --- a/incremental/runtime/tsconfig-test.json +++ b/incremental/runtime/tsconfig-with-tests.json @@ -1,8 +1,9 @@ { "extends": "./tsconfig.json", - "include": ["src/**/*", "test/**/*"], + "include": [ "src/**/*.ts", "test/**/*.ts" ], "references": [ { "path": "../compiler-plugin" }, - { "path": "../common" } + { "path": "../common" }, + { "path": "../harness" } ] } diff --git a/incremental/runtime/tsconfig.json b/incremental/runtime/tsconfig.json index 1cbc06787..e7b1a4b00 100644 --- a/incremental/runtime/tsconfig.json +++ b/incremental/runtime/tsconfig.json @@ -3,16 +3,18 @@ "compilerOptions": { "outDir": "build/lib", "rootDir": ".", - "rootDirs": ["src", "test"], "module": "CommonJS", "plugins": [ - { "transform": "@koalaui/compiler-plugin/build/lib/src/koala-transformer.js", "contextImport": "../../src/internals", "trace": false } + { + "transform": "@koalaui/compiler-plugin/build/lib/src/koala-transformer.js", + "contextImport": "../../src/internals", + "trace": false + } ] }, - "include": ["src/**/*"], + "include": [ "src/**/*.ts" ], "references": [ { "path": "../compiler-plugin" }, - { "path": "../common" }, - { "path": "../compat" } + { "path": "../common" } ] } -- Gitee From 38945c5d4fb0af932420c03deb8260bc9f7bd654 Mon Sep 17 00:00:00 2001 From: Sergey Malenkov Date: Wed, 5 Feb 2025 18:04:33 +0300 Subject: [PATCH 09/15] prepare for auto-generation Signed-off-by: Sergey Malenkov --- .../test-arkts/animation/Easing.test.ts | 2 +- .../test-arkts/common/MarkableQueue.test.ts | 2 +- .../runtime/test-arkts/memo/bind.test.ts | 2 +- .../test-arkts/memo/changeListener.test.ts | 2 +- .../test-arkts/memo/contextLocal.test.ts | 2 +- .../runtime/test-arkts/memo/remember.test.ts | 2 +- .../runtime/test-arkts/memo/repeat.test.ts | 2 +- .../runtime/test-arkts/states/Journal.test.ts | 2 +- .../runtime/test-arkts/states/State.test.ts | 2 +- .../test-arkts/states/state_basics.test.ts | 2 +- incremental/runtime/test-arkts/tests.ts | 39 +++++++------------ .../runtime/test-arkts/tree/TreeNode.test.ts | 2 +- .../runtime/test-arkts/tree/TreePath.test.ts | 2 +- 13 files changed, 26 insertions(+), 37 deletions(-) diff --git a/incremental/runtime/test-arkts/animation/Easing.test.ts b/incremental/runtime/test-arkts/animation/Easing.test.ts index dacb7cf49..7658ee0db 100644 --- a/incremental/runtime/test-arkts/animation/Easing.test.ts +++ b/incremental/runtime/test-arkts/animation/Easing.test.ts @@ -17,7 +17,6 @@ import { Assert, suite, test } from "@koalaui/harness" import { float64, int32 } from "@koalaui/common" import { Easing, EasingCurve, EasingStepJump } from "../../src/animation/Easing" -export const FileName = "animation/Easing.test" function assertEasing(easing: EasingCurve, ...expected: int32[]) { const last = expected.length - 1 @@ -64,3 +63,4 @@ suite("Easing", () => { test("10 steps with EasingStepJump.End", () => { assertEasing(Easing.steps(10, EasingStepJump.End), 0, 10, 20, 30, 40, 50, 60, 70, 80, 90) }) test("9 steps with EasingStepJump.Both", () => { assertEasing(Easing.steps(9, EasingStepJump.Both), 10, 20, 30, 40, 50, 60, 70, 80, 90) }) }) +export const __ARKTEST__ = "animation/Easing.test" diff --git a/incremental/runtime/test-arkts/common/MarkableQueue.test.ts b/incremental/runtime/test-arkts/common/MarkableQueue.test.ts index fa519859f..ad7177e86 100644 --- a/incremental/runtime/test-arkts/common/MarkableQueue.test.ts +++ b/incremental/runtime/test-arkts/common/MarkableQueue.test.ts @@ -16,7 +16,6 @@ // TODO: the real chai exports 'assert', but 'assert' is still a keyword in ArkTS import { Assert, suite, test } from "@koalaui/harness" import { MarkableQueue, markableQueue } from "@koalaui/common" -export const FileName = "common/MarkableQueue.test" const collector = new Array() @@ -107,3 +106,4 @@ suite("MarkableQueue reversed tests", () => { testQueue(queue, Array.of()) // called only once }) }) +export const __ARKTEST__ = "common/MarkableQueue.test" diff --git a/incremental/runtime/test-arkts/memo/bind.test.ts b/incremental/runtime/test-arkts/memo/bind.test.ts index 1e443e2e2..dc5017664 100644 --- a/incremental/runtime/test-arkts/memo/bind.test.ts +++ b/incremental/runtime/test-arkts/memo/bind.test.ts @@ -23,7 +23,6 @@ import { memoBind, testTick, } from "../../src" -export const FileName = "memo/bind.test" const collector = new Array() @@ -50,3 +49,4 @@ suite("memo tests", () => { testExpected(root, "red") }) }) +export const __ARKTEST__ = "memo/bind.test" diff --git a/incremental/runtime/test-arkts/memo/changeListener.test.ts b/incremental/runtime/test-arkts/memo/changeListener.test.ts index 1f9081e14..493358a9f 100644 --- a/incremental/runtime/test-arkts/memo/changeListener.test.ts +++ b/incremental/runtime/test-arkts/memo/changeListener.test.ts @@ -24,7 +24,6 @@ import { mutableState, testTick, } from "../../src" -export const FileName = "memo/changeListeners.test" const collector = new Array() @@ -64,3 +63,4 @@ suite("changeListener tests", () => { test("OnChange", () => { testChange((value: string) => { OnChange(value, (change: string) => { collector.push(change) }) }) }) }) +export const __ARKTEST__ = "memo/changeListeners.test" diff --git a/incremental/runtime/test-arkts/memo/contextLocal.test.ts b/incremental/runtime/test-arkts/memo/contextLocal.test.ts index 86eb28416..b27f09358 100644 --- a/incremental/runtime/test-arkts/memo/contextLocal.test.ts +++ b/incremental/runtime/test-arkts/memo/contextLocal.test.ts @@ -24,7 +24,6 @@ import { mutableState, testTick, } from "../../src" -export const FileName = "memo/contextLocal.test" const collector = new Array() @@ -65,3 +64,4 @@ suite("contextLocal tests", () => { testExpected(root, "ui:third", "state:third") }) }) +export const __ARKTEST__ = "memo/contextLocal.test" diff --git a/incremental/runtime/test-arkts/memo/remember.test.ts b/incremental/runtime/test-arkts/memo/remember.test.ts index 98829e95d..bbdbf96ba 100644 --- a/incremental/runtime/test-arkts/memo/remember.test.ts +++ b/incremental/runtime/test-arkts/memo/remember.test.ts @@ -28,7 +28,6 @@ import { rememberMutableState, testTick, } from "../../src" -export const FileName = "memo/remember.test" const collector = new Array() @@ -164,3 +163,4 @@ suite("remember tests", () => { testExpected(root, "global=2", "local=0") }) }) +export const __ARKTEST__ = "memo/remember.test" diff --git a/incremental/runtime/test-arkts/memo/repeat.test.ts b/incremental/runtime/test-arkts/memo/repeat.test.ts index 4ecf99e58..bd567d258 100644 --- a/incremental/runtime/test-arkts/memo/repeat.test.ts +++ b/incremental/runtime/test-arkts/memo/repeat.test.ts @@ -28,7 +28,6 @@ import { mutableState, testTick, } from "../../src" -export const FileName = "memo/repeat.test" // For tests we compute positional ids from strings. export function key(name: string): KoalaCallsiteKey { @@ -230,3 +229,4 @@ suite("repeat tests", () => { }) }) }) +export const __ARKTEST__ = "memo/repeat.test" diff --git a/incremental/runtime/test-arkts/states/Journal.test.ts b/incremental/runtime/test-arkts/states/Journal.test.ts index b9aba4053..e6ccf772a 100644 --- a/incremental/runtime/test-arkts/states/Journal.test.ts +++ b/incremental/runtime/test-arkts/states/Journal.test.ts @@ -16,7 +16,6 @@ // TODO: the real chai exports 'assert', but 'assert' is still a keyword in ArkTS import { Assert, suite, test } from "@koalaui/harness" import { Changes, Journal } from "../../src/states/Journal" -export const FileName = "states/Journal.test" function assertChange(changes: Changes | undefined, state: Object, expected: Value) { const change = changes?.getChange(state) @@ -99,3 +98,4 @@ suite("Journal tests", () => { assertNoChanges(journal) }) }) +export const __ARKTEST__ = "states/Journal.test" diff --git a/incremental/runtime/test-arkts/states/State.test.ts b/incremental/runtime/test-arkts/states/State.test.ts index a738b2590..21e6ec9bc 100644 --- a/incremental/runtime/test-arkts/states/State.test.ts +++ b/incremental/runtime/test-arkts/states/State.test.ts @@ -18,7 +18,6 @@ import { Assert, suite, test } from "@koalaui/harness" import { KoalaCallsiteKey, float64, int32 } from "@koalaui/common" import { IncrementalNode, State, StateContext, TestNode, testUpdate, ValueTracker } from "../../src" import { createStateManager } from "../../src/states/State" -export const FileName = "states/State.test" // For tests we compute positional ids from strings. export function key(name: string): KoalaCallsiteKey { @@ -1808,3 +1807,4 @@ suite("ArrayState", () => { Assert.isEmpty(computing) }) }) +export const __ARKTEST__ = "states/State.test" diff --git a/incremental/runtime/test-arkts/states/state_basics.test.ts b/incremental/runtime/test-arkts/states/state_basics.test.ts index e068d0ac1..713426c0e 100644 --- a/incremental/runtime/test-arkts/states/state_basics.test.ts +++ b/incremental/runtime/test-arkts/states/state_basics.test.ts @@ -24,7 +24,6 @@ import { testRoot, testTick, } from "../../src" -export const FileName = "states/state_basics.test" function assertResultArray(actual: Array, ...expected: T[]) { Assert.deepEqual(actual, asArray(expected)) @@ -376,3 +375,4 @@ suite("State management basics", () => { }) }) +export const __ARKTEST__ = "states/state_basics.test" diff --git a/incremental/runtime/test-arkts/tests.ts b/incremental/runtime/test-arkts/tests.ts index 9f8aad799..95172ddaa 100644 --- a/incremental/runtime/test-arkts/tests.ts +++ b/incremental/runtime/test-arkts/tests.ts @@ -14,30 +14,19 @@ */ import { suite } from "@koalaui/harness" -import { FileName as Easing } from "./animation/Easing.test" -import { FileName as MarkableQueue } from "./common/MarkableQueue.test" -import { FileName as bind } from "./memo/bind.test" -import { FileName as changeListener } from "./memo/changeListener.test" -import { FileName as contextLocal } from "./memo/contextLocal.test" -import { FileName as remember } from "./memo/remember.test" -import { FileName as repeat } from "./memo/repeat.test" -import { FileName as Journal } from "./states/Journal.test" -import { FileName as state_basics } from "./states/state_basics.test" -import { FileName as State } from "./states/State.test" -import { FileName as TreeNode } from "./tree/TreeNode.test" -import { FileName as TreePath } from "./tree/TreePath.test" +import { __ARKTEST__ as Easing } from "./animation/Easing.test" +import { __ARKTEST__ as MarkableQueue } from "./common/MarkableQueue.test" +import { __ARKTEST__ as bind } from "./memo/bind.test" +import { __ARKTEST__ as changeListener } from "./memo/changeListener.test" +import { __ARKTEST__ as contextLocal } from "./memo/contextLocal.test" +import { __ARKTEST__ as remember } from "./memo/remember.test" +import { __ARKTEST__ as repeat } from "./memo/repeat.test" +import { __ARKTEST__ as Journal } from "./states/Journal.test" +import { __ARKTEST__ as state_basics } from "./states/state_basics.test" +import { __ARKTEST__ as State } from "./states/State.test" +import { __ARKTEST__ as TreeNode } from "./tree/TreeNode.test" +import { __ARKTEST__ as TreePath } from "./tree/TreePath.test" -suite("harness", () => { - console.log("register file:", Easing) - console.log("register file:", MarkableQueue) - console.log("register file:", bind) - console.log("register file:", changeListener) - console.log("register file:", contextLocal) - console.log("register file:", remember) - console.log("register file:", repeat) - console.log("register file:", Journal) - // console.log("register file:", state_basics) - console.log("register file:", State) - console.log("register file:", TreeNode) - console.log("register file:", TreePath) +suite("runtime", () => { + Array.of(Easing, MarkableQueue, bind, changeListener, contextLocal, remember, repeat, Journal, /* state_basics, */State, TreeNode, TreePath) }) diff --git a/incremental/runtime/test-arkts/tree/TreeNode.test.ts b/incremental/runtime/test-arkts/tree/TreeNode.test.ts index 7eee0e511..f485acf01 100644 --- a/incremental/runtime/test-arkts/tree/TreeNode.test.ts +++ b/incremental/runtime/test-arkts/tree/TreeNode.test.ts @@ -17,7 +17,6 @@ import { Assert, suite, test } from "@koalaui/harness" import { float64, int32, uint32 } from "@koalaui/common" import { TreeNode } from "../../src/tree/TreeNode" -export const FileName = "tree/TreeNode.test" class StringNode extends TreeNode { readonly content: string @@ -352,3 +351,4 @@ suite("TreeNode", () => { Assert.equal(root.childrenCount, 0) }) }) +export const __ARKTEST__ = "tree/TreeNode.test" diff --git a/incremental/runtime/test-arkts/tree/TreePath.test.ts b/incremental/runtime/test-arkts/tree/TreePath.test.ts index a2b0ec406..62fb6b067 100644 --- a/incremental/runtime/test-arkts/tree/TreePath.test.ts +++ b/incremental/runtime/test-arkts/tree/TreePath.test.ts @@ -16,7 +16,6 @@ // TODO: the real chai exports 'assert', but 'assert' is still a keyword in ArkTS import { Assert, suite, test } from "@koalaui/harness" import { TreePath } from "../../src/tree/TreePath" -export const FileName = "tree/TreePath.test" suite("TreePath", () => { @@ -40,3 +39,4 @@ suite("TreePath", () => { test("siblings has the same parents", () => Assert.strictEqual(current.parent, sibling.parent)) }) +export const __ARKTEST__ = "tree/TreePath.test" -- Gitee From 7b4ca8253ef743376a5a3fe9f320e9e015f9ca2e Mon Sep 17 00:00:00 2001 From: Sergey Malenkov Date: Thu, 6 Feb 2025 14:44:26 +0300 Subject: [PATCH 10/15] rewrite test output Signed-off-by: Sergey Malenkov --- incremental/harness/src/arkts/mocha.ts | 99 +++++++++++--------------- 1 file changed, 41 insertions(+), 58 deletions(-) diff --git a/incremental/harness/src/arkts/mocha.ts b/incremental/harness/src/arkts/mocha.ts index 88fe84a26..d942ef06d 100644 --- a/incremental/harness/src/arkts/mocha.ts +++ b/incremental/harness/src/arkts/mocha.ts @@ -13,78 +13,61 @@ * limitations under the License. */ -import { int32 } from "@koalaui/common" - const stack = new Array() +const array = new Array() +let passedCount = 0 +let failedCount = 0 + +function addTest(test: Test, content?: () => void) { + array.push(test) + const length = stack.length + const parent = 0 < length ? stack[length - 1] : undefined + if (parent?.suite == false) throw new Error("test is already running") + try { + console.log(test.prefix + test.name) + stack.push(test) + content?.() + if (!test.suite) passedCount++ + } catch (error) { + if (!test.suite) failedCount++ + test.passed = false + console.log(error?.toString()) + } finally { + stack.pop() + if (parent) { + if (!test.passed) parent.passed = false + } else { + console.log("-".repeat(50)) + array.forEach(logFailedTest) + console.log("failed tests: " + failedCount) + console.log("passed tests: " + passedCount) + if (!test.passed) throw new Error("TEST FAILED") + } + } +} -function getCurrentSuite(): Suite | undefined { - const index = stack.length - 1 - if (index < 0) return undefined - const test = stack[index] - if (test instanceof Suite) return test - throw new Error("test is already running") +function logFailedTest(test: Test) { + if (!test.passed) console.log(test.prefix + "failed " + test.name) } class Test { - public error: Error | undefined = undefined readonly name: string - readonly content: () => void - constructor(name: string, content?: () => void) { + readonly suite: boolean + readonly prefix: string + public passed: boolean = true + constructor(name: string, suite: boolean) { this.name = name - this.content = () => { - const suite = getCurrentSuite() - try { - stack.push(this) - content?.() - } catch (error) { - this.error = error as Error - console.log(this.error!.toString()) - } finally { - stack.pop() - console.log(" ".repeat(stack.length) + this.toString()) - if (suite) suite.tests.push(this) - else if (!this.passed) throw new Error("TEST FAILED") - } - } - } - get passed(): boolean { - return this.error === undefined - } - toString(): string { - let result = this.passed ? "passed" : "failed" - return `${result} test: "${this.name}"` - } -} - -class Suite extends Test { - readonly tests = new Array() - constructor(name: string, content?: () => void) { - super(name, content) - } - private get failures(): int32 { - let failed = 0 - for (let i = 0; i < this.tests.length; i++) { - const test = this.tests[i] - if (!test.passed) failed++ - } - return failed - } - override get passed(): boolean { - return super.passed && this.failures == 0 - } - override toString(): string { - const failures = this.failures - const result = failures > 0 ? `failed tests '${failures}' in` : this.error ? "failed" : "passed" - return `${result} suite: "${this.name}"` + this.suite = suite + this.prefix = " ".repeat(stack.length) } } export function test(name: string, content?: () => void) { - new Test(name, content).content() + addTest(new Test(`test: "${name}"`, false), content) } export function suite(name: string, content?: () => void) { - new Suite(name, content).content() + addTest(new Test(`suite: "${name}"`, true), content) } export function suiteSetup(name: string, content?: () => void) { -- Gitee From eef6059f24c9b52e8906724492b358ff70af77a4 Mon Sep 17 00:00:00 2001 From: Sergey Malenkov Date: Thu, 6 Feb 2025 17:12:00 +0300 Subject: [PATCH 11/15] print stack trace if possible Signed-off-by: Sergey Malenkov --- incremental/harness/src/arkts/mocha.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/incremental/harness/src/arkts/mocha.ts b/incremental/harness/src/arkts/mocha.ts index d942ef06d..abd4b6e97 100644 --- a/incremental/harness/src/arkts/mocha.ts +++ b/incremental/harness/src/arkts/mocha.ts @@ -31,7 +31,7 @@ function addTest(test: Test, content?: () => void) { } catch (error) { if (!test.suite) failedCount++ test.passed = false - console.log(error?.toString()) + console.log(error instanceof Error ? error.stack : error) } finally { stack.pop() if (parent) { -- Gitee From 09f5b06210d99f547e186232bc7b096442b2a36f Mon Sep 17 00:00:00 2001 From: Sergey Malenkov Date: Thu, 6 Feb 2025 17:13:47 +0300 Subject: [PATCH 12/15] register harness for publishing Signed-off-by: Sergey Malenkov --- incremental/harness/package.json | 6 +++--- incremental/runtime/package.json | 4 ++-- tools/release.mjs | 2 ++ 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/incremental/harness/package.json b/incremental/harness/package.json index 907389be0..3994d5f8b 100644 --- a/incremental/harness/package.json +++ b/incremental/harness/package.json @@ -1,6 +1,6 @@ { "name": "@koalaui/harness", - "version": "1.4.4+devel", + "version": "1.4.6+devel", "description": "A harness library compatible with OHOS and ArkTS", "main": "build/src/index.js", "types": "build/src/index.d.ts", @@ -35,8 +35,8 @@ }, "keywords": [], "dependencies": { - "@koalaui/common": "1.4.4+devel", - "@koalaui/compat": "1.4.4+devel" + "@koalaui/common": "1.4.6+devel", + "@koalaui/compat": "1.4.6+devel" }, "devDependencies": { "@ohos/hypium": "1.0.6", diff --git a/incremental/runtime/package.json b/incremental/runtime/package.json index 2dda15f74..2ccf99148 100644 --- a/incremental/runtime/package.json +++ b/incremental/runtime/package.json @@ -41,10 +41,10 @@ "keywords": [], "dependencies": { "@koalaui/common": "1.4.7+devel", - "@koalaui/compat": "1.4.7+devel" + "@koalaui/compat": "1.4.7+devel", + "@koalaui/harness": "1.4.7+devel" }, "devDependencies": { - "@koalaui/harness": "1.4.4+devel", "@types/chai": "^4.3.1", "@types/mocha": "^9.1.0", "@typescript-eslint/eslint-plugin": "^5.20.0", diff --git a/tools/release.mjs b/tools/release.mjs index 1fe89b5b8..0904dfdf6 100644 --- a/tools/release.mjs +++ b/tools/release.mjs @@ -41,6 +41,7 @@ class Package { "@koalaui/build-common", "@koalaui/compat", "@koalaui/common", + "@koalaui/harness", "@koalaui/runtime", "@koalaui/compiler-plugin", "@koalaui/ets-plugin", @@ -55,6 +56,7 @@ const all_packages = [ new Package(path.join(EXTERNAL_HOME, "incremental/build-common")), new Package(path.join(EXTERNAL_HOME, "incremental/compat")), new Package(path.join(EXTERNAL_HOME, "incremental/common")), + new Package(path.join(EXTERNAL_HOME, "incremental/harness")), new Package(path.join(EXTERNAL_HOME, "incremental/runtime")), new Package(path.join(EXTERNAL_HOME, "incremental/compiler-plugin")), new Package(path.join(EXTERNAL_HOME, "arkoala/ets-plugin")), -- Gitee From a8ba9dcd1197ba2b33d05564928e49c9a1e7427d Mon Sep 17 00:00:00 2001 From: Sergey Malenkov Date: Thu, 6 Feb 2025 17:43:27 +0300 Subject: [PATCH 13/15] remove duplicated task from CI Signed-off-by: Sergey Malenkov --- incremental/harness/.gitlab-ci.yml | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/incremental/harness/.gitlab-ci.yml b/incremental/harness/.gitlab-ci.yml index c3c91a63e..d37c1c2a9 100644 --- a/incremental/harness/.gitlab-ci.yml +++ b/incremental/harness/.gitlab-ci.yml @@ -15,11 +15,6 @@ build harness: stage: build interruptible: true extends: .linux-vm-shell-task - before_script: - - !reference [.setup, script] - - cd incremental/harness - script: - - npm run compile:all needs: - install node modules (arkoala) - install node modules (arkoala-arkts) @@ -27,20 +22,6 @@ build harness: - install node modules (interop) - build compat - build common - artifacts: - expire_in: 2 days - paths: - - incremental/harness/build - -build harness: - stage: build - interruptible: true - extends: .linux-vm-shell-task - needs: - - install node modules (arkoala) - - install node modules (arkoala-arkts) - - install node modules (incremental) - - install node modules (interop) before_script: - !reference [.setup, script] - cd incremental/harness -- Gitee From 8fd8d5784f451ab76471b6541fb154ff890a8fa2 Mon Sep 17 00:00:00 2001 From: Sergey Malenkov Date: Thu, 6 Feb 2025 17:47:06 +0300 Subject: [PATCH 14/15] fix build task for common CI Signed-off-by: Sergey Malenkov --- incremental/common/.gitlab-ci.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/incremental/common/.gitlab-ci.yml b/incremental/common/.gitlab-ci.yml index a324029da..61128bf13 100644 --- a/incremental/common/.gitlab-ci.yml +++ b/incremental/common/.gitlab-ci.yml @@ -20,6 +20,7 @@ build common: - install node modules (arkoala-arkts) - install node modules (incremental) - install node modules (interop) + - build compat before_script: - !reference [.setup, script] - cd incremental/common -- Gitee From 9642992c33cae5eb39d017b60258154e2bacd9c2 Mon Sep 17 00:00:00 2001 From: Sergey Malenkov Date: Fri, 7 Feb 2025 12:09:41 +0300 Subject: [PATCH 15/15] fix version Signed-off-by: Sergey Malenkov --- incremental/harness/package.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/incremental/harness/package.json b/incremental/harness/package.json index 3994d5f8b..225781dd0 100644 --- a/incremental/harness/package.json +++ b/incremental/harness/package.json @@ -1,6 +1,6 @@ { "name": "@koalaui/harness", - "version": "1.4.6+devel", + "version": "1.4.7+devel", "description": "A harness library compatible with OHOS and ArkTS", "main": "build/src/index.js", "types": "build/src/index.d.ts", @@ -35,8 +35,8 @@ }, "keywords": [], "dependencies": { - "@koalaui/common": "1.4.6+devel", - "@koalaui/compat": "1.4.6+devel" + "@koalaui/common": "1.4.7+devel", + "@koalaui/compat": "1.4.7+devel" }, "devDependencies": { "@ohos/hypium": "1.0.6", -- Gitee