Skip to content
Snippets Groups Projects
Select Git revision
  • 1f1348aece0ecf4b445d0640bb58dda53b882405
  • master default protected
  • v1.23.2
  • v1.23.1
  • v1.23.0
  • v1.22.0
  • v1.21.1
  • v1.21.0
  • v1.20.3
  • v1.20.2
  • v1.20.1
  • v1.20.0
  • v1.19.4
  • v1.19.3
  • v1.19.2
  • v1.19.1
  • v1.19.0
  • v1.18.2
  • v1.18.1
  • v1.18.0
  • v1.17.0
  • v1.16.1
22 results

runnable_test.go

Blame
  • 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)
    
                });
            });
        });
    
    
    });