• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/*
2 * Copyright (c) 2021-2022 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 testViewStateFU = tsuite("View State", () => {
17
18  // instead of a register, use glbal variables
19  let childView: Child;
20
21  class Parent extends View {
22    // generated from
23    // @State private forLink_ : boolean = 1
24    // Info is the public property not the name of the prop in backing store
25    /* private, for test purposes: */ public __forLink_: ObservedPropertySimple<boolean>
26      = new ObservedPropertySimple<boolean>(true, this, "forLink_");
27    public get forLink_(): boolean {
28      return this.__forLink_.get();
29    }
30    public set forLink_(newValue: boolean) {
31      this.__forLink_.set(newValue);
32    }
33
34    // generated from
35    // @State private forProp_ : string
36    // Info is the public property not the name of the prop in backing store
37    /* private, for test purposes: */ public __forProp_: ObservedPropertySimple<string>
38      = new ObservedPropertySimple<string>("ForProp Value OK", this, "forProp_");
39    public get forProp_(): string {
40      return this.__forProp_.get();
41    }
42    public set forProp_(newValue: string) {
43      this.__forProp_.set(newValue);
44    }
45
46    // generated from
47    // private forReg_ : string = "forReg Orig";
48     /* private, for test purposes: */ public forReg_: string = "forReg Orig";
49
50    // generated based on state definitions
51    constructor(compilerAssignedUniqueChildId: string, parent: View,
52      params: {
53        forLink_?: boolean,
54        forProp_?: string,
55        forReg_?: string;
56      },
57      uri : string,
58      localStorage : LocalStorage) {
59      super(compilerAssignedUniqueChildId, parent, localStorage);
60      // tsc will add initialization of forLink_ and forProp_ with defauilt values here
61      this.updateWithValueParams(params);
62    }
63
64    // generated based on state definitions
65    public updateWithValueParams(params: {
66      forLink_?: boolean,
67      forProp_?: string,
68      forReg_?: string
69    }): void {
70      console.debug(`${this.id__()}:${this.constructor.name}: updateWithValueParams ...`);
71      if (params.forLink_) {
72        this.forLink_ = params.forLink_;
73      }
74      if (params.forProp_) {
75        this.forProp_ = params.forProp_;
76      }
77      if (params.forReg_) {
78        this.forReg_ = params.forReg_;
79      }
80      console.debug(`${this.id__()}:${this.constructor.name}: updateWithValueParams done`);
81    }
82
83    // generated based on state definitions
84    public aboutToBeDeleted() {
85      console.debug(`${this.id__()}:${this.constructor.name}: abouttoBeDeleted`);
86      // tell the ObservedProperty this View is to be deleted
87      this.__forLink_.aboutToBeDeleted(this);
88      this.__forProp_.aboutToBeDeleted(this);
89      SubscriberManager.Delete(this.id__());
90    }
91
92    // added just to make things work, not in production when this func is implemented in C++
93    findChildById(compilerAssignedUniqueChildId: string): View {
94      console.log(`${this.id__()}:${this.constructor.name}: findChildById ${compilerAssignedUniqueChildId}. Will not work!`);
95      return childView;
96    }
97
98    public render() {
99      // dump state
100      console.debug(`${this.id__()}:${this.constructor.name}: render: state is: forLink_: ${this.forLink_}, forProp_: ${this.forProp_}`);
101
102      const compilerAssignedChildId = "1";
103            /* global var */ childView = this.findChildById(compilerAssignedChildId) as Child;
104      if (!childView) {
105        childView = new Child(compilerAssignedChildId, this, {
106          link_: this.__forLink_,
107          prop_: this.__forProp_,
108          reg_: this.forReg_
109        });
110      } else {
111        childView.updateWithValueParams({ reg_: this.forReg_ });
112      }
113    }
114  }
115
116  // Child ----------------------------------------
117  class Child extends View
118    implements IPropertySubscriber {
119
120    // generated from
121    // @State private num_ : number = 1;
122    private __num_: ObservedPropertySimple<number>
123      = new ObservedPropertySimple<number>(1, this, "__num_");
124    /* private changed to for testing: */ public get num_(): number {
125      return this["__num_"].get();
126    }
127    /* private changed to for testing: */ public set num_(newValue: number) {
128      this["__num_"].set(newValue);
129    }
130
131    // generated from
132    // @Link private link_ : boolean
133     /* private changed to for testing: */ __link_: ObservedPropertySimpleAbstract<boolean>;
134    /* private changed to for testing: */ public get link_(): boolean {
135      return this.__link_.get();
136    }
137    /* private changed to for testing: */ public set link_(newValue: boolean) {
138      this.__link_.set(newValue);
139    }
140
141    // generated from
142    // @Prop private prop_ : string
143     /* private changed to for testing: */ __prop_: ObservedPropertySimpleAbstract<string>;
144    /* private changed to for testing: */ public get prop_(): string {
145      return this.__prop_.get();
146    }
147    /* private changed to for testing: */ public set prop_(newValue: string) {
148      this.__prop_.set(newValue);
149    }
150
151    // eDSL compiler makes no change
152    /* private changed to for testing: */ public reg_: string = "initial value"
153
154    constructor(compilerAssignedUniqueChildId: string, parent: View,
155      params: {
156        num_?: number,
157        link_: ObservedPropertySimpleAbstract<boolean>,
158        prop_: ObservedPropertySimpleAbstract<string>,
159        reg_?: string
160      }) {
161      super(compilerAssignedUniqueChildId, parent);
162      // tsc will add initialization of num_ and reg_ with defauilt values here
163      this.__link_ = params.link_.createLink(this, "__link_");
164      this.__prop_ = params.prop_.createProp(this, "__prop_");
165      this.updateWithValueParams(params);
166    }
167
168    // generated
169    public updateWithValueParams(params: { num_?: number, reg_?: string }): void {
170      console.debug(`${this.id__()}:${this.constructor.name}: updateWithValueParams start`);
171      if (params.num_) {
172        this.num_ = params.num_;
173      }
174      if (params.reg_) {
175        this.reg_ = params.reg_;
176      }
177      console.debug(`${this.constructor.name}: updateWithValueParams done`);
178    }
179
180    // generated based on state definitions
181    // notify all SubscribedAbstractProperty objects they will be deleted soon
182    public aboutToBeDeleted() {
183      console.debug(`${this.id__()}:${this.constructor.name}: aboutToBeDeleted`);
184      // notify @State
185      // this View unsubscribes
186      this.__num_.aboutToBeDeleted(this);
187
188      // notify for @Link and @Prop
189      // to allow them to unsubscribe from their source properties
190      // this View unsubscribes as well
191      this.__link_.aboutToBeDeleted();
192      this.__prop_.aboutToBeDeleted();
193
194      SubscriberManager.Delete(this.id__());
195    }
196
197
198    // transpiled from build()
199    public render() {
200      // dump state
201      console.debug(`${this.id__()}:${this.constructor.name}: render: state is: num_: ${this.num_}, link_: ${this.link_}, prop_: ${this.prop_}, reg_=${this.reg_}`);
202    }
203  }
204
205
206  // simulate the process
207
208  console.debug("create LocalStorage ...")
209  const uri = "stateMgmt/src/utest/view_test.ts";
210  let localStorageForView : LocalStorage = new LocalStorage({});
211
212  console.debug("create Parent ...");
213  let parentView: Parent = new Parent("0", undefined, {}, uri, localStorageForView);
214
215
216  tcase("Parent.render #1 ...", () => {
217
218    // to test the recording of get access / View.propsNeededForRender
219    parentView.aboutToRender();
220    parentView.render();
221    parentView.onRenderDone();
222
223    test(`get access recording during parent render, recorded: ${JSON.stringify(Array.from(parentView.propertiesNeededToRender()))}`,
224      eqSet(parentView.propertiesNeededToRender(), new Set<string>(["forLink_", "forProp_"])));
225
226    childView.render();
227
228    test(`parentView.forLink_ value ok`, parentView.forLink_ == true);
229    test(`parentView.forProp_ value ok`, parentView.forProp_ == "ForProp Value OK");
230    test(`parentView.forReg_ value ok`, parentView.forReg_ == "forReg Orig");
231
232    test(`childView.num_ value ok`, childView.num_ == 1);
233    test(`childView.link_ value ok`, childView.link_ == true);
234    test(`childView.prop_ value ok`, childView.prop_ == "ForProp Value OK");
235    test(`childView.reg_ value ok`, childView.reg_ == "forReg Orig");
236
237    test(`Check @Link and @State subscribed ok: parent.__forLink_ subscribers`, parentView.__forLink_.numberOfSubscrbers() == 2);
238    test(`Check @Link and @State subscribed ok: parent.__forProp_ subscribers`, parentView.__forProp_.numberOfSubscrbers() == 2);
239
240    SubscriberManager.DumpSubscriberInfo();
241
242    // parent view
243    // parent view 2 state vard
244    // child view
245    // child view 3 state vars
246    // 7 subscribers total
247    test(`SubscriberManager num of subscribers, is ${SubscriberManager.NumberOfSubscribers()} should be 7`, SubscriberManager.NumberOfSubscribers() == 7);
248  });
249
250  tcase("Simulate an event handler mutating parent's @State props", () => {
251
252    let spyParentPropertyHasChanged = spyOn(parentView, "propertyHasChanged");
253    let spyChildPropertyHasChanged = spyOn(childView, "propertyHasChanged");
254
255    let spyParentPropertyHasBeenRead = spyOn(parentView, "propertyRead");
256    let spyChildPropertyHasBeenRead = spyOn(childView, "propertyRead");
257
258    parentView.forLink_ = false;
259    parentView.forProp_ = "_forProp changed value OK";
260    // render will do the updating of the prop value:
261    childView.prop_ = parentView.forProp_;
262
263    test(`parentView.forLink_ value ok`, parentView.forLink_ == false);
264    test(`parentView.forProp_ value ok`, parentView.forProp_ == "_forProp changed value OK");
265    test(`parentView.forReg_ value ok`, parentView.forReg_ == "forReg Orig");
266
267    test(`childView.num_ unchanged`, childView.num_ == 1);
268    test(`childView.link_ updated ok`, childView.link_ == false);
269    test(`childView.prop_ updated ok`, childView.prop_ == "_forProp changed value OK");
270    test(`childView.reg_ unchanged`, childView.reg_ == "forReg Orig");
271
272    test(`parentView.aPropertyHasChanged was called`, spyParentPropertyHasChanged.called)
273    test(`childView.aPropertyHasChanged was called`, spyChildPropertyHasChanged.called)
274
275    test(`parentView.aPropertyHasBeenRead was called`, spyParentPropertyHasBeenRead.called)
276    test(`childView.aPropertyHasBeenRead was called`, spyChildPropertyHasBeenRead.called)
277  });
278
279  tcase("Parent.render #2 ...", () => {
280
281    parentView.render();
282    childView.render();
283
284    test(`childView.num_ unchanged`, childView.num_ == 1);
285    test(`childView.link_ unchanged`, childView.link_ == false);
286    test(`childView.prop_ unchanged`, childView.prop_ == "_forProp changed value OK");
287    test(`childView.reg_ unchanged`, childView.reg_ == "forReg Orig");
288  });
289
290  tcase("Simulate an event handler mutating parent's regular (unobserved) variable", () => {
291
292    let spyParentPropertyHasChanged = spyOn(parentView, "propertyHasChanged");
293
294    parentView.forReg_ = "forReg_ chanaged";
295    test(`parentView.forReg_ value ok`, parentView.forReg_ == "forReg_ chanaged");
296    test(`childView.reg_ unchanged`, childView.reg_ == "forReg Orig");
297    test(`parentView.propertyHasChanged was NOT called`, !spyParentPropertyHasChanged.called)
298  });
299
300  tcase("Parent.render #3 ...", () => {
301
302    parentView.render();
303    childView.render();
304
305    let spyChildaPropertyHasChanged = spyOn(childView, "aPropertyHasChanged");
306
307    test(`parentView.forReg_ value ok`, parentView.forReg_ == "forReg_ chanaged");
308    test(`childView.reg_ changed`, childView.reg_ == "forReg_ chanaged");
309    test(`childView.aPropertyHasChanged was NOT called`, !spyChildaPropertyHasChanged.called)
310  });
311
312  tcase("Notify about deletion, first child ...", () => {
313
314    let spyChildLinkAboutToBeDeleted = spyOn(childView.__prop_, "aboutToBeDeleted");
315    let spyChildPropAboutToBeDeleted = spyOn(childView.__link_, "aboutToBeDeleted");
316
317    childView.aboutToBeDeleted();
318    test(`aboutToBeDeleted was called on childView.__link`, spyChildLinkAboutToBeDeleted.called);
319    test(`aboutToBeDeleted was called on childView.__prop`, spyChildPropAboutToBeDeleted.called);
320    test(`parent.__forLink_ subscribers`, parentView.__forLink_.numberOfSubscrbers() == 1);
321
322    SubscriberManager.DumpSubscriberInfo();
323
324    // parent view
325    // parent view forLink_, forProp_ state vars
326    test(`SubscriberManager num of subscribers is ${SubscriberManager.NumberOfSubscribers()} should be 3`, SubscriberManager.NumberOfSubscribers() == 3);
327  });
328
329  tcase("Notify about deletion, second parent ...", () => {
330    parentView.aboutToBeDeleted();
331    test(`parent.__forLink_ subscribers`, parentView.__forLink_.numberOfSubscrbers() == 0);
332
333    SubscriberManager.DumpSubscriberInfo();
334
335    test(`SubscriberManager num of subscribers is ${SubscriberManager.NumberOfSubscribers()} should be 0 .`, SubscriberManager.NumberOfSubscribers() == 0);
336  });
337
338});
339