|
|
@@ -14,6 +14,8 @@ import CKEditorError from '/ckeditor5/core/ckeditorerror.js';
|
|
|
|
|
|
testUtils.createSinonSandbox();
|
|
|
|
|
|
+let el, text;
|
|
|
+
|
|
|
describe( 'Template', () => {
|
|
|
describe( 'constructor', () => {
|
|
|
it( 'accepts the definition', () => {
|
|
|
@@ -39,7 +41,7 @@ describe( 'Template', () => {
|
|
|
} ).to.throw( CKEditorError, /ui-template-wrong-syntax/ );
|
|
|
} );
|
|
|
|
|
|
- it( 'creates a HTMLElement', () => {
|
|
|
+ it( 'creates a HTMLElement with attributes', () => {
|
|
|
let el = new Template( {
|
|
|
tag: 'p',
|
|
|
attributes: {
|
|
|
@@ -110,130 +112,315 @@ describe( 'Template', () => {
|
|
|
expect( el.outerHTML ).to.be.equal( '<p>abcd</p>' );
|
|
|
} );
|
|
|
|
|
|
- describe( 'callback', () => {
|
|
|
- it( 'works for attributes', () => {
|
|
|
- let spy1 = testUtils.sinon.spy();
|
|
|
- let spy2 = testUtils.sinon.spy();
|
|
|
+ it( 'activates listener attachers – root', () => {
|
|
|
+ let spy1 = testUtils.sinon.spy();
|
|
|
+ let spy2 = testUtils.sinon.spy();
|
|
|
+ let spy3 = testUtils.sinon.spy();
|
|
|
|
|
|
- let el = new Template( {
|
|
|
- tag: 'p',
|
|
|
- attributes: {
|
|
|
- 'class': spy1
|
|
|
+ let el = new Template( {
|
|
|
+ tag: 'p',
|
|
|
+ on: {
|
|
|
+ _listenerAttachers: {
|
|
|
+ foo: spy1,
|
|
|
+ baz: [ spy2, spy3 ]
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } ).render();
|
|
|
+
|
|
|
+ sinon.assert.calledWithExactly( spy1, el, 'foo', null );
|
|
|
+ sinon.assert.calledWithExactly( spy2, el, 'baz', null );
|
|
|
+ sinon.assert.calledWithExactly( spy3, el, 'baz', null );
|
|
|
+ } );
|
|
|
+
|
|
|
+ it( 'activates listener attachers – children', () => {
|
|
|
+ let spy = testUtils.sinon.spy();
|
|
|
+ let el = new Template( {
|
|
|
+ tag: 'p',
|
|
|
+ children: [
|
|
|
+ {
|
|
|
+ tag: 'span',
|
|
|
+ on: {
|
|
|
+ _listenerAttachers: {
|
|
|
+ bar: spy
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ ],
|
|
|
+ } ).render();
|
|
|
+
|
|
|
+ sinon.assert.calledWithExactly( spy, el.firstChild, 'bar', null );
|
|
|
+ } );
|
|
|
+
|
|
|
+ it( 'activates listener attachers – DOM selectors', () => {
|
|
|
+ let spy1 = testUtils.sinon.spy();
|
|
|
+ let spy2 = testUtils.sinon.spy();
|
|
|
+ let spy3 = testUtils.sinon.spy();
|
|
|
+ let spy4 = testUtils.sinon.spy();
|
|
|
+
|
|
|
+ let el = new Template( {
|
|
|
+ tag: 'p',
|
|
|
+ children: [
|
|
|
+ {
|
|
|
+ tag: 'span',
|
|
|
+ attributes: {
|
|
|
+ 'id': 'x'
|
|
|
+ }
|
|
|
},
|
|
|
- children: [
|
|
|
- {
|
|
|
- tag: 'span',
|
|
|
- attributes: {
|
|
|
- id: spy2
|
|
|
+ {
|
|
|
+ tag: 'span',
|
|
|
+ attributes: {
|
|
|
+ 'class': 'y'
|
|
|
+ },
|
|
|
+ on: {
|
|
|
+ _listenerAttachers: {
|
|
|
+ 'bar@p': spy2
|
|
|
}
|
|
|
}
|
|
|
- ]
|
|
|
- } ).render();
|
|
|
+ },
|
|
|
+ ],
|
|
|
+ on: {
|
|
|
+ _listenerAttachers: {
|
|
|
+ 'foo@span': spy1,
|
|
|
+ 'baz@.y': [ spy3, spy4 ]
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } ).render();
|
|
|
|
|
|
- sinon.assert.calledWithExactly( spy1, el, sinon.match.func );
|
|
|
- sinon.assert.calledWithExactly( spy2, el.firstChild, sinon.match.func );
|
|
|
+ sinon.assert.calledWithExactly( spy1, el, 'foo', 'span' );
|
|
|
+ sinon.assert.calledWithExactly( spy2, el.lastChild, 'bar', 'p' );
|
|
|
+ sinon.assert.calledWithExactly( spy3, el, 'baz', '.y' );
|
|
|
+ sinon.assert.calledWithExactly( spy4, el, 'baz', '.y' );
|
|
|
+ } );
|
|
|
|
|
|
- spy1.firstCall.args[ 1 ]( el, 'foo' );
|
|
|
- spy2.firstCall.args[ 1 ]( el.firstChild, 'bar' );
|
|
|
+ it( 'activates model bindings – attributes', () => {
|
|
|
+ let spy1 = testUtils.sinon.spy();
|
|
|
+ let spy2 = testUtils.sinon.spy();
|
|
|
|
|
|
- expect( el.outerHTML ).to.be.equal( '<p class="foo"><span id="bar"></span></p>' );
|
|
|
- } );
|
|
|
+ let el = new Template( {
|
|
|
+ tag: 'p',
|
|
|
+ attributes: {
|
|
|
+ 'class': spy1
|
|
|
+ },
|
|
|
+ children: [
|
|
|
+ {
|
|
|
+ tag: 'span',
|
|
|
+ attributes: {
|
|
|
+ id: spy2
|
|
|
+ }
|
|
|
+ }
|
|
|
+ ]
|
|
|
+ } ).render();
|
|
|
+
|
|
|
+ sinon.assert.calledWithExactly( spy1, el, sinon.match.func );
|
|
|
+ sinon.assert.calledWithExactly( spy2, el.firstChild, sinon.match.func );
|
|
|
|
|
|
- it( 'works for "text" property', () => {
|
|
|
- let spy1 = testUtils.sinon.spy();
|
|
|
- let spy2 = testUtils.sinon.spy();
|
|
|
+ spy1.firstCall.args[ 1 ]( el, 'foo' );
|
|
|
+ spy2.firstCall.args[ 1 ]( el.firstChild, 'bar' );
|
|
|
|
|
|
- let el = new Template( {
|
|
|
+ expect( el.outerHTML ).to.be.equal( '<p class="foo"><span id="bar"></span></p>' );
|
|
|
+ } );
|
|
|
+
|
|
|
+ it( 'activates model bindings – Text Node', () => {
|
|
|
+ let spy1 = testUtils.sinon.spy();
|
|
|
+ let spy2 = testUtils.sinon.spy();
|
|
|
+
|
|
|
+ let el = new Template( {
|
|
|
+ tag: 'p',
|
|
|
+ children: [
|
|
|
+ {
|
|
|
+ text: spy1
|
|
|
+ },
|
|
|
+ {
|
|
|
+ tag: 'span',
|
|
|
+ children: [
|
|
|
+ {
|
|
|
+ text: spy2
|
|
|
+ }
|
|
|
+ ]
|
|
|
+ }
|
|
|
+ ]
|
|
|
+ } ).render();
|
|
|
+
|
|
|
+ sinon.assert.calledWithExactly( spy1, el.firstChild, sinon.match.func );
|
|
|
+ sinon.assert.calledWithExactly( spy2, el.lastChild.firstChild, sinon.match.func );
|
|
|
+
|
|
|
+ spy2.firstCall.args[ 1 ]( el.lastChild.firstChild, 'bar' );
|
|
|
+ expect( el.outerHTML ).to.be.equal( '<p><span>bar</span></p>' );
|
|
|
+
|
|
|
+ spy1.firstCall.args[ 1 ]( el.firstChild, 'foo' );
|
|
|
+ expect( el.outerHTML ).to.be.equal( '<p>foo<span>bar</span></p>' );
|
|
|
+ } );
|
|
|
+ } );
|
|
|
+
|
|
|
+ describe( 'apply', () => {
|
|
|
+ beforeEach( () => {
|
|
|
+ el = document.createElement( 'div' );
|
|
|
+ text = document.createTextNode( '' );
|
|
|
+ } );
|
|
|
+
|
|
|
+ it( 'throws when wrong template definition', () => {
|
|
|
+ expect( () => {
|
|
|
+ new Template( {} ).apply( el );
|
|
|
+ } ).to.throw( CKEditorError, /ui-template-wrong-syntax/ );
|
|
|
+
|
|
|
+ expect( () => {
|
|
|
+ new Template( {
|
|
|
tag: 'p',
|
|
|
- children: [
|
|
|
- {
|
|
|
- text: spy1
|
|
|
- },
|
|
|
- {
|
|
|
- tag: 'span',
|
|
|
- children: [
|
|
|
- {
|
|
|
- text: spy2
|
|
|
- }
|
|
|
- ]
|
|
|
+ text: 'foo'
|
|
|
+ } ).apply( el );
|
|
|
+ } ).to.throw( CKEditorError, /ui-template-wrong-syntax/ );
|
|
|
+ } );
|
|
|
+
|
|
|
+ it( 'throws when no HTMLElement passed', () => {
|
|
|
+ expect( () => {
|
|
|
+ new Template( {
|
|
|
+ tag: 'p'
|
|
|
+ } ).apply();
|
|
|
+ } ).to.throw( CKEditorError, /ui-template-wrong-node/ );
|
|
|
+ } );
|
|
|
+
|
|
|
+ it( 'applies textContent to a Text Node', () => {
|
|
|
+ new Template( {
|
|
|
+ text: 'abc'
|
|
|
+ } ).apply( text );
|
|
|
+
|
|
|
+ expect( text.textContent ).to.be.equal( 'abc' );
|
|
|
+ } );
|
|
|
+
|
|
|
+ it( 'applies attributes to an HTMLElement', () => {
|
|
|
+ new Template( {
|
|
|
+ tag: 'div',
|
|
|
+ attributes: {
|
|
|
+ 'class': [ 'a', 'b' ],
|
|
|
+ x: 'bar'
|
|
|
+ }
|
|
|
+ } ).apply( el );
|
|
|
+
|
|
|
+ expect( el.outerHTML ).to.be.equal( '<div class="a b" x="bar"></div>' );
|
|
|
+ } );
|
|
|
+
|
|
|
+ it( 'applies doesn\'t apply new child to an HTMLElement – Text Node', () => {
|
|
|
+ new Template( {
|
|
|
+ tag: 'div',
|
|
|
+ children: [ 'foo' ]
|
|
|
+ } ).apply( el );
|
|
|
+
|
|
|
+ expect( el.outerHTML ).to.be.equal( '<div></div>' );
|
|
|
+ } );
|
|
|
+
|
|
|
+ it( 'applies doesn\'t apply new child to an HTMLElement – HTMLElement', () => {
|
|
|
+ new Template( {
|
|
|
+ tag: 'div',
|
|
|
+ children: [
|
|
|
+ {
|
|
|
+ tag: 'span'
|
|
|
+ }
|
|
|
+ ]
|
|
|
+ } ).apply( el );
|
|
|
+
|
|
|
+ expect( el.outerHTML ).to.be.equal( '<div></div>' );
|
|
|
+ } );
|
|
|
+
|
|
|
+ it( 'applies new textContent to an existing Text Node of an HTMLElement', () => {
|
|
|
+ el.textContent = 'bar';
|
|
|
+
|
|
|
+ new Template( {
|
|
|
+ tag: 'div',
|
|
|
+ children: [ 'foo' ]
|
|
|
+ } ).apply( el );
|
|
|
+
|
|
|
+ expect( el.outerHTML ).to.be.equal( '<div>foo</div>' );
|
|
|
+ } );
|
|
|
+
|
|
|
+ it( 'applies attributes and TextContent to a DOM tree', () => {
|
|
|
+ el.textContent = 'abc';
|
|
|
+ el.appendChild( document.createElement( 'span' ) );
|
|
|
+
|
|
|
+ new Template( {
|
|
|
+ tag: 'div',
|
|
|
+ attributes: {
|
|
|
+ 'class': 'parent'
|
|
|
+ },
|
|
|
+ children: [
|
|
|
+ 'Children: ',
|
|
|
+ {
|
|
|
+ tag: 'span',
|
|
|
+ attributes: {
|
|
|
+ class: 'child'
|
|
|
}
|
|
|
- ]
|
|
|
- } ).render();
|
|
|
+ }
|
|
|
+ ]
|
|
|
+ } ).apply( el );
|
|
|
|
|
|
- sinon.assert.calledWithExactly( spy1, el.firstChild, sinon.match.func );
|
|
|
- sinon.assert.calledWithExactly( spy2, el.lastChild.firstChild, sinon.match.func );
|
|
|
+ expect( el.outerHTML ).to.be.equal( '<div class="parent">Children: <span class="child"></span></div>' );
|
|
|
+ } );
|
|
|
|
|
|
- spy2.firstCall.args[ 1 ]( el.lastChild.firstChild, 'bar' );
|
|
|
- expect( el.outerHTML ).to.be.equal( '<p><span>bar</span></p>' );
|
|
|
+ it( 'activates listener attachers – root', () => {
|
|
|
+ const spy = testUtils.sinon.spy();
|
|
|
|
|
|
- spy1.firstCall.args[ 1 ]( el.firstChild, 'foo' );
|
|
|
- expect( el.outerHTML ).to.be.equal( '<p>foo<span>bar</span></p>' );
|
|
|
- } );
|
|
|
+ new Template( {
|
|
|
+ tag: 'div',
|
|
|
+ on: {
|
|
|
+ _listenerAttachers: {
|
|
|
+ click: spy
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } ).apply( el );
|
|
|
|
|
|
- it( 'works for "on" property', () => {
|
|
|
- let spy1 = testUtils.sinon.spy();
|
|
|
- let spy2 = testUtils.sinon.spy();
|
|
|
- let spy3 = testUtils.sinon.spy();
|
|
|
- let spy4 = testUtils.sinon.spy();
|
|
|
+ sinon.assert.calledWithExactly( spy, el, 'click', null );
|
|
|
+ } );
|
|
|
|
|
|
- let el = new Template( {
|
|
|
- tag: 'p',
|
|
|
- children: [
|
|
|
- {
|
|
|
- tag: 'span',
|
|
|
- on: {
|
|
|
- bar: spy2
|
|
|
+ it( 'activates listener attachers – children', () => {
|
|
|
+ const spy = testUtils.sinon.spy();
|
|
|
+ el.appendChild( document.createElement( 'span' ) );
|
|
|
+
|
|
|
+ new Template( {
|
|
|
+ tag: 'div',
|
|
|
+ children: [
|
|
|
+ {
|
|
|
+ tag: 'span',
|
|
|
+ on: {
|
|
|
+ _listenerAttachers: {
|
|
|
+ click: spy
|
|
|
}
|
|
|
}
|
|
|
- ],
|
|
|
- on: {
|
|
|
- foo: spy1,
|
|
|
- baz: [ spy3, spy4 ]
|
|
|
}
|
|
|
- } ).render();
|
|
|
+ ]
|
|
|
+ } ).apply( el );
|
|
|
|
|
|
- sinon.assert.calledWithExactly( spy1, el, 'foo', null );
|
|
|
- sinon.assert.calledWithExactly( spy2, el.firstChild, 'bar', null );
|
|
|
- sinon.assert.calledWithExactly( spy3, el, 'baz', null );
|
|
|
- sinon.assert.calledWithExactly( spy4, el, 'baz', null );
|
|
|
- } );
|
|
|
+ sinon.assert.calledWithExactly( spy, el.firstChild, 'click', null );
|
|
|
+ } );
|
|
|
|
|
|
- it( 'works for "on" property with selectors', () => {
|
|
|
- let spy1 = testUtils.sinon.spy();
|
|
|
- let spy2 = testUtils.sinon.spy();
|
|
|
- let spy3 = testUtils.sinon.spy();
|
|
|
- let spy4 = testUtils.sinon.spy();
|
|
|
+ it( 'activates model bindings – root', () => {
|
|
|
+ const spy = testUtils.sinon.spy();
|
|
|
|
|
|
- let el = new Template( {
|
|
|
- tag: 'p',
|
|
|
- children: [
|
|
|
- {
|
|
|
- tag: 'span',
|
|
|
- attributes: {
|
|
|
- 'id': 'x'
|
|
|
- }
|
|
|
- },
|
|
|
- {
|
|
|
- tag: 'span',
|
|
|
- attributes: {
|
|
|
- 'class': 'y'
|
|
|
- },
|
|
|
- on: {
|
|
|
- 'bar@p': spy2
|
|
|
- }
|
|
|
- },
|
|
|
- ],
|
|
|
- on: {
|
|
|
- 'foo@span': spy1,
|
|
|
- 'baz@.y': [ spy3, spy4 ]
|
|
|
+ new Template( {
|
|
|
+ tag: 'div',
|
|
|
+ attributes: {
|
|
|
+ class: spy
|
|
|
+ }
|
|
|
+ } ).apply( el );
|
|
|
+
|
|
|
+ sinon.assert.calledWithExactly( spy, el, sinon.match.func );
|
|
|
+ } );
|
|
|
+
|
|
|
+ it( 'activates model bindings – children', () => {
|
|
|
+ const spy = testUtils.sinon.spy();
|
|
|
+ el.appendChild( document.createElement( 'span' ) );
|
|
|
+
|
|
|
+ new Template( {
|
|
|
+ tag: 'div',
|
|
|
+ children: [
|
|
|
+ {
|
|
|
+ tag: 'span',
|
|
|
+ attributes: {
|
|
|
+ class: spy
|
|
|
+ }
|
|
|
}
|
|
|
- } ).render();
|
|
|
+ ]
|
|
|
+ } ).apply( el );
|
|
|
|
|
|
- sinon.assert.calledWithExactly( spy1, el, 'foo', 'span' );
|
|
|
- sinon.assert.calledWithExactly( spy2, el.lastChild, 'bar', 'p' );
|
|
|
- sinon.assert.calledWithExactly( spy3, el, 'baz', '.y' );
|
|
|
- sinon.assert.calledWithExactly( spy4, el, 'baz', '.y' );
|
|
|
- } );
|
|
|
+ sinon.assert.calledWithExactly( spy, el.firstChild, sinon.match.func );
|
|
|
} );
|
|
|
} );
|
|
|
} );
|