blob: 763b2026faba1b06576e926c0298e982f4117efd [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import chai from 'chai'
import sinon from 'sinon'
import sinonChai from 'sinon-chai'
const { expect } = chai
chai.use(sinonChai)
import {
Document,
Element,
Comment,
isWeexElement,
registerElement,
clearWeexElements
} from '../../../../../runtime/vdom'
describe('document constructor', () => {
it('create & destroy document', () => {
const doc = new Document('foo', 'http://path/to/url')
expect(doc).is.an.object
expect(doc.id).eql('foo')
expect(doc.URL).eql('http://path/to/url')
expect(doc.documentElement).is.an.object
expect(doc.documentElement.role).equal('documentElement')
doc.destroy()
})
})
describe('component methods management', () => {
before(() => {
registerElement('x', ['foo', 'bar'])
registerElement('y', [])
registerElement('z')
})
after(() => {
clearWeexElements()
})
it('has registered element types', () => {
expect(isWeexElement('x')).to.be.true
})
it('will call component method', () => {
const spy = sinon.spy()
const doc = new Document('test', '', spy)
const x = new Element('x')
const y = new Element('y')
const z = new Element('z')
const n = new Element('n')
expect(x.foo).is.function
expect(x.bar).is.function
expect(x.baz).is.undefined
expect(y.foo).is.undefined
expect(z.foo).is.undefined
expect(n.foo).is.undefined
doc.createBody('r')
doc.documentElement.appendChild(doc.body)
doc.body.appendChild(x)
doc.body.appendChild(y)
doc.body.appendChild(z)
doc.body.appendChild(n)
expect(spy.args.length).eql(5)
x.foo(1, 2, 3)
expect(spy.args.length).eql(6)
expect(spy.args[5]).eql([[{ component: 'x', method: 'foo', ref: x.ref, args: [1, 2, 3] }]])
})
})
describe('document methods', () => {
let doc
beforeEach(() => {
doc = new Document('foo', null, function () {})
})
afterEach(() => {
doc.destroy()
})
it('open & close with a listener', () => {
expect(doc.listener.batched).is.false
doc.close()
expect(doc.listener.batched).is.true
doc.open()
expect(doc.listener.batched).is.false
})
it('create body', () => {
const ele = doc.createBody('container',
{ attr: { id: 'a' }, style: { fontSize: 16 }})
expect(ele.role).equal('body')
expect(ele.attr).to.have.a.property('id', 'a')
expect(ele.style).to.have.a.property('fontSize', 16)
expect(ele).have.a.property('ref')
expect(ele).have.a.property('children')
expect(ele).have.a.property('pureChildren')
expect(ele).have.a.property('insertBefore')
expect(ele).have.a.property('setStyle')
const ref = ele.ref
expect(doc.nodeMap[ref]).equal(ele)
})
it('create element', () => {
const ele = doc.createElement('container',
{ attr: { id: 'a' }, style: { fontSize: 16 }})
expect(ele.attr).to.have.a.property('id', 'a')
expect(ele.style).to.have.a.property('fontSize', 16)
expect(ele).have.a.property('ref')
expect(ele).have.a.property('children')
expect(ele).have.a.property('pureChildren')
expect(ele).have.a.property('insertBefore')
expect(ele).have.a.property('setStyle')
expect(doc.nodeMap[ele.ref]).is.undefined
doc.documentElement.appendChild(ele)
expect(doc.nodeMap[ele.ref]).equal(ele)
})
it('create comment', () => {
const comment = doc.createComment('start')
expect(comment).have.a.property('ref')
expect(comment).not.have.a.property('appendChild')
expect(comment).have.a.property('value', 'start')
expect(comment.toString()).eql('<!-- start -->')
const ref = comment.ref
expect(doc.nodeMap[ref]).is.undefined
doc.documentElement.appendChild(comment)
expect(doc.nodeMap[ref]).equal(comment)
})
})
describe('Element in document methods', () => {
let doc, el, el2, el3
beforeEach(() => {
doc = new Document('foo', null, function () {})
el = new Element('bar', {
attr: { a: 11, b: 12 },
style: { c: 13, d: 14 },
classStyle: { a: 211, c: 213 }
})
el2 = new Element('baz', {
attr: { a: 21, b: 22 },
style: { c: 23, d: 24 },
classStyle: { a: 221, c: 223 }
})
el3 = new Element('qux', {
attr: { a: 31, b: 32 },
style: { c: 33, d: 34 },
classStyle: { a: 231, c: 233 }
})
})
afterEach(() => {
doc.destroy()
})
it('init correctly', () => {
expect(el).is.an.object
expect(el.type).eql('bar')
expect(el.attr).eql({ a: 11, b: 12 })
expect(el.style).eql({ c: 13, d: 14 })
expect(el.event).eql({})
expect(el.children).eql([])
expect(el.pureChildren).eql([])
expect(doc.nodeMap).is.an.object
expect(doc.documentElement).is.an.object
expect(Object.keys(doc.nodeMap)).eql([doc.documentElement.ref])
doc.documentElement.appendChild(el)
doc.documentElement.appendChild(el2)
el2.appendChild(el3)
expect(Object.keys(doc.nodeMap)).eql([
doc.documentElement.ref,
el.ref
])
})
it('has correct exports', () => {
const ref = el.ref
const finalStyle = el.toStyle()
expect(finalStyle).eql({ a: 211, c: 13, d: 14 })
expect(el.toJSON()).eql({
ref: ref, type: 'bar',
attr: el.attr, style: finalStyle
})
expect(el.toString()).eql(
'<bar attr={"a":11,"b":12} style={"a":211,"c":13,"d":14}></bar>')
})
it('createBody', () => {
doc.createBody('r')
doc.documentElement.appendChild(doc.body)
expect(doc.body).is.an.object
expect(doc.body.role).eql('body')
expect(doc.body.type).eql('r')
expect(doc.body.docId).eql('foo')
})
it('appendChild', () => {
el.appendChild(el2)
expect(el.children.length).eql(1)
expect(el.children[0]).equal(el2)
expect(el2.parentNode.ref).eql(el.ref)
expect(el.docId).is.not.ok
expect(el2.docId).is.not.ok
doc.createBody('r')
doc.documentElement.appendChild(doc.body)
doc.body.appendChild(el)
expect(doc.body.children.length).eql(1)
expect(el.parentNode.ref).eql(doc.body.ref)
expect(el.docId).is.ok
expect(el2.docId).is.ok
expect(el3.docId).is.not.ok
el.appendChild(el3)
expect(el.children.length).eql(2)
expect(el.children[0]).equal(el2)
expect(el.children[1]).equal(el3)
expect(el3.parentNode.ref).eql(el.ref)
expect(el3.docId).is.ok
expect(el2.previousSibling).is.not.ok
expect(el2.nextSibling).eql(el3)
expect(el3.previousSibling).eql(el2)
expect(el3.nextSibling).is.not.ok
})
it('insertBefore', () => {
doc.createBody('r')
doc.documentElement.appendChild(doc.body)
doc.body.appendChild(el)
expect(el.parentNode.ref).eql(doc.body.ref)
el.appendChild(el2)
expect(el2.parentNode.ref).eql(el.ref)
expect(el.children.length).eql(1)
expect(el.children[0]).equal(el2)
el.insertBefore(el3, el2)
expect(el.children.length).eql(2)
expect(el.children[0]).equal(el3)
expect(el.children[1]).equal(el2)
expect(el3.parentNode.ref).eql(el.ref)
expect(el.docId).eql('foo')
expect(el2.docId).eql('foo')
expect(el3.docId).eql('foo')
expect(el3.previousSibling).is.not.ok
expect(el3.nextSibling).eql(el2)
expect(el2.previousSibling).eql(el3)
expect(el2.nextSibling).is.not.ok
el.insertBefore(el2, el3)
expect(el.children.length).eql(2)
expect(el.children[0]).equal(el2)
expect(el.children[1]).equal(el3)
expect(el.docId).eql('foo')
expect(el2.docId).eql('foo')
expect(el3.docId).eql('foo')
expect(el2.previousSibling).is.not.ok
expect(el2.nextSibling).eql(el3)
expect(el3.previousSibling).eql(el2)
expect(el3.nextSibling).is.not.ok
})
it('insertAfter', () => {
doc.createBody('r')
doc.documentElement.appendChild(doc.body)
doc.body.appendChild(el)
expect(el.parentNode.ref).eql(doc.body.ref)
el.appendChild(el2)
el.insertAfter(el3, el2)
expect(el.children.length).eql(2)
expect(el.children[0]).equal(el2)
expect(el.children[1]).equal(el3)
expect(el2.parentNode.ref).eql(el.ref)
expect(el3.parentNode.ref).eql(el.ref)
expect(el.docId).eql('foo')
expect(el2.docId).eql('foo')
expect(el3.docId).eql('foo')
expect(el2.previousSibling).is.not.ok
expect(el2.nextSibling).eql(el3)
expect(el3.previousSibling).eql(el2)
expect(el3.nextSibling).is.not.ok
el.insertAfter(el2, el3, true)
expect(el.children.length).eql(2)
expect(el.children[0]).equal(el3)
expect(el.children[1]).equal(el2)
expect(el3.previousSibling).is.not.ok
expect(el3.nextSibling).eql(el2)
expect(el2.previousSibling).eql(el3)
expect(el2.nextSibling).is.not.ok
})
it('removeChild', () => {
doc.createBody('r')
doc.documentElement.appendChild(doc.body)
doc.body.appendChild(el)
doc.body.appendChild(el2)
el2.appendChild(el3)
expect(el.docId).eql('foo')
expect(el2.docId).eql('foo')
expect(el3.docId).eql('foo')
doc.body.removeChild(el)
expect(doc.body.children.length).equal(1)
expect(doc.body.children[0]).equal(el2)
expect(el.docId).is.not.ok
expect(el2.docId).eql('foo')
expect(el3.docId).eql('foo')
doc.body.removeChild(el2)
expect(doc.body.children.length).equal(0)
expect(el.docId).is.not.ok
expect(el2.docId).is.not.ok
expect(el3.docId).is.not.ok
})
it('clear', () => {
doc.createBody('r')
doc.documentElement.appendChild(doc.body)
doc.body.appendChild(el)
doc.body.appendChild(el2)
doc.body.appendChild(el3)
expect(el.docId).eql('foo')
expect(el2.docId).eql('foo')
expect(el3.docId).eql('foo')
doc.body.clear()
expect(doc.body.children.length).equal(0)
expect(el.docId).is.not.ok
expect(el2.docId).is.not.ok
expect(el3.docId).is.not.ok
})
it('modify attr, style, event', () => {
doc.createBody('r')
doc.documentElement.appendChild(doc.body)
doc.body.appendChild(el)
el.setAttr('a', 21)
expect(el.toJSON().attr).eql({ a: 21, b: 12 })
el.setAttr('a', 22, true)
expect(el.toJSON().attr).eql({ a: 22, b: 12 })
el.setAttr('a', 23, false)
expect(el.toJSON().attr).eql({ a: 23, b: 12 })
el.setStyle('c', 21)
expect(el.toJSON().style).eql({ a: 211, c: 21, d: 14 })
el.setStyle('c', 22, true)
expect(el.toJSON().style).eql({ a: 211, c: 22, d: 14 })
el.setStyle('c', 23, false)
expect(el.toJSON().style).eql({ a: 211, c: 23, d: 14 })
el.setClassStyle({ a: 311, c: 313 })
expect(el.toJSON().style).eql({ a: 311, c: 23, d: 14 })
const handler = function () {}
el.addEvent('click', handler)
expect(el.toJSON().event).eql(['click'])
expect(el.event.click.handler).equal(handler)
el.removeEvent('click')
expect(el.event.click).is.undefined
})
})
describe('Node', () => {
let doc, el, el2, el3, c, c2, c3, spy
beforeEach(() => {
spy = sinon.spy()
doc = new Document('foo', '', spy)
doc.createBody('r')
doc.documentElement.appendChild(doc.body)
el = new Element('bar')
el2 = new Element('baz')
el3 = new Element('qux')
c = new Comment('aaa')
c2 = new Comment('bbb')
c3 = new Comment('ccc')
})
afterEach(() => {
doc.destroy()
})
it('prev and next', () => {
expect(el.previousSibling).is.not.ok
expect(el.nextSibling).is.not.ok
doc.body.appendChild(el)
doc.body.appendChild(el2)
doc.body.appendChild(c)
doc.body.appendChild(el3)
doc.body.appendChild(c2)
doc.body.appendChild(c3)
expect(el.previousSibling).is.not.ok
expect(el2.previousSibling).equal(el)
expect(c.previousSibling).equal(el2)
expect(el3.previousSibling).equal(c)
expect(c2.previousSibling).equal(el3)
expect(c3.previousSibling).equal(c2)
expect(el.nextSibling).equal(el2)
expect(el2.nextSibling).equal(c)
expect(c.nextSibling).equal(el3)
expect(el3.nextSibling).equal(c2)
expect(c2.nextSibling).equal(c3)
expect(c3.nextSibling).is.not.ok
})
it('tree operations with renderer', () => {
doc.body.appendChild(el)
el.appendChild(el2)
el.appendChild(el3)
expect(spy.args.length).eql(4)
el.insertBefore(el3, el2) // [el3, el2]
expect(spy.args.length).eql(5)
expect(spy.args[4][0]).eql([{
module: 'dom', method: 'moveElement',
args: [el3.ref, el.ref, 0]
}])
el.insertAfter(el3, el2) // [el2, el3]
expect(spy.args.length).eql(6)
expect(spy.args[5][0]).eql([{
module: 'dom', method: 'moveElement',
args: [el3.ref, el.ref, 2]
}])
el.removeChild(el2) // [el3]
expect(spy.args.length).eql(7)
expect(spy.args[6][0]).eql([{
module: 'dom', method: 'removeElement',
args: [el2.ref]
}])
el.clear() // []
expect(spy.args.length).eql(8)
expect(spy.args[7][0]).eql([{
module: 'dom', method: 'removeElement',
args: [el3.ref]
}])
})
})
describe('complicated situations', () => {
let doc, el, el2, el3, c, c2, c3, spy
beforeEach(() => {
spy = sinon.spy()
doc = new Document('foo', '', spy)
doc.createBody('r')
doc.documentElement.appendChild(doc.body)
el = new Element('bar')
el2 = new Element('baz')
el3 = new Element('qux')
c = new Comment('aaa')
c2 = new Comment('bbb')
c3 = new Comment('ccc')
})
afterEach(() => {
doc.destroy()
})
it('move a node to its original position', () => {
doc.body.appendChild(el)
doc.body.appendChild(el2)
doc.body.appendChild(el3)
expect(spy.args.length).eql(4)
expect(doc.body.children).eql([el, el2, el3])
expect(doc.body.pureChildren).eql([el, el2, el3])
doc.body.insertAfter(el2, el)
expect(spy.args.length).eql(4)
expect(doc.body.children).eql([el, el2, el3])
expect(doc.body.pureChildren).eql([el, el2, el3])
doc.body.insertAfter(el3, el2, true)
expect(spy.args.length).eql(4)
expect(doc.body.children).eql([el, el2, el3])
expect(doc.body.pureChildren).eql([el, el2, el3])
doc.body.insertBefore(el, el2)
expect(spy.args.length).eql(4)
expect(doc.body.children).eql([el, el2, el3])
expect(doc.body.pureChildren).eql([el, el2, el3])
doc.body.insertBefore(el2, el3, true)
expect(spy.args.length).eql(4)
expect(doc.body.children).eql([el, el2, el3])
expect(doc.body.pureChildren).eql([el, el2, el3])
doc.body.insertAfter(c, el)
doc.body.insertAfter(c2, el2)
doc.body.insertAfter(c3, el3)
expect(spy.args.length).eql(4)
expect(doc.body.children).eql([el, c, el2, c2, el3, c3])
expect(doc.body.pureChildren).eql([el, el2, el3])
doc.body.insertBefore(el, c)
doc.body.insertBefore(el, c, true)
doc.body.insertBefore(c, el2)
doc.body.insertBefore(c, el2, true)
doc.body.insertAfter(c, el)
doc.body.insertAfter(c, el, true)
doc.body.insertAfter(el2, c)
doc.body.insertAfter(el2, c, true)
expect(spy.args.length).eql(4)
expect(doc.body.children).eql([el, c, el2, c2, el3, c3])
expect(doc.body.pureChildren).eql([el, el2, el3])
// move to another place that not change the pureChildren
doc.body.insertBefore(el, el2)
expect(spy.args.length).eql(4)
expect(doc.body.children).eql([c, el, el2, c2, el3, c3])
expect(doc.body.pureChildren).eql([el, el2, el3])
doc.body.insertBefore(el, c)
expect(spy.args.length).eql(4)
expect(doc.body.children).eql([el, c, el2, c2, el3, c3])
expect(doc.body.pureChildren).eql([el, el2, el3])
doc.body.insertAfter(c, el2)
expect(spy.args.length).eql(4)
expect(doc.body.children).eql([el, el2, c, c2, el3, c3])
expect(doc.body.pureChildren).eql([el, el2, el3])
doc.body.insertBefore(c2, c)
expect(spy.args.length).eql(4)
expect(doc.body.children).eql([el, el2, c2, c, el3, c3])
expect(doc.body.pureChildren).eql([el, el2, el3])
})
it('insert before a comment', () => {
doc.body.appendChild(el)
doc.body.appendChild(c)
doc.body.appendChild(c2)
doc.body.appendChild(el2)
expect(spy.args.length).eql(3)
expect(doc.body.children).eql([el, c, c2, el2])
expect(doc.body.pureChildren).eql([el, el2])
doc.body.insertBefore(el3, c)
expect(spy.args.length).eql(4)
expect(doc.body.children).eql([el, el3, c, c2, el2])
expect(doc.body.pureChildren).eql([el, el3, el2])
expect(spy.args[3][0]).eql([{
module: 'dom', method: 'addElement',
args: [doc.body.ref, el3.toJSON(), 1] }])
})
it('insert before a comment which has no more element after', () => {
doc.body.appendChild(el)
doc.body.appendChild(el2)
doc.body.appendChild(c)
doc.body.appendChild(c2)
expect(spy.args.length).eql(3)
expect(doc.body.children).eql([el, el2, c, c2])
expect(doc.body.pureChildren).eql([el, el2])
doc.body.insertBefore(el3, c)
expect(spy.args.length).eql(4)
expect(doc.body.children).eql([el, el2, el3, c, c2])
expect(doc.body.pureChildren).eql([el, el2, el3])
expect(spy.args[3][0]).eql([{
module: 'dom', method: 'addElement',
args: [doc.body.ref, el3.toJSON(), 2] }])
})
it('insert after a comment', () => {
doc.body.appendChild(el)
doc.body.appendChild(c)
doc.body.appendChild(c2)
doc.body.appendChild(el2)
expect(spy.args.length).eql(3)
expect(doc.body.children).eql([el, c, c2, el2])
expect(doc.body.pureChildren).eql([el, el2])
doc.body.insertAfter(el3, c2)
expect(spy.args.length).eql(4)
expect(doc.body.children).eql([el, c, c2, el3, el2])
expect(doc.body.pureChildren).eql([el, el3, el2])
expect(spy.args[3][0]).eql([{
module: 'dom', method: 'addElement',
args: [doc.body.ref, el3.toJSON(), 1] }])
})
it('insert after a comment which has no more element before', () => {
doc.body.appendChild(c)
doc.body.appendChild(c2)
doc.body.appendChild(el)
doc.body.appendChild(el2)
expect(spy.args.length).eql(3)
expect(doc.body.children).eql([c, c2, el, el2])
expect(doc.body.pureChildren).eql([el, el2])
doc.body.insertAfter(el3, c2)
expect(spy.args.length).eql(4)
expect(doc.body.children).eql([c, c2, el3, el, el2])
expect(doc.body.pureChildren).eql([el3, el, el2])
expect(spy.args[3][0]).eql([{
module: 'dom', method: 'addElement',
args: [doc.body.ref, el3.toJSON(), 0] }])
})
})