1/* 2 * Copyright (c) 2021 Huawei Device Co., Ltd. 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software 10 * distributed under the License is distributed on an "AS IS" BASIS, 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * See the License for the specific language governing permissions and 13 * limitations under the License. 14 */ 15 16const testAppStorageArray = tsuite("AppStorage Array", () => { 17 18 class ArrayChangeSubscriber implements ISinglePropertyChangeSubscriber<number[]> { 19 20 private id_: number = SubscriberManager. MakeId(); 21 22 constructor() { 23 AppStorage.SubscribeToChangesOf("arr", this); 24 SubscriberManager.Add(this); 25 } 26 27 // globally unique id 28 id__(): number { 29 return this.id_; 30 } 31 32 // inform the subscribed property 33 // that the subscriber is about to be deleted 34 // hence , unsubscribe 35 aboutToBeDeleted(owningView?: IPropertySubscriber): void { 36 AppStorage.UnsubscribeFromChangesOf("arr", this.id__()); 37 SubscriberManager.Delete(this.id__()); 38 } 39 40 // get informed after the property has changed. 41 hasChanged(newValue: number[]): void { 42 console.log(`ArrayChangeSubscriber.newValue ${newValue}.`) 43 } 44 } 45 46 class ClassPropertyChangeSubscriber<T extends Object> 47 implements ISinglePropertyChangeSubscriber<T>, IMultiPropertiesChangeSubscriber { 48 49 private id_: number = SubscriberManager.MakeId(); 50 private classInfo_: string; 51 52 constructor(classInfo: string) { 53 this.classInfo_ = classInfo; 54 SubscriberManager.Add(this); 55 } 56 57 // globally unique id 58 id__(): number { 59 return this.id_; 60 } 61 62 // inform the subscribed property 63 // that the subscriber is about to be deleted 64 // hence , unsubscribe 65 aboutToBeDeleted(owningView?: IPropertySubscriber): void { 66 SubscriberManager.Delete(this.id__()); 67 } 68 69 // get informed after the property has changed. 70 // implements: SinglePropertyChangeSubscriber<T> 71 hasChanged(newValue: T): void { 72 console.log(`Class ${this.classInfo_} object inside property has changed. newValue ${newValue}.`) 73 } 74 75 // implements IMultiPropertiesChangePublisher 76 propertyHasChanged(info?: PropertyInfo): void { 77 console.log(`Class ${this.classInfo_} object property ${info ? info : 'unknown'} has changed.`); 78 } 79 } 80 81 tcase("ObservedObject.IsObservedObject test", () => { 82 83 test("array", !ObservedObject.IsObservedObject([1, 2, 3])); 84 test("object", !ObservedObject.IsObservedObject({ a: 1 })); 85 test("number", !ObservedObject.IsObservedObject(1)); 86 test("boolean", !ObservedObject.IsObservedObject(true)); 87 test("undefined", !ObservedObject.IsObservedObject(undefined)); 88 test("undefined", !ObservedObject.IsObservedObject(undefined)); 89 90 let sub1 = new ClassPropertyChangeSubscriber<number[]>("number[] #1 sub"); 91 let arr1 = ObservedObject.createNew([1, 2, 3], sub1); 92 let sub2 = new ClassPropertyChangeSubscriber<number[]>("number[] #2 sub"); 93 let arr2 = ObservedObject.createNew({ a: 1 }, sub2); 94 95 test("observed array", ObservedObject.IsObservedObject(arr1)); 96 test("observed object", ObservedObject.IsObservedObject(arr2)); 97 98 ObservedObject.removeOwningProperty(arr1, sub1); 99 sub1.aboutToBeDeleted(); 100 101 ObservedObject.removeOwningProperty(arr2, sub2); 102 sub2.aboutToBeDeleted(); 103 }); 104 105 106 tcase("create and read back", () => { 107 let arr: number[] = [1, 2, 3, 4]; 108 AppStorage.SetOrCreate<number[]>("arr", arr); 109 110 test("AppStrorage has initial one ObservedProperties", AppStorage.Size() == 1); 111 test("Initial array value readback.", AppStorage.Get<number[]>("arr").toString() == arr.toString()); 112 test(`Initial array value readback, check Array.isArray`, Array.isArray(AppStorage.Get<number[]>("arr"))); 113 test(`Initial array value readback, check instance of ObservedObject`, ObservedObject.IsObservedObject(AppStorage.Get<number[]>("arr"))); 114 }); 115 116 tcase("push to array", () => { 117 let arrSub = new ArrayChangeSubscriber(); 118 let spyArrayChange = spyOn(arrSub, "hasChanged"); 119 AppStorage.Get<number[]>("arr").push(5); 120 test("Changed array value readback", AppStorage.Get<number[]>("arr").toString() == [1, 2, 3, 4, 5].toString()); 121 test(`hasChanged was called`, spyArrayChange.called); 122 test(`hasChanged param is the correct changed array.`, spyArrayChange.args[0].toString() == [1, 2, 3, 4, 5].toString()); 123 test(`Initial array value readback, check instance of ObservedObject`, ObservedObject.IsObservedObject(AppStorage.Get<number[]>("arr"))); 124 arrSub.aboutToBeDeleted(); 125 }); 126 127 tcase("change property value gets observed #1", () => { 128 let arrSub = new ArrayChangeSubscriber(); 129 let spyArrayChange = spyOn(arrSub, "hasChanged"); 130 131 AppStorage.Set<number[]>("arr", [2, 3, 4]); 132 133 test("Changed array value readback", AppStorage.Get<number[]>("arr").toString() == [2, 3, 4].toString()); 134 test(`hasChanged was called`, spyArrayChange.called); 135 test(`hasChanged param is the correct changed array.`, spyArrayChange.args[0].toString() == [2, 3, 4].toString()); 136 test(`Initial array value readback, check instance of ObservedObject`, ObservedObject.IsObservedObject(AppStorage.Get<number[]>("arr"))); 137 138 arrSub.aboutToBeDeleted(); 139 }); 140 141 142 tcase("change property value gets observed #2", () => { 143 let arrSub = new ArrayChangeSubscriber(); 144 AppStorage.Set<number[]>("arr", [2, 3, 4]); 145 let spyArrayChange = spyOn(arrSub, "hasChanged"); 146 AppStorage.Get<number[]>("arr")[1] = 7; 147 148 test("Changed array value readback", AppStorage.Get<number[]>("arr").toString() == [2, 7, 4].toString()); 149 test(`hasChanged was called`, spyArrayChange.called); 150 test(`hasChanged param is the correct changed array.`, spyArrayChange.args[0].toString() == [2, 7, 4].toString()); 151 arrSub.aboutToBeDeleted(); 152 }); 153 154 tcase("ObservedObject Array proxying is transparent", () => { 155 AppStorage.Set<number[]>("arr", [2, 3, 4]); 156 let link = AppStorage.Link<number[]>("arr"); 157 test("link readback value OK", link.get().toString() == [2, 3, 4].toString()); 158 test("link readback value OK", link.get().length == 3); 159 test("Array.isArray(link) OK", Array.isArray(link.get())); 160 test("link value is instance of ObservedObject OK", ObservedObject.IsObservedObject(link.get())); 161 162 const rawArray = ObservedObject.GetRawObject(link.get()); 163 test("ObservedObject.GetRawObject value readback OK", rawArray.toString() == [2, 3, 4].toString()); 164 test("GetRawObject on link value returns a raw", !ObservedObject.IsObservedObject(rawArray)); 165 166 link.aboutToBeDeleted(); 167 }); 168 169 170 tcase("cleanup ok", () => { 171 172 // manual cleanup to see everything works as expected 173 const deleteOk = AppStorage.Delete("arr"); 174 test(`Deletion of props form AppStrorage without isuses`, deleteOk); 175 test(`AppStrorage has ${AppStorage.Size()} ObservedProperties: >${Array.from(AppStorage.Keys())}<, should be none.`, AppStorage.Size() == 0); 176 177 test(`SubscriberManager num of subscribers us ${SubscriberManager.NumberOfSubscribers()} should be 0 .`, 178 SubscriberManager.NumberOfSubscribers() == 0); 179 180 // this will do nothing because manual cleanup was done already 181 AppStorage.AboutToBeDeleted(); 182 }); 183}); 184