Skip to content

Commit 55995a9

Browse files
committed
error throw + test State variable
1 parent f8886b3 commit 55995a9

File tree

3 files changed

+171
-45
lines changed

3 files changed

+171
-45
lines changed

build/stateElement.js

Lines changed: 41 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -4,18 +4,31 @@ export var stateBehaviour;
44
stateBehaviour["NORMAL"] = "NORMAL";
55
stateBehaviour["READONLY"] = "READONLY";
66
})(stateBehaviour || (stateBehaviour = {}));
7-
var _statewatchdog = 0;
7+
var _isCallback_locked = false;
88
const _transitions_callbackMap = new Map();
99
export class StateTransition {
1010
constructor(NAME) {
1111
this.name = NAME;
1212
this.callbackMap = new Map();
1313
this.usrDefined_transition = undefined;
14+
if (typeof (this.name) !== "string")
15+
throw Error("Variable name must be a string.");
16+
}
17+
lock_callbacks(event) {
18+
if (_isCallback_locked) {
19+
console.log('The following target has dispatched a ' + this.name + ' event during a UI update callback:');
20+
console.log(event.target);
21+
throw Error('Forbidden multiple-update during an update callback loop.');
22+
}
23+
else
24+
_isCallback_locked = true;
25+
}
26+
unlock_callbacks() {
27+
_isCallback_locked = false;
1428
}
1529
updateHandler(event) {
1630
console.log('Handling event UPDATE from stateTransition: ' + this.name);
17-
(_statewatchdog >= 10000) ? _statewatchdog = 0 : _statewatchdog++;
18-
let sanity_check = _statewatchdog;
31+
this.lock_callbacks(event);
1932
this.usrDefined_transition(event);
2033
// loop over watchers callbacks
2134
for (let update_callback of this.callbackMap.values()) {
@@ -28,8 +41,7 @@ export class StateTransition {
2841
}
2942
}
3043
_transitions_callbackMap.clear();
31-
if (sanity_check !== _statewatchdog)
32-
throw Error('State variables update is forbidden within a data update callback.');
44+
this.unlock_callbacks();
3345
}
3446
watchHanlder(event) {
3547
//console.log('Adding element to watchlist of: '+this.name);
@@ -43,29 +55,42 @@ export class StateTransition {
4355
}
4456
}
4557
export class StateVariable extends StateTransition {
46-
constructor(NAME, TYPE, BEHAVIOUR) {
58+
constructor(NAME, TYPE, DEFAULT) {
4759
super(NAME);
4860
this.type = TYPE;
49-
this.behaviour = BEHAVIOUR;
50-
this.default_val = '100'; // FIXME default value problem
61+
this.behaviour = stateBehaviour.NORMAL;
62+
this.default_val = DEFAULT;
63+
this._err_on_value = 'Wrong type assignment to state variable: ' + this.name;
64+
// Sanity checks
65+
let white_list_types = ["string", "object", "number", "boolean"];
66+
if (typeof (TYPE) !== "string")
67+
throw Error("StateVariable type must be a string.");
68+
if (!white_list_types.includes(TYPE))
69+
throw Error(this._err_on_value);
5170
// set localstorage variable if none
5271
if (localStorage.getItem(this.name) === null)
53-
localStorage.setItem(this.name, this.default_val);
72+
this.value = this.default_val;
73+
}
74+
setBehaviour(behave_as) {
75+
this.behaviour = behave_as;
5476
}
5577
set value(val) {
5678
let push_var = val;
57-
//console.log('setting value to: '+this.name);
5879
if (typeof (val) === this.type) {
5980
if (this.type !== 'string')
6081
push_var = JSON.stringify(val);
6182
localStorage.setItem(this.name, push_var);
6283
}
84+
else
85+
throw Error(this._err_on_value);
6386
}
6487
get value() {
65-
//console.log('getting value of: '+this.name);
6688
let return_val = localStorage.getItem(this.name);
67-
if (this.type !== 'string')
68-
return_val = JSON.parse(return_val); // FIXME: use catch/err on parse...
89+
if (this.type !== 'string') {
90+
return_val = JSON.parse(return_val);
91+
if (typeof (return_val) !== this.type)
92+
throw Error("State variable: " + this.name + " is corrupted, returns type " + typeof (return_val) + " expecting " + this.type);
93+
}
6994
return return_val;
7095
}
7196
set auto_value(val) {
@@ -74,19 +99,13 @@ export class StateVariable extends StateTransition {
7499
}
75100
updateHandler(event) {
76101
console.log('Handling event UPDATE from state variable: ' + this.name);
77-
(_statewatchdog >= 10000) ? _statewatchdog = 0 : _statewatchdog++;
78-
let sanity_check = _statewatchdog;
79-
if (typeof (event.detail.value) === this.type) {
80-
this.value = event.detail.value;
81-
}
82-
else
83-
console.log('ERR: stateVariable - ' + this.name + ' forbidden value type.');
102+
this.lock_callbacks(event);
103+
this.value = event.detail.value;
84104
// loop over watchers callbacks
85105
for (let update_callback of this.callbackMap.values()) {
86106
update_callback(event.detail.value);
87107
}
88-
if (sanity_check !== _statewatchdog)
89-
throw Error('State variables update is forbidden within a data update callback.');
108+
this.unlock_callbacks();
90109
}
91110
}
92111
export class Message extends StateTransition {

src/stateElement.ts

Lines changed: 25 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ export class StateTransition {
1818
this.name = NAME;
1919
this.callbackMap = new Map();
2020
this.usrDefined_transition = undefined;
21+
22+
if(typeof(this.name) !== "string") throw Error("Variable name must be a string.");
2123
}
2224

2325
lock_callbacks(event:CustomEvent){
@@ -75,37 +77,47 @@ export class StateVariable extends StateTransition{
7577
type : string;
7678
default_val : any ;
7779
behaviour : stateBehaviour;
80+
_err_on_value :string;
7881

79-
constructor(NAME:string, TYPE:string, BEHAVIOUR:stateBehaviour){
82+
constructor(NAME:string, TYPE:string, DEFAULT:any){ // FIXME DEFAULT HAS A TYPE OF TYPE
8083
super(NAME);
8184
this.type = TYPE;
82-
this.behaviour = BEHAVIOUR;
83-
this.default_val = '100'; // FIXME default value problem
85+
this.behaviour = stateBehaviour.NORMAL;
86+
this.default_val = DEFAULT;
87+
this._err_on_value = 'Wrong type assignment to state variable: ' + this.name;
88+
89+
// Sanity checks
90+
let white_list_types = ["string", "object", "number", "boolean"];
91+
if(typeof(TYPE) !== "string") throw Error("StateVariable type must be a string.");
92+
if(!white_list_types.includes(TYPE)) throw Error(this._err_on_value);
8493

8594
// set localstorage variable if none
8695
if(localStorage.getItem(this.name) === null)
87-
localStorage.setItem(this.name, this.default_val);
96+
this.value = this.default_val;
8897
}
8998

99+
setBehaviour(behave_as:stateBehaviour){
100+
this.behaviour = behave_as;
101+
}
90102

91103
set value(val:any){
92104
let push_var = val;
93105

94-
//console.log('setting value to: '+this.name);
95106
if( typeof(val) === this.type ) {
96107
if(this.type !== 'string') push_var = JSON.stringify(val);
97108
localStorage.setItem(this.name, push_var);
98-
}
109+
}
110+
else throw Error(this._err_on_value);
99111
}
100112

101113
get value():any{
102114

103-
//console.log('getting value of: '+this.name);
104-
105115
let return_val = localStorage.getItem(this.name);
106-
if(this.type !== 'string')
107-
return_val = JSON.parse(return_val); // FIXME: use catch/err on parse...
108-
116+
if(this.type !== 'string'){
117+
return_val = JSON.parse(return_val);
118+
if(typeof(return_val) !== this.type )
119+
throw Error("State variable: "+this.name+" is corrupted, returns type "+typeof(return_val) +" expecting "+ this.type);
120+
}
109121
return return_val;
110122
}
111123

@@ -118,14 +130,9 @@ export class StateVariable extends StateTransition{
118130

119131
console.log('Handling event UPDATE from state variable: '+this.name);
120132
this.lock_callbacks(event);
121-
122-
if( typeof(event.detail.value) === this.type ) {
123-
124-
this.value = event.detail.value;
133+
134+
this.value = event.detail.value;
125135

126-
}
127-
else console.log('ERR: stateVariable - ' + this.name + ' forbidden value type.');
128-
129136
// loop over watchers callbacks
130137
for( let update_callback of this.callbackMap.values()){
131138
update_callback(event.detail.value);

test/stateVariable.test.js

Lines changed: 105 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,110 @@
1-
import {StateVariable, stateBehaviour} from '../build/stateElement.js';
1+
import {StateVariable} from '../build/stateElement.js';
22

33
export default function (){
4-
describe("doesn't crash",()=>{
5-
it("bella",()=>{
6-
let test_state = new StateVariable("test",'string',stateBehaviour['NORMAL'],);
7-
chai.assert.equal(localStorage.getItem('test'), '100', "var is in local storage" );
4+
// this is a must ;)
5+
localStorage.clear();
6+
7+
describe("StateVariable Test:",()=>{
8+
describe("Instantiation",()=>{
9+
10+
it("Instantiated to the store,proper name and initial value",()=>{
11+
let test_string = new StateVariable("test_string",'string',"ciao");
12+
let test_number = new StateVariable("test_number",'number', 7);
13+
let test_object = new StateVariable("test_object",'object',{ciao:"bella", hey:67, poz:["cool", 9]});
14+
let test_bool = new StateVariable("test_bool",'boolean',true);
15+
16+
chai.assert.equal(localStorage.getItem('test_string'), 'ciao', "string not in local storage" );
17+
chai.assert.equal(localStorage.getItem('test_number'), '7', "number not in local storage" );
18+
chai.assert.equal(localStorage.getItem('test_object'), JSON.stringify({ciao:"bella", hey:67, poz:["cool", 9]}), "object not in local storage" );
19+
chai.assert.equal(localStorage.getItem('test_bool'), 'true', "bool not in local storage" );
20+
});
21+
22+
it("If exist do not overide",()=>{
23+
let test_string2 = new StateVariable("test_string",'string',"ciao2");
24+
let test_number2 = new StateVariable("test_number",'number', 14);
25+
let test_object2 = new StateVariable("test_object",'object',{ciao:"bella2", hey:67, poz:["cool", 9]});
26+
let test_bool2 = new StateVariable("test_bool",'boolean',false);
27+
chai.assert.equal(localStorage.getItem('test_string'), 'ciao', "string overridden" );
28+
chai.assert.equal(localStorage.getItem('test_number'), '7', "number overridden" );
29+
chai.assert.equal(localStorage.getItem('test_object'), JSON.stringify({ciao:"bella", hey:67, poz:["cool", 9]}), "object overridden" );
30+
chai.assert.equal(localStorage.getItem('test_bool'), 'true', "bool overridden" );
31+
});
32+
33+
it("throws for wrong init type",()=>{
34+
let pollo;
35+
let test_function = () =>{let a = new StateVariable("test_function",'function',"ciao"); };
36+
let test_function2 = () =>{let a = new StateVariable("test_function",'string',test_function); };
37+
let test_function3 = () =>{let a = new StateVariable("test_function",'bool',true); };
38+
let test_function6 = () =>{let a = new StateVariable("test_function",'boolean',5); };
39+
let test_function5 = () =>{let a = new StateVariable("test_function",'number',true); };
40+
let test_function4 = () =>{let a = new StateVariable("test_function",'string',8); };
41+
let test_function7 = () =>{let a = new StateVariable("test_function",'string',pollo); };
42+
43+
chai.assert.Throw(test_function);
44+
chai.assert.Throw(test_function2);
45+
chai.assert.Throw(test_function3);
46+
chai.assert.Throw(test_function4);
47+
chai.assert.Throw(test_function5);
48+
chai.assert.Throw(test_function6);
49+
chai.assert.Throw(test_function7);
50+
});
851
});
52+
describe('Input Output',()=>{
53+
54+
it("Getter and Setters return proper value and type.",()=>{
55+
let test_string = new StateVariable("test_string",'string',"ciao");
56+
let test_number = new StateVariable("test_number",'number', 7);
57+
let test_object = new StateVariable("test_object",'object',{ciao:"bella", hey:67, poz:["cool", 9]});
58+
let test_bool = new StateVariable("test_bool",'boolean',true);
59+
test_string.value = "pelam123";
60+
test_number.value = 9;
61+
test_object.value = {bim:"bum",bam:8};
62+
test_bool.value = false;
63+
64+
chai.assert.equal(test_string.value, "pelam123", "String " );
65+
chai.assert.equal(test_number.value, 9 , "Number " );
66+
chai.assert.deepEqual(test_object.value,{bim:"bum",bam:8} , "object " );
67+
chai.assert.equal(test_bool.value,false , "boolean " );
68+
});
69+
it("Throws when corrupted, also additional throw test of setter",()=>{
70+
// only number bool and object can be corrupted, strings cant because of performance cut on JSON parse
71+
// Also the throw of set function has been tested already in the init (few more here)
72+
let test_object = new StateVariable("test_object",'object',{ciao:"bella", hey:67, poz:["cool", 9]});
73+
let test_bool = new StateVariable("test_bool",'boolean',true);
74+
let test_number = new StateVariable("test_number",'number',7);
75+
76+
let test_function = () =>{ test_object.value = "fuck";};
77+
let test_function2 = () =>{ test_bool.value = 89;};
78+
let test_function3 = () =>{ test_number.value = undefined;};
79+
80+
chai.assert.Throw(test_function);
81+
chai.assert.Throw(test_function2);
82+
chai.assert.Throw(test_function3);
83+
84+
localStorage.setItem("test_object",'\"ciao\"');
85+
localStorage.setItem("test_bool",'\"ciao\"');
86+
localStorage.setItem("test_number",'\"ciao\"');
87+
let test_function4 = () =>{ let ciao = test_object.value;};
88+
let test_function5 = () =>{ let ciao = test_bool.value;};
89+
let test_function6 = () =>{ let ciao = test_number.value;};
90+
91+
chai.assert.Throw(test_function4);
92+
chai.assert.Throw(test_function5);
93+
chai.assert.Throw(test_function6);
94+
});
95+
96+
});
97+
// Input output for bool, string, object and number
98+
// write/read (getter/setter test) proper value, write read proper type,
99+
// throws when corrupted
100+
101+
// Update_handler
102+
// it does lock the callback, it throws
103+
// it updates the variable, updates only once
104+
// it runs the callbacks, set ad hoc, passes the right modified value
105+
// that unlocks
106+
107+
// Set_auto value willbe tested in Transitions
108+
9109
});
10110
}

0 commit comments

Comments
 (0)