From 8a46eacda58a0c178ff84db4f8a62c92d5bb4686 Mon Sep 17 00:00:00 2001 From: kopnovanatalia Date: Thu, 7 Aug 2025 14:00:29 +0300 Subject: [PATCH] [ets-tests] environment parameters --- .../arkui/@ohos.arkui.component/index.ets | 1 + arkoala-arkts/arkui/sdk/Storage.ets | 67 ++++++++++++++ arkoala-arkts/arkui/src/Storage.ets | 90 +++++++++++++++++-- ets-tests/ets/TestHarness.ets | 7 +- ets-tests/ets/environment-tests/Pages.ets | 2 + .../pages/storage/EnvironmentTest.ets | 53 +++++++++++ .../environment-tests/suites/StorageTests.ets | 28 ++++-- .../suites/WatchDecoratorTest.ets | 4 +- ets-tests/ets/utils.ets | 5 +- 9 files changed, 238 insertions(+), 19 deletions(-) create mode 100644 ets-tests/ets/environment-tests/pages/storage/EnvironmentTest.ets diff --git a/arkoala-arkts/arkui/@ohos.arkui.component/index.ets b/arkoala-arkts/arkui/@ohos.arkui.component/index.ets index 0b2dc9f2a..77b5febaa 100644 --- a/arkoala-arkts/arkui/@ohos.arkui.component/index.ets +++ b/arkoala-arkts/arkui/@ohos.arkui.component/index.ets @@ -116,6 +116,7 @@ export * from '../sdk/component/sidebar'; export * from '../sdk/component/slider'; export * from '../sdk/component/span'; export * from '../sdk/component/stack'; +export * from '../sdk/component/stateManagement'; export * from '../sdk/component/stepper'; export * from '../sdk/component/stepperItem'; export * from '../sdk/component/styledString'; diff --git a/arkoala-arkts/arkui/sdk/Storage.ets b/arkoala-arkts/arkui/sdk/Storage.ets index 5309aaffa..69f296005 100644 --- a/arkoala-arkts/arkui/sdk/Storage.ets +++ b/arkoala-arkts/arkui/sdk/Storage.ets @@ -301,3 +301,70 @@ export declare class LocalStorage { */ clear(): boolean } + +/** + * EnvProps object + * + * @interface EnvPropsOptions + * @syscap SystemCapability.ArkUI.ArkUI.Full + * @crossplatform + * @since 10 + */ +export declare interface EnvPropsOptions { + /** + * Property name + * + * @type { string } + * @syscap SystemCapability.ArkUI.ArkUI.Full + * @crossplatform + * @since 10 + */ + key: string; + /** + * DefaultValue is the default value if cannot get the environment property value + * + * @type { number | string | boolean } + * @syscap SystemCapability.ArkUI.ArkUI.Full + * @crossplatform + * @since 10 + */ + defaultValue: number | string | boolean; +} + +/** + * Defines the Environment interface. + * @since 7 + */ +export declare class Environment { + /** + * Called when a property value is checked. + * @since 7 + * @deprecated since 10 + */ + static EnvProp(key: string, value: T): boolean + + /** + * Called when a property value is checked. + * @since 10 + */ + static envProp(key: string, value: T): boolean + + /** + * Called when multiple property values are checked. + * @since 10 + */ + static envProps(props: EnvPropsOptions[]): void + + /** + * Set the key value. + * @since 7 + * @deprecated since 10 + */ + static Keys(): Array + + /** + * Set the key value. + * @since 10 + */ + static keys(): Array +} diff --git a/arkoala-arkts/arkui/src/Storage.ets b/arkoala-arkts/arkui/src/Storage.ets index 85f5fe696..337194d3f 100644 --- a/arkoala-arkts/arkui/src/Storage.ets +++ b/arkoala-arkts/arkui/src/Storage.ets @@ -16,7 +16,9 @@ import { observableProxy } from "@koalaui/common" import { MutableState, GlobalStateManager } from "@koalaui/runtime" import { AbstractProperty, SubscribedAbstractProperty } from "./ArkState" - +import { EnvironmentBackend } from "./stateManagement.ets" +import { ColorMode, LayoutDirection } from "#generated" +import { int32, float32 } from "@koalaui/common" ///// ArkUI ///// see common_ts_ets_api.d.ts ///// @@ -263,7 +265,7 @@ export class AppStorage { * @crossplatform * @since 10 */ -interface EnvPropsOptions { +export interface EnvPropsOptions { /** * Property name * @@ -281,7 +283,7 @@ interface EnvPropsOptions { * @crossplatform * @since 10 */ - defaultValue: number | string | boolean; + defaultValue: number | string | boolean | ColorMode | LayoutDirection; } @@ -290,6 +292,25 @@ interface EnvPropsOptions { * @since 7 */ export class Environment { + private static instance: Environment | undefined = undefined + private props = new Set() + private typeMap = new Map([ + ['accessibilityEnabled', Type.from()], + ['layoutDirection', Type.from()], + ['languageCode', Type.from()], + ['colorMode', Type.from()], + ['fontScale', Type.from()], + ['fontWeightScale', Type.from()], + ]) + + private static getInstance(): Environment { + let env = Environment.instance + if (!env) { + Environment.instance = env = new Environment() + } + return env + } + /** * Called when a property value is checked. * @since 7 @@ -303,7 +324,11 @@ export class Environment { * @since 10 */ static envProp(key: string, value: T): boolean { - throw new Error("Environment.EnvProp is not implemented") + if (!Environment.getInstance().typeMap.has(key)) { + // invalid key + return false + } + return Environment.getInstance().envPropInternal(key, value) } /** @@ -327,7 +352,59 @@ export class Environment { * @since 10 */ static keys(): Array { - throw new Error("Environment.Keys is not implemented") + return Array.from(Environment.getInstance().props.keys()) + } + + private envPropInternal(key: string, defaultValue: T): boolean { + if (AppStorage.has(key)) return false + + let value: Any = undefined + switch (key) { + case 'accessibilityEnabled': + value = EnvironmentBackend.isAccessibilityEnabled() + break; + case 'layoutDirection': + value = EnvironmentBackend.getLayoutDirection() + break; + case 'languageCode': + value = EnvironmentBackend.getLanguageCode() + break; + case 'colorMode': + value = EnvironmentBackend.getColorMode() + break; + case 'fontScale': + value = EnvironmentBackend.getFontScale() + break; + case 'fontWeightScale': + value = EnvironmentBackend.getFontWeightScale() + break; + default: + value = defaultValue as Any + } + + if (value === undefined || value === -1 || value === '') { + value = defaultValue as Any + } else if (key === 'layoutDirection') { + value = this.convertToLayoutDirection(value as string) + } else if (key === 'colorMode') { + value = (value as int32) > 0 ? ColorMode.DARK : ColorMode.LIGHT + } else if (key === 'fontWeightScale') { + value = Math.round((value as float) * 100) / 100 + } + + const prop = AppStorage.setAndRef(key, value as T) + if (!prop) return false + + Environment.getInstance().props.add(key) + return true + } + + private convertToLayoutDirection(value: string): LayoutDirection { + switch (value) { + case "LTR": return LayoutDirection.LTR + case "RTL": return LayoutDirection.RTL + default: return LayoutDirection.AUTO + } } } @@ -726,7 +803,8 @@ class StorageMap { } setAndProp(key: string, value: T): SubscribedAbstractProperty { - return new StorageProp(this, key, value) + const entry = this.entry(key) + return new StorageProp(this, key, entry ? entry.get() : value) } delete(key: string): boolean { diff --git a/ets-tests/ets/TestHarness.ets b/ets-tests/ets/TestHarness.ets index 82d2fd565..4b6a5fbbd 100644 --- a/ets-tests/ets/TestHarness.ets +++ b/ets-tests/ets/TestHarness.ets @@ -68,7 +68,7 @@ export interface TestController { stop(): TestController nextFrame(): Promise skipFrames(count: int32): Promise - loadPage(page: string): TestController + loadPage(page: string, preLoad?: (() => void) | undefined): TestController notifyChange(id: string): TestController notifyClick(id: string): TestController snapshot(target: string, name: string): boolean @@ -76,6 +76,7 @@ export interface TestController { export class TestHarness extends UserView implements TestController { private currentTest: string + private preLoad: (() => void) | undefined constructor() { super() this.currentTest = "" @@ -106,8 +107,9 @@ export class TestHarness extends UserView implements TestController { } return this } - loadPage(page: string): TestController { + loadPage(page: string, preLoad: (() => void) | undefined = undefined): TestController { this.currentTest = page + this.preLoad = preLoad this.control!.reloadView() return this } @@ -148,6 +150,7 @@ export class TestHarness extends UserView implements TestController { } getBuilder(): UserViewBuilder { + if (this.preLoad !== undefined) this.preLoad!() return memoBind(pageByNameFunction, this.currentTest) } } diff --git a/ets-tests/ets/environment-tests/Pages.ets b/ets-tests/ets/environment-tests/Pages.ets index d7d793b69..1d57cc945 100644 --- a/ets-tests/ets/environment-tests/Pages.ets +++ b/ets-tests/ets/environment-tests/Pages.ets @@ -126,6 +126,7 @@ import { ComponentV2Page1 } from "./pages/componentv2/ComponentV2Page1" import { ComponentV2Page2 } from "./pages/componentv2/ComponentV2Page2" import { LocalObjectTest } from "./pages/local/LocalObjectTest" import { ProviderConsumerBaseTest } from "./pages/provider/ProviderConsumerBaseTest" +import { EnvironmentTest } from "./pages/storage/EnvironmentTest" initModule("./pages/lifecycle/PageCallbacks") initModule("./pages/lifecycle/PageSwitch") @@ -233,6 +234,7 @@ function pageByName(name: string): void { case "ComponentV2Page2": ComponentV2Page2(); break case "LocalObjectTest": LocalObjectTest(); break case "ProviderConsumerBaseTest": ProviderConsumerBaseTest(); break + case "EnvironmentTest": EnvironmentTest(); break case "": break default: throw new Error(`No test case ${name} provided!`) } diff --git a/ets-tests/ets/environment-tests/pages/storage/EnvironmentTest.ets b/ets-tests/ets/environment-tests/pages/storage/EnvironmentTest.ets new file mode 100644 index 000000000..1882648d8 --- /dev/null +++ b/ets-tests/ets/environment-tests/pages/storage/EnvironmentTest.ets @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { TestComponent, uiLog } from '@ohos.arkui' +import { Environment } from '@ohos.arkui.stateManagement' +import { ColorMode, LayoutDirection } from '@ohos.arkui.component' +import { int32 } from "@koalaui/common" + +let initLanguage: boolean | undefined = undefined +let initColor: boolean | undefined = undefined +let initLayout: boolean | undefined = undefined +let initPath: boolean | undefined = undefined + +export function envInitialize() { + initLanguage = Environment.envProp("languageCode", 'ru') + initColor = Environment.envProp('colorMode', ColorMode.LIGHT) + initLayout = Environment.envProp('layoutDirection', LayoutDirection.LTR) + // invalid key + initPath = Environment.envProp('path', '.') +} + +@Entry +@Component +struct EnvironmentTest { + @StorageProp("languageCode") lang: string = 'zh' + @StorageProp("colorMode") color: ColorMode = ColorMode.LIGHT + @StorageProp("layoutDirection") layoutDir: LayoutDirection = LayoutDirection.LTR + private initColor: boolean | undefined = undefined + + aboutToAppear() { + // duplicated initialization + this.initColor = Environment.envProp('colorMode', ColorMode.DARK) + } + + build() { + TestComponent({}) { uiLog(`languageCode: ${this.lang} ${initLanguage}`) } + TestComponent({}) { uiLog(`colorMode: ${this.color} ${this.initColor} ${initColor}`) } + TestComponent({}) { uiLog(`layoutDirection: ${this.layoutDir} ${initLayout}`) } + TestComponent({}) { uiLog(`path: ${initPath}`) } + TestComponent({}) { uiLog(`env keys: ${Environment.keys().join(', ')}`) } + } +} diff --git a/ets-tests/ets/environment-tests/suites/StorageTests.ets b/ets-tests/ets/environment-tests/suites/StorageTests.ets index 4ea7c094b..16e1c6f9f 100644 --- a/ets-tests/ets/environment-tests/suites/StorageTests.ets +++ b/ets-tests/ets/environment-tests/suites/StorageTests.ets @@ -13,10 +13,11 @@ * limitations under the License. */ -import { testPageOnChange } from "../../utils" +import { testPageOnChange, testPageOnLoad } from "../../utils" import { TestController } from "../../TestHarness" import { suite, test } from "@koalaui/harness" -import { LocalStorage } from "@ohos.arkui.stateManagement" +import { LocalStorage, Environment } from "@ohos.arkui.stateManagement" +import { envInitialize } from "../pages/storage/EnvironmentTest" export function suiteAppLocalStorage(control: TestController) { suite("AppStorage - Link", () => { @@ -66,7 +67,7 @@ export function suiteAppLocalStorage(control: TestController) { const initLog = "number.ref: 74\n" + "number.link: 74\n" + "number.prop: 74\n" + - "number.field: 0\n" + "number.field: 74\n" test("@StorageProp update by ref", () => { testPageOnChange(control, "AppStoragePropTest", 21, // initialization @@ -158,7 +159,7 @@ export function suiteAppLocalStorage(control: TestController) { "number.ref: 74\n" + "number.link: 74\n" + "number.prop: 74\n" + - "number.field: 0\n" + + "number.field: 74\n" + // expected updates "number.ref: 111\n" + "number.link: 111\n" + @@ -171,7 +172,7 @@ export function suiteAppLocalStorage(control: TestController) { "number.ref: 111\n" + "number.link: 111\n" + "number.prop: 111\n" + - "number.field: 0\n" + + "number.field: 111\n" + // expected updates "number.ref: 222\n" + "number.link: 222\n" + @@ -184,7 +185,7 @@ export function suiteAppLocalStorage(control: TestController) { "number.ref: 222\n" + "number.link: 222\n" + "number.prop: 222\n" + - "number.field: 0\n" + + "number.field: 222\n" + // expected updates "number.prop: 333\n") }) @@ -194,9 +195,22 @@ export function suiteAppLocalStorage(control: TestController) { "number.ref: 222\n" + "number.link: 222\n" + "number.prop: 222\n" + - "number.field: 0\n" + + "number.field: 222\n" + // expected updates "number.field: 444\n") }) }) + + suite("Environment parameters", () => { + test("Get environment parameters", () => { + testPageOnLoad(control, "EnvironmentTest", + [ + "languageCode: en true", + "colorMode: 1 false true", + "layoutDirection: 0 true", + "path: false", + "env keys: languageCode, colorMode, layoutDirection", + ], "", 3, () => envInitialize()) + }) + }) } diff --git a/ets-tests/ets/environment-tests/suites/WatchDecoratorTest.ets b/ets-tests/ets/environment-tests/suites/WatchDecoratorTest.ets index 5d7653fc4..7804d591c 100644 --- a/ets-tests/ets/environment-tests/suites/WatchDecoratorTest.ets +++ b/ets-tests/ets/environment-tests/suites/WatchDecoratorTest.ets @@ -170,11 +170,11 @@ export function suiteWatchDecorator(control: TestController) { [100, 101, 110, 111, 120, 121, 130, 131, 140, 141, 101, 100], [ 'WatchA 1: linkValueA = 2; storage = 2', - 'WatchB 2: propValueB = 1; storage = 11', + 'WatchB 2: propValueB = 12; storage = 11', 'WatchA 3: linkValueA = 110; storage = 110', 'WatchA 4: linkValueA = 120; storage = 120', 'WatchA 5: linkValueA = 140; storage = 140', - 'WatchB 6: propValueB = 2; storage = 141', + 'WatchB 6: propValueB = 13; storage = 141', 'WatchA 7: linkValueA = 141; storage = 141', ] ) diff --git a/ets-tests/ets/utils.ets b/ets-tests/ets/utils.ets index f24e642f5..7db76fff2 100644 --- a/ets-tests/ets/utils.ets +++ b/ets-tests/ets/utils.ets @@ -21,11 +21,12 @@ export function testPageOnLoad( page: string, expected: string | Array, message?: string, - skipFrames: int32 = 3 + skipFrames: int32 = 3, + preLoad?: () => void ): void { control.start() // make sure child components of all nested levels are loaded - control.loadPage(page) + control.loadPage(page, preLoad) await control.skipFrames(skipFrames) control.stop() const result = control.getLog() -- Gitee