598 lines
17 KiB
JavaScript
598 lines
17 KiB
JavaScript
/*
|
|
* μlogger
|
|
*
|
|
* Copyright(C) 2019 Bartek Fabiszewski (www.fabiszewski.net)
|
|
*
|
|
* This is free software; you can redistribute it and/or modify it under
|
|
* the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful, but
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
import uObserve from '../src/observe.js';
|
|
|
|
describe('Observe tests', () => {
|
|
let object;
|
|
let result = false;
|
|
let resultValue;
|
|
|
|
beforeEach(() => {
|
|
object = { observed: 1, nonObserved: 1 };
|
|
result = false;
|
|
// eslint-disable-next-line no-undefined
|
|
resultValue = undefined;
|
|
});
|
|
|
|
describe('when object is observed', () => {
|
|
|
|
it('should throw error if observer is missing', () => {
|
|
expect(() => { uObserve.observe(object, 'observed'); }).toThrow(new Error('Invalid argument for observe'));
|
|
});
|
|
|
|
it('should notify observers when observed property is modified', () => {
|
|
// given
|
|
uObserve.observe(object, 'observed', (value) => {
|
|
result = true;
|
|
resultValue = value;
|
|
});
|
|
// when
|
|
expect(result).toBe(false);
|
|
object.observed = 2;
|
|
// then
|
|
expect(result).toBe(true);
|
|
expect(resultValue).toBe(2);
|
|
});
|
|
|
|
it('should notify multiple observers when observed property is modified', () => {
|
|
// given
|
|
let result2 = false;
|
|
let resultValue2;
|
|
uObserve.observe(object, 'observed', (value) => {
|
|
result = true;
|
|
resultValue = value;
|
|
});
|
|
uObserve.observe(object, 'observed', (value) => {
|
|
result2 = true;
|
|
resultValue2 = value;
|
|
});
|
|
// when
|
|
expect(result).toBe(false);
|
|
expect(result2).toBe(false);
|
|
object.observed = 2;
|
|
// then
|
|
expect(result).toBe(true);
|
|
expect(resultValue).toBe(2);
|
|
expect(result2).toBe(true);
|
|
// noinspection JSUnusedAssignment
|
|
expect(resultValue2).toBe(2);
|
|
});
|
|
|
|
it('should not notify observers when non-observed property is modified', () => {
|
|
// given
|
|
uObserve.observe(object, 'observed', () => {
|
|
result = true;
|
|
});
|
|
// when
|
|
expect(result).toBe(false);
|
|
object.nonObserved = 2;
|
|
// then
|
|
expect(result).toBe(false);
|
|
});
|
|
|
|
it('should not notify observers when modified value is same', () => {
|
|
// given
|
|
uObserve.observe(object, 'observed', () => {
|
|
result = true;
|
|
});
|
|
// when
|
|
expect(result).toBe(false);
|
|
object.observed = 1;
|
|
// then
|
|
expect(result).toBe(false);
|
|
});
|
|
|
|
it('should notify observers when any property is modified', () => {
|
|
// given
|
|
uObserve.observe(object, (value) => {
|
|
result = true;
|
|
resultValue = value;
|
|
});
|
|
// when
|
|
expect(result).toBe(false);
|
|
object.observed = 2;
|
|
// then
|
|
expect(result).toBe(true);
|
|
expect(resultValue).toBe(2);
|
|
|
|
// given
|
|
result = false;
|
|
resultValue = null;
|
|
|
|
// when
|
|
expect(result).toBe(false);
|
|
object.nonObserved = 2;
|
|
// then
|
|
expect(result).toBe(true);
|
|
expect(resultValue).toBe(2);
|
|
});
|
|
|
|
it('should notify observers when observed array property is modified', () => {
|
|
// given
|
|
const array = [ 1, 2 ];
|
|
object = { array: array };
|
|
uObserve.observe(object, 'array', (value) => {
|
|
result = true;
|
|
resultValue = value;
|
|
});
|
|
// when
|
|
expect(result).toBe(false);
|
|
array.push(3);
|
|
// then
|
|
expect(result).toBe(true);
|
|
expect(resultValue).toEqual(array);
|
|
});
|
|
|
|
it('should notify observers when observed array is modified', () => {
|
|
// given
|
|
const array = [ 1, 2 ];
|
|
uObserve.observe(array, (value) => {
|
|
result = true;
|
|
resultValue = value;
|
|
});
|
|
// when
|
|
expect(result).toBe(false);
|
|
array.push(3);
|
|
// then
|
|
expect(result).toBe(true);
|
|
expect(resultValue).toEqual(array);
|
|
});
|
|
|
|
it('should retain observers after array is reassigned', () => {
|
|
// given
|
|
let result2 = false;
|
|
let resultValue2;
|
|
const array = [ 1, 2 ];
|
|
const newArray = [ 3, 4 ];
|
|
object = { array: array };
|
|
uObserve.observe(object, 'array', (value) => {
|
|
result = true;
|
|
resultValue = value;
|
|
});
|
|
uObserve.observe(object, 'array', (value) => {
|
|
result2 = true;
|
|
resultValue2 = value;
|
|
});
|
|
// when
|
|
object.array = newArray;
|
|
result = false;
|
|
result2 = false;
|
|
|
|
expect(result).toBe(false);
|
|
expect(result2).toBe(false);
|
|
object.array.push(5);
|
|
// then
|
|
expect(result).toBe(true);
|
|
expect(result2).toBe(true);
|
|
expect(resultValue).toEqual(newArray);
|
|
// noinspection JSUnusedAssignment
|
|
expect(resultValue2).toEqual(newArray);
|
|
});
|
|
|
|
it('should retain observers after array property is silently set', () => {
|
|
// given
|
|
let result2 = false;
|
|
let resultValue2;
|
|
const array = [ 1, 2 ];
|
|
const newArray = [ 3, 4 ];
|
|
object = { array: [] };
|
|
uObserve.observe(object, 'array', (value) => {
|
|
result = true;
|
|
resultValue = value;
|
|
});
|
|
uObserve.observe(object, 'array', (value) => {
|
|
result2 = true;
|
|
resultValue2 = value;
|
|
});
|
|
// when
|
|
uObserve.setSilently(object, 'array', array);
|
|
object.array = newArray;
|
|
result = false;
|
|
result2 = false;
|
|
|
|
expect(result).toBe(false);
|
|
expect(result2).toBe(false);
|
|
object.array.push(5);
|
|
// then
|
|
expect(result).toBe(true);
|
|
expect(result2).toBe(true);
|
|
expect(resultValue).toEqual(newArray);
|
|
// noinspection JSUnusedAssignment
|
|
expect(resultValue2).toEqual(newArray);
|
|
});
|
|
});
|
|
|
|
describe('when object is unobserved', () => {
|
|
|
|
it('should throw error if removed observer is missing', () => {
|
|
expect(() => {
|
|
uObserve.unobserve(object, 'unobserved');
|
|
}).toThrow(new Error('Invalid argument for unobserve'));
|
|
});
|
|
|
|
it('should not notify observers when unobserved property is modified', () => {
|
|
// given
|
|
const observer = (value) => {
|
|
result = true;
|
|
resultValue = value;
|
|
};
|
|
uObserve.observe(object, 'observed', observer);
|
|
// when
|
|
uObserve.unobserve(object, 'observed', observer);
|
|
|
|
expect(result).toBe(false);
|
|
object.observed = 2;
|
|
// then
|
|
expect(result).toBe(false);
|
|
// eslint-disable-next-line no-undefined
|
|
expect(resultValue).toBe(undefined);
|
|
expect(object.observed).toBe(2);
|
|
});
|
|
|
|
it('should not notify observers when any unobserved object property is modified', () => {
|
|
// given
|
|
const observer = (value) => {
|
|
result = true;
|
|
resultValue = value;
|
|
};
|
|
uObserve.observe(object, observer);
|
|
// when
|
|
uObserve.unobserve(object, observer);
|
|
|
|
expect(result).toBe(false);
|
|
object.observed = 2;
|
|
// then
|
|
expect(result).toBe(false);
|
|
// eslint-disable-next-line no-undefined
|
|
expect(resultValue).toBe(undefined);
|
|
expect(object.observed).toBe(2);
|
|
|
|
// given
|
|
result = false;
|
|
// eslint-disable-next-line no-undefined
|
|
resultValue = undefined;
|
|
|
|
// when
|
|
expect(result).toBe(false);
|
|
object.nonObserved = 2;
|
|
// then
|
|
expect(result).toBe(false);
|
|
// eslint-disable-next-line no-undefined
|
|
expect(resultValue).toBe(undefined);
|
|
expect(object.nonObserved).toBe(2);
|
|
});
|
|
|
|
it('should not notify observers when unobserved array property is modified', () => {
|
|
// given
|
|
const observer = (value) => {
|
|
result = true;
|
|
resultValue = value;
|
|
};
|
|
const array = [ 1, 2 ];
|
|
object = { array: array };
|
|
uObserve.observe(object, 'array', observer);
|
|
// when
|
|
uObserve.unobserve(object, 'array', observer);
|
|
|
|
expect(result).toBe(false);
|
|
array.push(3);
|
|
// then
|
|
expect(result).toBe(false);
|
|
// eslint-disable-next-line no-undefined
|
|
expect(resultValue).toEqual(undefined);
|
|
expect(array).toEqual([ 1, 2, 3 ]);
|
|
});
|
|
|
|
it('should not notify observers when unobserved array is modified', () => {
|
|
// given
|
|
const observer = (value) => {
|
|
result = true;
|
|
resultValue = value;
|
|
};
|
|
const array = [ 1, 2 ];
|
|
uObserve.observe(array, observer);
|
|
// when
|
|
uObserve.unobserve(array, observer);
|
|
|
|
expect(result).toBe(false);
|
|
array.push(3);
|
|
// then
|
|
expect(result).toBe(false);
|
|
// eslint-disable-next-line no-undefined
|
|
expect(resultValue).toEqual(undefined);
|
|
expect(array).toEqual([ 1, 2, 3 ]);
|
|
});
|
|
|
|
it('should remove one of two observers of object property', () => {
|
|
// given
|
|
let result2 = false;
|
|
let resultValue2;
|
|
const observer = (value) => {
|
|
result = true;
|
|
resultValue = value;
|
|
};
|
|
const observer2 = (value) => {
|
|
result2 = true;
|
|
resultValue2 = value;
|
|
};
|
|
uObserve.observe(object, 'observed', observer);
|
|
uObserve.observe(object, 'observed', observer2);
|
|
// when
|
|
uObserve.unobserve(object, 'observed', observer2);
|
|
|
|
expect(result).toBe(false);
|
|
expect(result2).toBe(false);
|
|
object.observed = 2;
|
|
// then
|
|
expect(result).toBe(true);
|
|
expect(resultValue).toBe(2);
|
|
expect(result2).toBe(false);
|
|
// noinspection JSUnusedAssignment
|
|
expect(resultValue2).toBe(undefined);// eslint-disable-line no-undefined
|
|
});
|
|
|
|
it('should remove one of two observers from array', () => {
|
|
// given
|
|
let result2 = false;
|
|
let resultValue2;
|
|
const observer = (value) => {
|
|
result = true;
|
|
resultValue = value;
|
|
};
|
|
const observer2 = (value) => {
|
|
result2 = true;
|
|
resultValue2 = value;
|
|
};
|
|
const array = [ 1, 2 ];
|
|
uObserve.observe(array, observer);
|
|
uObserve.observe(array, observer2);
|
|
// when
|
|
uObserve.unobserve(array, observer2);
|
|
|
|
expect(result).toBe(false);
|
|
expect(result2).toBe(false);
|
|
array.push(3);
|
|
// then
|
|
expect(result).toBe(true);
|
|
expect(result2).toBe(false);
|
|
expect(resultValue).toEqual(array);
|
|
// noinspection JSUnusedAssignment
|
|
expect(resultValue2).toEqual(undefined);// eslint-disable-line no-undefined
|
|
expect(array).toEqual([ 1, 2, 3 ]);
|
|
});
|
|
|
|
it('should remove all observers of object property', () => {
|
|
// given
|
|
let result2 = false;
|
|
let resultValue2;
|
|
const observer = (value) => {
|
|
result = true;
|
|
resultValue = value;
|
|
};
|
|
const observer2 = (value) => {
|
|
result2 = true;
|
|
resultValue2 = value;
|
|
};
|
|
uObserve.observe(object, 'observed', observer);
|
|
uObserve.observe(object, 'observed', observer2);
|
|
// when
|
|
uObserve.unobserveAll(object, 'observed');
|
|
|
|
expect(result).toBe(false);
|
|
expect(result2).toBe(false);
|
|
object.observed = 2;
|
|
// then
|
|
expect(result).toBe(false);
|
|
expect(resultValue).toBe(undefined);// eslint-disable-line no-undefined
|
|
expect(result2).toBe(false);
|
|
// noinspection JSUnusedAssignment
|
|
expect(resultValue2).toBe(undefined);// eslint-disable-line no-undefined
|
|
expect(object.observed).toBe(2);
|
|
});
|
|
|
|
it('should remove all observers from array property', () => {
|
|
// given
|
|
let result2 = false;
|
|
let resultValue2;
|
|
const observer = (value) => {
|
|
result = true;
|
|
resultValue = value;
|
|
};
|
|
const observer2 = (value) => {
|
|
result2 = true;
|
|
resultValue2 = value;
|
|
};
|
|
const array = [ 1, 2 ];
|
|
object.arr = array;
|
|
uObserve.observe(object, 'arr', observer);
|
|
uObserve.observe(object, 'arr', observer2);
|
|
// when
|
|
uObserve.unobserveAll(object, 'arr');
|
|
|
|
expect(result).toBe(false);
|
|
expect(result2).toBe(false);
|
|
array.push(3);
|
|
// then
|
|
expect(result).toBe(false);
|
|
expect(result2).toBe(false);
|
|
expect(resultValue).toEqual(undefined);// eslint-disable-line no-undefined
|
|
// noinspection JSUnusedAssignment
|
|
expect(resultValue2).toEqual(undefined);// eslint-disable-line no-undefined
|
|
expect(object.arr).toEqual([ 1, 2, 3 ]);
|
|
});
|
|
|
|
it('should remove all observers of all object properties', () => {
|
|
// given
|
|
let result2 = false;
|
|
let resultValue2;
|
|
const observer = (value) => {
|
|
result = true;
|
|
resultValue = value;
|
|
};
|
|
const observer2 = (value) => {
|
|
result2 = true;
|
|
resultValue2 = value;
|
|
};
|
|
object.observed2 = null;
|
|
uObserve.observe(object, 'observed', observer);
|
|
uObserve.observe(object, 'observed2', observer2);
|
|
// when
|
|
uObserve.unobserveAll(object);
|
|
|
|
expect(result).toBe(false);
|
|
expect(result2).toBe(false);
|
|
object.observed = 2;
|
|
object.observed2 = 2;
|
|
// then
|
|
expect(result).toBe(false);
|
|
expect(resultValue).toBe(undefined);// eslint-disable-line no-undefined
|
|
expect(result2).toBe(false);
|
|
// noinspection JSUnusedAssignment
|
|
expect(resultValue2).toBe(undefined);// eslint-disable-line no-undefined
|
|
expect(object.observed).toBe(2);
|
|
expect(object.observed2).toBe(2);
|
|
});
|
|
|
|
it('should throw error when observing non-existing property', () => {
|
|
// given
|
|
const nonExisting = '___non-existing___';
|
|
|
|
expect(object.hasOwnProperty(nonExisting)).toBe(false);
|
|
// then
|
|
expect(() => uObserve.observe(object, nonExisting, (value) => {
|
|
result = true;
|
|
resultValue = value;
|
|
})).toThrow();
|
|
|
|
expect(object.hasOwnProperty(nonExisting)).toBe(false);
|
|
});
|
|
|
|
it('should throw error when observing non-object', () => {
|
|
// given
|
|
const nonExisting = '___non-existing___';
|
|
// then
|
|
expect(() => uObserve.observe(nonExisting, (value) => {
|
|
result = true;
|
|
resultValue = value;
|
|
})).toThrow();
|
|
});
|
|
|
|
it('should throw error when observing null object', () => {
|
|
// given
|
|
const nullObject = null;
|
|
// then
|
|
expect(() => uObserve.observe(nullObject, (value) => {
|
|
result = true;
|
|
resultValue = value;
|
|
})).toThrow();
|
|
});
|
|
|
|
it('should not notify observers when observed property is silently changed', () => {
|
|
// given
|
|
uObserve.observe(object, 'observed', (value) => {
|
|
result = true;
|
|
resultValue = value;
|
|
});
|
|
// when
|
|
expect(result).toBe(false);
|
|
uObserve.setSilently(object, 'observed', 2);
|
|
// then
|
|
expect(result).toBe(false);
|
|
// eslint-disable-next-line no-undefined
|
|
expect(resultValue).toBe(undefined);
|
|
});
|
|
|
|
it('should return true if property is observed', () => {
|
|
// when
|
|
uObserve.observe(object, 'observed', (value) => {
|
|
result = true;
|
|
resultValue = value;
|
|
});
|
|
// then
|
|
expect(uObserve.isObserved(object, 'observed')).toBe(true);
|
|
});
|
|
|
|
it('should return false if property is not observed', () => {
|
|
// when
|
|
uObserve.observe(object, 'observed', (value) => {
|
|
result = true;
|
|
resultValue = value;
|
|
});
|
|
// then
|
|
expect(uObserve.isObserved(object, 'nonObserved')).toBe(false);
|
|
});
|
|
|
|
it('should return true if array property is observed', () => {
|
|
// when
|
|
const array = [ 1, 2 ];
|
|
object = { array: array };
|
|
uObserve.observe(object, 'array', (value) => {
|
|
result = true;
|
|
resultValue = value;
|
|
});
|
|
// then
|
|
expect(uObserve.isObserved(object, 'array')).toBe(true);
|
|
});
|
|
|
|
it('should return false if property is unobserved', () => {
|
|
// when
|
|
const observer = (value) => {
|
|
result = true;
|
|
resultValue = value;
|
|
};
|
|
uObserve.observe(object, 'observed', observer);
|
|
uObserve.unobserve(object, 'observed', observer);
|
|
// then
|
|
expect(uObserve.isObserved(object, 'observed')).toBe(false);
|
|
});
|
|
|
|
it('should return true if property is observed by given observer', () => {
|
|
// when
|
|
const observer = (value) => {
|
|
result = true;
|
|
resultValue = value;
|
|
};
|
|
const observer2 = () => {/* ignored */};
|
|
uObserve.observe(object, 'observed', observer);
|
|
// then
|
|
expect(uObserve.isObserved(object, 'observed', observer)).toBe(true);
|
|
expect(uObserve.isObserved(object, 'observed', observer2)).toBe(false);
|
|
});
|
|
});
|
|
|
|
describe('when notify is called directly', () => {
|
|
it('should call observers with given value', () => {
|
|
// given
|
|
const observers = new Set();
|
|
let result2 = false;
|
|
observers.add((value) => { result = value; });
|
|
observers.add((value) => { result2 = value; });
|
|
// when
|
|
expect(result).toBe(false);
|
|
expect(result2).toBe(false);
|
|
uObserve.notify(observers, true);
|
|
// then
|
|
expect(result).toBe(true);
|
|
expect(result2).toBe(true);
|
|
});
|
|
|
|
});
|
|
});
|