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