Skip to content
Snippets Groups Projects
Select Git revision
  • master default protected
  • 1.31
  • 4.15.1
  • 4.15.0
  • 4.14.0
  • 4.13.1
  • 4.13.0
  • 4.12.0
  • 4.11.1
  • 4.11.0
  • 4.10.4
  • 4.10.3
  • 4.10.2
  • 4.10.1
  • 4.10.0
  • 4.9.0
  • 4.8.0
  • 4.7.0
  • 4.6.1
  • 4.6.0
  • 4.5.1
  • 4.5.0
22 results

customelement.mjs

Blame
  • customelement.mjs 20.61 KiB
    'use strict';
    
    import * as chai from 'chai';
    import {internalSymbol} from "../../../source/constants.mjs";
    import {getDocument} from "../../../source/dom/util.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 = `
        <div id="test1">
        </div>
    `;
    
    let html2 = `
        <input data-monster-bind="path:a" id="test2" data-monster-attributes="value path:a">
    `;
    
    // defined in constants.mjs
    const updaterSymbolKey = "@schukai/monster/dom/custom-element@@options-updater-link"
    const updaterSymbolSymbol = Symbol.for(updaterSymbolKey);
    
    
    describe('DOM', function () {
    
        let CustomElement, registerCustomElement, TestComponent, document, TestComponent2, assignUpdaterToElement,
            addObjectWithUpdaterToElement;
    
        describe("assignUpdaterToElement", function () {
    
            before(function (done) {
                const options = {};
                initJSDOM(options).then(() => {
                    import("../../../source/dom/updater.mjs").then((yy) => {
                        addObjectWithUpdaterToElement = yy['addObjectWithUpdaterToElement'];
                        import("../../../source/dom/customelement.mjs").then((m) => {
                            try {
                                CustomElement = m['CustomElement'];
                                assignUpdaterToElement = function (elements, object) {
                                    return addObjectWithUpdaterToElement.call(this, elements, updaterSymbolSymbol, object);
                                }
                                document = getDocument();
    
                                done()
                            } catch (e) {
                                done(e);
                            }
    
    
                        }).catch((e) => {
                            done(e);
                        });
    
                    }).catch((e) => {
                        done(e);
                    });
                });
            })
    
            beforeEach(() => {
                let mocks = document.getElementById('mocks');
                mocks.innerHTML = html2;
            })
    
            afterEach(() => {
                let mocks = document.getElementById('mocks');
                mocks.innerHTML = "";
            })
    
            /**
             * this test try to simulate the bug that was found in the assignUpdaterToElement function.
             * The bug was that the updater was not assigned to the element when the element was created.
             *
             * unfortunately, this test does not reproduce the bug.
             */
            it("should assign an updater to an element", function (done) {
                let element = document.getElementById('test2');
    
                expect(document.getElementById("mocks").innerHTML).to.equal(html2);
    
                const a = {a: 1};
                const b = {b: 2};
    
                const ap = new ProxyObserver(a);
                const bp = new ProxyObserver(b);
    
                const x = ap.getSubject()
                const y = bp.getSubject()
    
                const set = new Set();
                set.add(element);
    
                assignUpdaterToElement.call(element, set, ap);
                assignUpdaterToElement.call(element, set, bp);
    
                expect(JSON.stringify(x)).to.equal('{"a":1}');
                expect(JSON.stringify(y)).to.equal('{"b":2}');
    
                const sy = updaterSymbolSymbol;
    
                let v = element.getAttribute("data-monster-objectlink");
                expect(v).to.equal('Symbol(' + updaterSymbolKey + ')');
    
                const updater = element[sy];
    
                for (const v of updater) {
                    for (const u of v) {
                        u.run().then(() => {
                            u.enableEventProcessing();
                        });
                    }
                }
    
                expect(updater).to.be.an.instanceof(Set);
                expect(updater).to.be.a("Set");
    
                x.a = 3;
                bp.getSubject().b = 4;
    
                setTimeout(() => {
    
                    let mockHTML = document.getElementById("mocks");
    
                    // html expexted:
                    // <input data-monster-bind="path:a" id="test2" data-monster-attributes="value path:a" data-monster-objectlink="Symbol(@schukai/monster/dom/@@object-updater-link)" value="3">
    
                    expect(mockHTML.querySelector("#test2")).to.have.value('3')
                    expect(mockHTML.querySelector("#test2")).to.have.attribute('data-monster-objectlink', 'Symbol(' + updaterSymbolKey + ')')
                    //expect(mockHTML).to.have.html(resultHTML);
    
                    expect(element.value).to.equal("3");
    
                    expect(JSON.stringify(ap.getRealSubject())).to.equal('{"a":3}');
                    expect(JSON.stringify(bp.getRealSubject())).to.equal('{"b":4}');
                    done()
                }, 50)
    
            })
    
        })
    
        describe('CustomElement()', function () {
    
            before(function (done) {
                initJSDOM({}).then(() => {
    
                    import("../../../source/dom/customelement.mjs").then((m) => {
    
                        try {
                            CustomElement = m['CustomElement'];
                            registerCustomElement = m['registerCustomElement'];
                            TestComponent = class extends CustomElement {
                                static getTag() {
                                    return "monster-testclass"
                                }
                            }
    
                            registerCustomElement(TestComponent)
    
                            TestComponent2 = class extends CustomElement {
                                static getTag() {
                                    return "monster-testclass2"
                                }
    
                                /**
                                 *
                                 * @return {Object}
                                 */
                                get defaults() {
    
                                    return Object.assign({}, super.defaults, {
                                        demotest: undefined,
                                        templates: {
                                            main: '<h1></h1><article><p>test</p><div id="container"></div></article>'
                                        },
                                    })
                                }
    
                            }
    
                            registerCustomElement(TestComponent2)
    
                            document = getDocument();
                            done()
                        } catch (e) {
                            done(e);
                        }
    
    
                    });
    
                });
            })
    
            beforeEach(() => {
                let mocks = document.getElementById('mocks');
                mocks.innerHTML = html1;
            })
    
            afterEach(() => {
                let mocks = document.getElementById('mocks');
                mocks.innerHTML = "";
            })
    
            describe('CustomElement() with Config', function () {
                it('should read config from tag', function () {
    
                    let mocks = document.getElementById('mocks');
                    mocks.innerHTML = `
                    
                    <script id="config1" type="application/json">
                    {
                        "demotest":1425
                    }
                    </script>
                    
                    <monster-testclass2 id="thisisatest" data-monster-options-selector="#config1">
                    </monster-testclass2>
                    `;
    
                    let monster = document.getElementById('thisisatest');
                    expect(monster.getOption('demotest')).is.eql(1425);
    
                });
            });
    
            describe('create', function () {
                it('should return custom-element object', function () {
                    let d = new TestComponent();
                    expect(typeof d).is.equal('object');
                });
            });
    
            describe('connect empty element', function () {
                it('document should contain monster-testclass', function () {
                    let d = document.createElement('monster-testclass');
                    document.getElementById('test1').appendChild(d);
                    expect(document.getElementsByTagName('monster-testclass').length).is.equal(1);
                    // no data-monster-objectlink="Symbol(monsterUpdater)" because it has nothing to update
                    // but data-monster-error="Error: html is not set."
                    expect(document.getElementById('test1')).contain.html('<monster-testclass data-monster-error="html is not set."></monster-testclass>');
                });
            });
    
            describe('connect element with html', function () {
                it('document should contain monster-testclass2', function (done) {
                    let d = document.createElement('monster-testclass2');
                    document.getElementById('test1').appendChild(d);
    
                    // insert DOM run in extra process via setTimeout!
                    setTimeout(function () {
                        try {
                            expect(document.getElementsByTagName('monster-testclass2').length).is.equal(1);
                            expect(document.getElementsByTagName('monster-testclass2').item(0).shadowRoot.innerHTML).is.equal('<h1></h1><article><p>test</p><div id="container"></div></article>');
                            expect(document.getElementById('test1')).contain.html('<monster-testclass2 data-monster-objectlink="Symbol(' + updaterSymbolKey + ')"></monster-testclass2>');
                            return done();
                        } catch (e) {
                            done(e);
                        }
    
                    }, 10);
    
                });
            });
    
            describe('Options change', function () {
    
                it('delegatesFocus should change from true to false', function () {
                    let element = document.createElement('monster-testclass')
    
                    const o = element[internalSymbol].realSubject;
                    expect(Object.is(element[internalSymbol].realSubject, o)).to.be.true;
    
                    expect(element[internalSymbol].realSubject.options.delegatesFocus).to.be.true;
                    expect(element[internalSymbol].subject.options.delegatesFocus).to.be.true;
                    expect(element.getOption('delegatesFocus')).to.be.true;
                    expect(Object.is(element[internalSymbol].realSubject, o)).to.be.true;
    
                    // element.setAttribute(ATTRIBUTE_OPTIONS, JSON.stringify({delegatesFocus: false}));
                    // expect(Object.is(element[internalSymbol].realSubject, o)).to.be.true;
                    //
                    // expect(element.getOption('delegatesFocus')).to.be.false;
                    // expect(element[internalSymbol].realSubject.options.delegatesFocus).to.be.false;
                    // expect(Object.is(element[internalSymbol].realSubject, o)).to.be.true;
    
                })
    
    
            })
    
            describe('setOptions()', function () {
                [
                    ['shadowMode', 'x1'],
                    ['templates.main', 'x2'], // is explicitly set to undefined
                    ['delegatesFocus', 'x4'],
                ].forEach(function (data) {
    
    
                    let key = data.shift()
                    let newValue = data.shift()
    
                    let text = key + ' should return ' + newValue;
                    if (newValue !== undefined) {
                        text = key + ' was not set, therefore default ' + newValue;
                    }
    
    
                    it(text, function () {
    
                        let d = document.createElement('monster-testclass');
                        expect(d.getOption(key)).to.be.not.equal(newValue);
                        let x = d.setOption(key, newValue);
                        expect(d.getOption(key)).to.be.equal(newValue);
                    })
    
    
                })
            });
    
            describe('getOptions()', function () {
    
                [
                    ['shadowMode', 'open'],
                    ['templates.main', undefined], // is explicitly set to undefined
                    ['delegatesFocus', true],
                    ['x.y.z', true, true], // x.y.z isnt set, defaultValue is used
                    ['x', true, true] // x isnt set, defaultValue is used
                ].forEach(function (data) {
    
    
                    let key = data.shift()
                    let value = data.shift()
                    let defaultValue = data.shift()
    
                    let text = key + ' should return ' + value;
                    if (defaultValue !== undefined) {
                        text = key + ' was not set, therefore default ' + defaultValue;
                    }
    
    
                    it(text, function () {
    
                        let d = document.createElement('monster-testclass');
                        let x = d.getOption(key, defaultValue);
                        expect(x).to.be.equal(value);
                    })
    
    
                })
            })
    
            /**
             * @link https://gitlab.schukai.com/oss/libraries/javascript/monster/-/issues/113
             */
            describe('Assign CSSStyle as Array with wrong type', function () {
    
                const htmlTAG = 'monster-testclass-x1';
    
                let mocks, TestComponentX1;
                beforeEach(() => {
    
                    mocks = document.getElementById('mocks');
                    mocks.innerHTML = html1;
    
    
                    TestComponentX1 = class extends CustomElement {
                        static getTag() {
                            return htmlTAG
                        }
    
                        static getCSSStyleSheet() {
                            return [true];
                        }
    
                        /**
                         * @return {Object}
                         */
                        get defaults() {
    
                            return Object.assign({}, super.defaults, {
                                templates: {
                                    main: '<h1>test</h1>'
                                },
                            })
                        }
    
                    }
    
                    registerCustomElement(TestComponentX1)
    
    
                })
    
                it(htmlTAG + " should throw Exception", function (done) {
                    let d = document.createElement(htmlTAG);
    
                    let div = document.getElementById('test1');
                    div.append(d);
    
    
                    expect(div).contain.html('data-monster-error="value is not an instance of CSSStyleSheet"');
                    done();
    
                })
    
    
            })
    
    
            /**
             * @link https://gitlab.schukai.com/oss/libraries/javascript/monster/-/issues/113
             */
            describe('Assign CSSStyle as Array and CSSStylesheet', function () {
    
                const htmlTAG = 'monster-testclass-x113-2';
    
                let mocks, TestComponentX113X2;
                beforeEach(() => {
    
                    mocks = document.getElementById('mocks');
                    mocks.innerHTML = html1;
    
                    TestComponentX113X2 = class extends CustomElement {
                        static getTag() {
                            return htmlTAG
                        }
    
                        /**
                         * @return {Object}
                         */
                        get defaults() {
                            return Object.assign({}, super.defaults, {
                                templates: {main: '<h1>test</h1>'},
                            })
                        }
    
    
                        static getCSSStyleSheet() {
    
                            const s = (new CSSStyleSheet())
                            s.insertRule('a { color : red}');
    
                            return [s];
                        }
                    }
    
                    registerCustomElement(TestComponentX113X2)
    
    
                })
    
                it(htmlTAG + " should throw Exception 2", function (done) {
                    let d = document.createElement(htmlTAG);
    
                    let div = document.getElementById('test1');
                    div.append(d);
    
                    expect(d.shadowRoot.innerHTML).is.eq('<h1>test</h1>');
                    done();
    
                })
            })
    
            /**
             * @link https://gitlab.schukai.com/oss/libraries/javascript/monster/-/issues/113
             */
            describe('Assign CSSStyle as Array and CSS as string', function () {
    
                const htmlTAG = 'monster-testclass-x113-21';
    
                let mocks, TestComponentX113X22;
                beforeEach(() => {
    
                    mocks = document.getElementById('mocks');
                    mocks.innerHTML = html1;
    
                    TestComponentX113X22 = class extends CustomElement {
                        static getTag() {
                            return htmlTAG
                        }
    
                        /**
                         * @return {Object}
                         */
                        get defaults() {
                            return Object.assign({}, super.defaults, {
                                templates: {main: '<h1>test</h1>'},
                            })
                        }
    
    
                        static getCSSStyleSheet() {
                            return 'a { color:red }';
                        }
                    }
    
                    registerCustomElement(TestComponentX113X22)
    
    
                })
    
                it(htmlTAG + " should eq <style>a { color:red }</style><h1>test</h1>", function (done) {
                    let d = document.createElement(htmlTAG);
    
                    let div = document.getElementById('test1');
                    div.append(d);
    
    
                    expect(d.shadowRoot.innerHTML).is.eq('<style>a { color:red }</style><h1>test</h1>');
                    done();
    
                })
            })
            /**
             * @link https://gitlab.schukai.com/oss/libraries/javascript/monster/-/issues/113
             */
            describe('Assign CSSStyle as Array and CSS as string', function () {
    
                const htmlTAG = 'monster-testclass-x113-22';
    
                let mocks, TestComponentX113X223;
                beforeEach(() => {
    
                    mocks = document.getElementById('mocks');
                    mocks.innerHTML = html1;
    
                    TestComponentX113X223 = class extends CustomElement {
                        static getTag() {
                            return htmlTAG
                        }
    
                        /**
                         * @return {Object}
                         */
                        get defaults() {
                            return Object.assign({}, super.defaults, {
                                templates: {main: '<h1>test</h1>'},
                            })
                        }
    
    
                        static getCSSStyleSheet() {
                            return ['a { color:red }'];
                        }
                    }
    
                    registerCustomElement(TestComponentX113X223)
    
    
                })
    
                it(htmlTAG + " should eq <style>a { color:red }</style><h1>test</h1>", function (done) {
                    let d = document.createElement(htmlTAG);
    
                    let div = document.getElementById('test1');
                    div.append(d);
    
    
                    expect(d.shadowRoot.innerHTML).is.eq('<style>a { color:red }</style><h1>test</h1>');
                    done();
    
                })
            })
    
            describe('hasNode()', function () {
    
                let mocks;
                beforeEach(() => {
    
                    mocks = document.getElementById('mocks');
                    mocks.innerHTML = html1;
    
                })
    
                it("hasNode monster-testclass should return ...", function () {
                    let d = document.createElement('monster-testclass');
    
                    let p1 = document.createElement('p');
                    let t1 = document.createTextNode('test1');
                    p1.appendChild(t1);
    
                    let p = document.createElement('div');
                    let t = document.createTextNode('test');
                    p.appendChild(p1);
                    p.appendChild(t);
                    d.appendChild(p);
    
                    let div = document.getElementById('test1');
                    div.append(d);
    
    
                    let n1 = document.createElement('p');
    
                    expect(d.hasNode(n1)).to.be.false;
                    expect(d.hasNode(t)).to.be.true;
                    expect(d.hasNode(p)).to.be.true;
                    expect(d.hasNode(p1)).to.be.true;
                    expect(d.hasNode(t1)).to.be.true;
    
                })
    
                it("hasNode monster-testclass2 should return ...", function () {
                    let d = document.createElement('monster-testclass2');
    
                    let p1 = document.createElement('p');
                    let t1 = document.createTextNode('test1');
                    p1.appendChild(t1);
    
                    let p = document.createElement('div');
                    let t = document.createTextNode('test');
                    p.appendChild(p1);
                    p.appendChild(t);
    
    
                    let div = document.getElementById('test1');
                    div.append(d);
    
                    let a = d.shadowRoot.getElementById('container');
    
                    d.shadowRoot.getElementById('container').appendChild(p);
    
                    let n1 = document.createElement('p');
    
                    expect(d.hasNode(n1)).to.be.false;
                    expect(d.hasNode(t)).to.be.true;
                    expect(d.hasNode(p)).to.be.true;
                    expect(d.hasNode(p1)).to.be.true;
                    expect(d.hasNode(t1)).to.be.true;
    
                })
    
    
            })
    
        });
    })