Select Git revision
updater.mjs
updater.mjs 26.37 KiB
'use strict';
import chai from "chai"
import {ID} from "../../../source/types/id.mjs";
import {Observer} from "../../../source/types/observer.mjs";
import {ProxyObserver} from "../../../source/types/proxyobserver.mjs";
import {chaiDom} from "../../util/chai-dom.mjs";
import {initJSDOM} from "../../util/jsdom.mjs";
let expect = chai.expect;
chai.use(chaiDom);
let html1 = `
<template id="current">
<li data-monster-replace="path:current | tojson"></li>
</template>
<div id="test1">
<ul data-monster-insert="current path:a.b">
</ul>
</div>
<div id="test2">
<ul data-monster-insert="current path:a.b | doit">
</ul>
</div>
<div id="test3">
<div data-monster-attributes="class path:a.b">
<input data-monster-attributes="value path:a.c" id="input1">
<input data-monster-attributes="checked path:a.checkbox" type="checkbox" name="checkbox" id="checkbox">
<input data-monster-attributes="value path:a.text" type="text" name="text" id="text">
<input data-monster-attributes="checked path:a.radio" type="radio" name="radio" value="r1" id="radio">
<input type="radio" name="radio" value="r2" id="r2">
<input type="radio" name="radio" value="rx" id="rx">
<select data-monster-attributes="value path:a.select" name="select" id="select">
<option value="other-value">value1</option>
<option>value2</option>
</select>
<select data-monster-attributes="value path:a.multiselect" name="multiselect" multiple id="multiselect">
<option>value1</option>
<option>value2</option>
<option>value3</option>
<option>value4</option>
<option value="other-value5">value5</option>
</select>
<textarea name="textarea" id="textarea" data-monster-attributes="value path:a.textarea"></textarea>
</div>
</div>
`;
let html2 = `
<div id="test1">
<div data-monster-replace="path:text | tolower"></div>
<div data-monster-replace="path:text | call:myformatter"></div>
<div data-monster-replace="static:hello\\ "></div>
</div>
`;
let html3 = `
<template id="myinnerid">
<span data-monster-replace="path:myinnerid | tojson"></span>
</template>
<template id="myid">
<p data-monster-insert="myinnerid path:a.b"></p>
</template>
<div id="test1">
<div data-monster-insert="myid path:a.b"></div>
</div>
`;
let html4 = `
<div>
<form id="form1">
<input type="checkbox" value="checked" name="checkbox" data-monster-bind="path:state">
<input type="text" name="text">
<input type="radio" name="radio" value="r1" id="r1" data-monster-bind="path:radio">
<input type="radio" name="radio" value="r2" id="r2" data-monster-bind="path:radio">
<input type="radio" name="radio" value="rx" id="rx" data-comment="not called because no bind attribute">
<input type="button" name="button">
<select name="select1" id="select1" data-monster-bind="path:select">
<option>value1</option>
<option>value2</option>
</select>
<select name="select2" multiple id="select2" data-monster-bind="path:multiselect">
<option>value1</option>
<option>value2</option>
<option>value3</option>
<option>value4</option>
<option>value5</option>
</select>
<textarea name="textarea" id="textarea" data-monster-bind="path:textarea">
</textarea>
</form>
</div>
`;
describe('DOM', function () {
let Updater = null;
before(function (done) {
const options = {
}
initJSDOM(options).then(() => {
import("../../../source/dom/updater.mjs").then((m) => {
Updater = m.Updater;
done();
}).catch((e) => {
done(e)
});
});
});
beforeEach(() => {
let mocks = document.getElementById('mocks');
mocks.innerHTML = html1;
})
afterEach(() => {
let mocks = document.getElementById('mocks');
mocks.innerHTML = "";
})
describe('Updater()', function () {
describe('test Getter && Setter', function () {
it('setEventTypes()', function () {
let element = document.getElementById('test1')
expect(new Updater(element).setEventTypes(['touch'])).to.be.instanceof(Updater);
})
it('getSubject()', function () {
let element = document.getElementById('test1')
let subject = {a: 1};
expect(new Updater(element, subject).getSubject().a).to.be.equal(1);
})
});
describe('test control methods', function () {
it('enableEventProcessing()', function () {
let element = document.getElementById('test1')
expect(new Updater(element).enableEventProcessing()).to.be.instanceof(Updater);
})
it('disableEventProcessing()', function () {
let element = document.getElementById('test1')
expect(new Updater(element).disableEventProcessing()).to.be.instanceof(Updater);
})
});
describe('test Errors', function () {
it('should throw value is not an instance of HTMLElement Error', function () {
expect(() => new Updater()).to.throw(TypeError)
})
it('should throw value is wrong', function () {
let element = document.getElementById('test1')
expect(() => new Updater(element, null)).to.throw(TypeError)
})
it('should throw Error: the value is not iterable', function (done) {
let element = document.getElementById('test1')
let u = new Updater(
element,
{
a: {
x: []
}
}
);
let promise = u.run();
setTimeout(() => {
promise.then(() => {
setTimeout(() => {
done(new Error("should never called!"));
}, 100);
}).catch((e) => {
expect(e).is.instanceOf(Error);
expect(e + "").to.be.equal('Error: the value is not iterable');
done();
})
}, 100);
});
});
});
describe('Updater()', function () {
describe('new Updater', function () {
it('should return document object', function () {
let element = document.getElementById('test1')
let d = new Updater(
element,
{}
);
expect(typeof d).is.equal('object');
});
});
});
describe('Updater()', function () {
describe('Repeat', function () {
it('should build 6 li elements', function (done) {
let element = document.getElementById('test1')
let d = new Updater(
element,
{
a: {
b: [
{i: '0'},
{i: '1'},
{i: '2'},
{i: '3'},
{i: '4'},
{i: '5'},
]
}
}
);
d.run().then(() => {
setTimeout(() => {
expect(typeof d).is.equal('object');
for (let i = 0; i < 6; i++) {
expect(element).contain.html('<li data-monster-replace="path:a.b.' + i + ' | tojson" data-monster-insert-reference="current-' + i + '">{"i":"' + i + '"}</li>');
}
done();
}, 100);
}).catch(
e => {
done(new Error(e))
})
});
});
});
describe('Updater()', function () {
beforeEach(() => {
let mocks = document.getElementById('mocks');
mocks.innerHTML = html4;
})
describe('Eventhandling', function () {
let updater, form1, proxyobserver;
beforeEach(() => {
proxyobserver = new ProxyObserver({})
updater = new Updater(document.getElementById('form1'), proxyobserver);
form1 = document.getElementById('form1');
})
// here click events are thrown on the checkbox and the setting of the value is observed via the proxyobserver.
it('should handle checkbox click events', function (done) {
updater.enableEventProcessing();
let subject = updater.getSubject();
expect(subject).is.equal(proxyobserver.getSubject());
let expected = ['checked', undefined, 'checked'];
// here the notation with function is important, because the pointer is set.
proxyobserver.attachObserver(new Observer(function () {
let e = expected.shift();
if (e === undefined && expected.length === 0) done(new Error('to many calls'));
if (this.getSubject()['state'] !== e) done(new Error(this.getSubject()['state'] + ' should ' + e));
if (expected.length === 0) {
done();
} else {
setTimeout(() => {
form1.querySelector('[name=checkbox]').click();
}, 10)
}
}));
setTimeout(() => {
form1.querySelector('[name=checkbox]').click();
}, 10)
})
it('should handle radio click events 1', function (done) {
updater.enableEventProcessing();
let subject = updater.getSubject();
expect(subject).is.equal(proxyobserver.getSubject());
let expected = ['r1', 'r2', 'r1'];
let clickTargets = ['r2', 'r1']
// here the notation with function is important, because the this pointer is set.
proxyobserver.attachObserver(new Observer(function () {
let e = expected.shift();
if (e === undefined && expected.length === 0) done(new Error('to many calls'));
let v = this.getSubject()['radio'];
if (v !== e) done(new Error(v + ' should ' + e));
if (expected.length === 0) {
done();
} else {
setTimeout(() => {
document.getElementById(clickTargets.shift()).click();
}, 10)
}
}));
setTimeout(() => {
document.getElementById('r1').click();
}, 10)
// no handler // bind
setTimeout(() => {
document.getElementById('rx').click();
}, 20)
})
it('should handle select click events 2', function (done) {
let selectElement = document.getElementById('select1');
updater.enableEventProcessing();
let subject = updater.getSubject();
expect(subject).is.equal(proxyobserver.getSubject());
let expected = ['value2', 'value1', 'value2'];
// here the notation with function is important, because the this pointer is set.
proxyobserver.attachObserver(new Observer(function () {
let e = expected.shift();
if (e === undefined && expected.length === 0) done(new Error('to many calls'));
let v = this.getSubject()['select'];
if (v !== e) done(new Error(v + ' should ' + e));
if (expected.length === 0) {
done();
} else {
setTimeout(() => {
selectElement.selectedIndex = selectElement.selectedIndex === 1 ? 0 : 1;
selectElement.click();
}, 10)
}
}));
setTimeout(() => {
// set value and simulate click event for bubble
selectElement.selectedIndex = 1;
selectElement.click();
}, 20)
});
it('should handle textarea events', function (done) {
let textareaElement = document.getElementById('textarea');
updater.enableEventProcessing();
let subject = updater.getSubject();
expect(subject).is.equal(proxyobserver.getSubject());
let expected = ['testX', 'lorem ipsum', ''];
let testValues = ["lorem ipsum", ""];
// here the notation with function is important, because the this pointer is set.
proxyobserver.attachObserver(new Observer(function () {
let e = expected.shift();
if (e === undefined && expected.length === 0) done(new Error('to many calls'));
let v = this.getSubject()['textarea'];
if (JSON.stringify(v) !== JSON.stringify(e)) done(new Error(JSON.stringify(v) + ' should ' + JSON.stringify(e)));
if (expected.length === 0) {
done();
} else {
setTimeout(() => {
textareaElement.value = testValues.shift();
textareaElement.click();
}, 10)
}
}));
setTimeout(() => {
// set value and simulate click event for bubble
textareaElement.value = "testX";
textareaElement.click();
}, 20)
});
it('should handle multiple select events', function (done) {
let selectElement = document.getElementById('select2');
updater.enableEventProcessing();
let subject = updater.getSubject();
expect(subject).is.equal(proxyobserver.getSubject());
let expected = [
['value1'],
['value2', 'value3', 'value4'],
['value1', 'value4'],
];
let testSelections = [
[false, true, true, true],
[true, false, false, true],
]
// here the notation with function is important, because the this pointer is set.
proxyobserver.attachObserver(new Observer(function () {
let e = expected.shift();
if (e === undefined && expected.length === 0) done(new Error('to many calls'));
let v = this.getSubject()['multiselect'];
if (JSON.stringify(v) !== JSON.stringify(e)) done(new Error(JSON.stringify(v) + ' should ' + JSON.stringify(e)));
if (expected.length === 0) {
done();
} else {
setTimeout(() => {
let v = testSelections.shift();
selectElement.options[0].selected = v[0];
selectElement.options[1].selected = v[1];
selectElement.options[2].selected = v[2];
selectElement.options[3].selected = v[3];
selectElement.click();
}, 10)
}
}));
setTimeout(() => {
selectElement.options[0].selected = true;
selectElement.options[1].selected = false;
selectElement.options[2].selected = false;
selectElement.options[3].selected = false;
selectElement.click();
}, 20)
});
});
})
describe('Updater()', function () {
beforeEach(() => {
let mocks = document.getElementById('mocks');
mocks.innerHTML = html2;
})
describe('Replace', function () {
it('should add lower hello and HELLOyes!', function (done) {
let element = document.getElementById('test1')
let d = new Updater(
element,
{
text: "HALLO"
}
);
d.setCallback('myformatter', function (a) {
return a + 'yes!'
})
d.run().then(() => {
setTimeout(() => {
expect(typeof d).is.equal('object');
expect(element).contain.html('<div data-monster-replace="path:text | tolower">hallo</div>');
expect(element).contain.html('<div data-monster-replace="path:text | call:myformatter">HALLOyes!</div>');
expect(element).contain.html('<div data-monster-replace="static:hello\\ ">hello </div>');
return done();
}, 100);
}).catch(
e => {
done(new Error(e))
})
});
});
});
describe('Updater()', function () {
beforeEach(() => {
let mocks = document.getElementById('mocks');
mocks.innerHTML = html3;
})
describe('Replace', function () {
it('should ', function (done) {
let element = document.getElementById('test1')
let d = new Updater(
element,
{
a: {
b: [
{i: '0'},
]
}
}
);
d.run().then(() => {
setTimeout(() => {
expect(typeof d).is.equal('object');
expect(element).contain.html('<div data-monster-insert="myid path:a.b">');
expect(element).contain.html('<p data-monster-insert="myinnerid path:a.b" data-monster-insert-reference="myid-0">');
expect(element).contain.html('<span data-monster-replace="path:a.b.0 | tojson" data-monster-insert-reference="myinnerid-0">{"i":"0"}</span>');
done();
}, 100);
}).catch(
e => {
done(new Error(e))
})
});
});
});
describe('Updater()', function () {
describe('Attributes', function () {
it('should change attributes', function (done) {
let element = document.getElementById('test3')
let text = document.getElementById('text')
expect(text.value).to.be.equal("");
let radio = document.getElementById('radio')
expect(radio.checked).to.be.false;
let checkbox = document.getElementById('checkbox')
expect(checkbox.checked).to.be.false;
let select = document.getElementById('select')
expect(select.selectedIndex).to.be.equal(0);
let multiselect = document.getElementById('multiselect')
expect(multiselect.selectedIndex).to.be.equal(-1);
let textarea = document.getElementById('textarea')
expect(textarea.value).to.be.equal("");
let d = new Updater(
element,
{
a: {
b: "div-class",
c: "hello",
text: "hello",
radio: "true",
textarea: "test",
multiselect: ['value3', 'value4', 'other-value5'],
select: "value2",
checkbox: "true"
}
}
);
d.run().then(() => {
setTimeout(() => {
expect(element).contain.html('<div data-monster-attributes="class path:a.b" class="div-class">');
expect(element).contain.html('<input data-monster-attributes="value path:a.c" id="input1" value="hello">');
expect(element).contain.html('<textarea name="textarea" id="textarea" data-monster-attributes="value path:a.textarea" value="test">');
expect(element).contain.html('<input data-monster-attributes="checked path:a.radio" type="radio" name="radio" value="r1" id="radio" checked="true">');
expect(text.value, 'text control').to.be.equal(d.getSubject()['a']['c']);
expect(radio.checked, 'radio control').to.be.equal(true);
expect(textarea.value, 'textarea control').to.be.equal(d.getSubject()['a']['textarea']);
expect(select.selectedIndex, 'select control').to.be.equal(1); // [0=>other-value, 1=>value2]
let multiselectSelectedOptions = [];
for (const [index, obj] of Object.entries(multiselect.selectedOptions)) {
multiselectSelectedOptions.push(obj.value);
}
expect(JSON.stringify(multiselectSelectedOptions), 'multiselect control').to.be.equal(JSON.stringify(d.getSubject()['a']['multiselect']));
expect(checkbox.checked, 'checkbox control').to.be.true;
done();
}, 100);
}).catch(
e => {
done(new Error(e))
})
});
});
});
describe('Get Attribute Pipe', function () {
let id, mocks;
beforeEach(() => {
mocks = document.getElementById('mocks');
id = new ID('monster');
mocks.innerHTML = ` <div id="` + id + `"
data-monster-replace="path:a | if:value:\\ "></div>`
})
afterEach(() => {
mocks.innerHTML = "";
})
it('should include space', function () {
const div = document.getElementById(id.toString())
const pipe = div.getAttribute('data-monster-replace');
expect(pipe.length).to.be.equal(20);
});
});
describe('manuel update', function () {
let id, mocks;
beforeEach(() => {
mocks = document.getElementById('mocks');
id = new ID('monster').toString();
mocks.innerHTML = `<input id="` + id + `"data-monster-bind="path:myvalue">`
})
afterEach(() => {
mocks.innerHTML = "";
})
it('should get value', function () {
document.getElementById(id).value = "hello";
const updater = new Updater(mocks);
const subject = updater.getSubject();
expect(subject).to.not.have.property('myvalue');
updater.retrieve();
expect(subject).to.have.property('myvalue');
});
});
/**
* https://gitlab.schukai.com/oss/libraries/javascript/monster/-/issues/112
*/
describe('Updater() 20220107', function () {
beforeEach(() => {
let mocks = document.getElementById('mocks');
// language=HTML
mocks.innerHTML = `
<div id="container">
<div data-monster-replace="path:content"></div>
</div>
`;
})
describe('Bugfix #112', function () {
it('should add ', function (done) {
let containerElement = document.getElementById('container');
let newElement = document.createElement('div');
newElement.innerHTML = 'yeah! <b>Test</b>!';
const containerHTML = containerElement.innerHTML;
const newHTML = newElement.innerHTML;
let d = new Updater(
containerElement,
{
content: newElement
}
);
setTimeout(() => {
d.run().then(() => {
setTimeout(() => {
try {
expect(containerElement).contain.html('<div>yeah! <b>Test</b>!</div>');
} catch (e) {
return done(e);
}
done()
}, 100)
})
}, 100)
// d.setCallback('myformatter', function (a) {
// return a + 'yes!'
// })
//
// setTimeout(() => {
// d.run().then(() => {
//
// expect(typeof d).is.equal('object');
// expect(element).contain.html('<div data-monster-replace="path:text | tolower">hallo</div>');
// expect(element).contain.html('<div data-monster-replace="path:text | call:myformatter">HALLOyes!</div>');
// expect(element).contain.html('<div data-monster-replace="static:hello\\ ">hello </div>');
//
// return done();
// }).catch(
// e => {
// done(new Error(e))
// })
// }, 100)
});
});
});
});