X-Git-Url: https://git.mdrn.pl/fnpeditor.git/blobdiff_plain/1cbef89f1995ae8bb83d01a58d20b005ad8b3a8f..bb204203f5f64dd13b256263fd398e1463d4eaf9:/src/smartxml/smartxml.test.js diff --git a/src/smartxml/smartxml.test.js b/src/smartxml/smartxml.test.js index 8ae7721..a994f05 100644 --- a/src/smartxml/smartxml.test.js +++ b/src/smartxml/smartxml.test.js @@ -169,40 +169,16 @@ describe('smartxml', function() { it('can change tag name', function() { var node = elementNodeFromXML('
'); - node.setTag('span'); + node = node.setTag('span'); expect(node.getTagName()).to.equal('span'); }); - it('emits nodeTagChange event', function() { - var node = elementNodeFromXML('
'), - spy = sinon.spy(); - - node.document.on('change', spy); - node.setTag('span'); - var event = spy.args[0][0]; - - expect(event.type).to.equal('nodeTagChange'); - expect(event.meta.node.sameNode(node)).to.be.true; - expect(event.meta.oldTagName).to.equal('div'); - }); - describe('Implementation specific expectations', function() { - // DOM specifies ElementNode tag as a read-only property, so - // changing it in a seamless way is a little bit tricky. For this reason - // the folowing expectations are required, despite the fact that they actually are - // motivated by implemetation details. - - it('keeps node in the document', function() { - var doc = getDocumentFromXML('
'), - header = doc.root.contents()[0]; - header.setTag('span'); - expect(header.parent().sameNode(doc.root)).to.be.true; - }); it('keeps custom data', function() { var node = elementNodeFromXML('
'); node.setData('key', 'value'); - node.setTag('header'); + node = node.setTag('header'); expect(node.getTagName()).to.equal('header'); expect(node.getData()).to.eql({key: 'value'}); @@ -214,12 +190,13 @@ describe('smartxml', function() { expect(doc.root.getTagName()).to.equal('span'); }); - it('keeps contents', function() { + it('keeps node contents', function() { var node = elementNodeFromXML('
'); - node.setTag('header'); + node = node.setTag('header'); expect(node.contents()).to.have.length(1); }); }); + }); describe('Setting node attributes', function() { it('can set node attribute', function() { @@ -243,6 +220,19 @@ describe('smartxml', function() { }); }); + describe('Searching for the last child text node', function() { + [ + '
xxx
last
', + '
last
', + '
xxx
last
' + ].forEach(function(xml, i) { + var example = 'example ' + i; + it('returns last child text node ' + example + ')', function() { + var doc = getDocumentFromXML(xml), + lastTextNode = doc.root.getLastTextNode(); + expect(lastTextNode.getText()).to.equal('last', example); + }); + }); }); }); @@ -385,6 +375,15 @@ describe('smartxml', function() { describe('Manipulations', function() { + describe('detaching nodes', function() { + it('can detach document root node', function() { + var doc = getDocumentFromXML('
'); + + doc.root.detach(); + expect(doc.root).to.equal(null); + }); + }); + describe('replacing node with another one', function() { it('replaces node with another one', function() { var doc = getDocumentFromXML('
'), @@ -520,12 +519,33 @@ describe('smartxml', function() { }); }); - it('wraps element node with another element node', function() { + it('wraps root element node with another element node', function() { var node = elementNodeFromXML('
'), wrapper = elementNodeFromXML(''); node.wrapWith(wrapper); expect(node.parent().sameNode(wrapper)).to.be.true; + expect(node.document.root.sameNode(wrapper)).to.be.true; + }); + + it('wraps element node with another element node', function() { + var doc = getDocumentFromXML('
'), + div = doc.root.contents()[0]; + + var wrapper = div.wrapWith({tagName: 'wrapper'}); + expect(wrapper.sameNode(doc.root.contents()[0])).to.equal(true, '1'); + expect(div.parent().sameNode(wrapper)).to.equal(true, '2'); + expect(wrapper.contents()[0].sameNode(div)).to.equal(true, '3'); + }); + + it('wraps element outside of document tree', function() { + var doc = getDocumentFromXML('
'), + node = doc.createDocumentNode({tagName: 'node'}); + + node.wrapWith({tagName: 'wrapper'}); + expect(node.parent().getTagName()).to.equal('wrapper'); + expect(node.parent().contents()[0].sameNode(node)).to.be.true; + expect(doc.root.getTagName()).to.equal('section'); }); it('unwraps element node contents', function() { @@ -754,6 +774,42 @@ describe('smartxml', function() { expect(contents.length).to.equal(1); expect(contents[0].getText()).to.equal('Acat'); }); + it('remove across elements - 4', function() { + var doc = getDocumentFromXML('
Alice
has a cat
'); + doc.deleteText({ + from: { + node: getTextNode('Alice ', doc), + offset: 1 + }, + to: { + node: getTextNode(' cat', doc), + offset: 1 + } + }); + var contents = doc.root.contents(); + expect(contents.length).to.equal(2); + expect(contents[0].getText()).to.equal('A'); + expect(contents[1].getTagName()).to.equal('div'); + expect(contents[1].contents().length).to.equal(1); + expect(contents[1].contents()[0].getText()).to.equal('cat'); + }); + it('removes across elements - 5 (whole document)', function() { + var doc = getDocumentFromXML('
Alice
has a cat
!!!
'); + doc.deleteText({ + from: { + node: getTextNode('Alice ', doc), + offset: 0 + }, + to: { + node: getTextNode('!!!', doc), + offset: 3 + } + }); + + expect(doc.root.getTagName()).to.equal('div'); + expect(doc.root.contents().length).to.equal(1); + expect(doc.root.contents()[0].getText()).to.equal(''); + }); it('removes nodes in between', function() { var doc = getDocumentFromXML('
aaa!xxx!bbb
'); doc.deleteText({ @@ -896,6 +952,7 @@ describe('smartxml', function() { expect(spy.callCount).to.equal(1); expect(event.type).to.equal('nodeMoved'); expect(event.meta.node.sameNode(appended)).to.be.true; + expect(node.document.root.sameNode(event.meta.parent)).to.equal(true, 'previous parent attached to event meta'); }); it('emits nodeAdded event when prepending new node', function() { @@ -1075,6 +1132,16 @@ describe('smartxml', function() { expect(siblingParents.node1.sameNode(aliceText)).to.equal(true, 'aliceText'); expect(siblingParents.node2.sameNode(span)).to.equal(true, 'span'); }); + + it('returns node itself for two same nodes', function() { + var doc = getDocumentFromXML('
'), + div = doc.root.contents()[0]; + + var siblingParents = doc.getSiblingParents({node1: div, node2: div}); + expect(!!siblingParents.node1 && !!siblingParents.node2).to.equal(true, 'nodes defined'); + expect(siblingParents.node1.sameNode(div)).to.be.equal(true, 'node1'); + expect(siblingParents.node2.sameNode(div)).to.be.equal(true, 'node2'); + }); }); }); @@ -1555,6 +1622,101 @@ describe('smartxml', function() { expect(doc.root.getAttr('smart')).to.equal('1'); expect(doc.root.getAttr('unaware')).to.equal('1'); }); + + it('can have associated metadata', function() { + var doc = getDocumentFromXML('
'), + metadata = Object.create({}); + + doc.registerExtension({document: {transformations: { + test: function() { + this.trigger('change'); + } + }}}); + + doc.startTransaction(metadata); + doc.test(); + doc.endTransaction(); + + var transaction = doc.undoStack[0]; + expect(transaction.metadata).to.equal(metadata); + }); + + it('can be rolled back', function() { + var doc = getDocumentFromXML(''); + + doc.startTransaction(); + doc.root.append({tagName: 'div'}); + doc.rollbackTransaction(); + + expect(doc.undoStack.length).to.equal(0, 'nothing to undo'); + expect(doc.root.contents().length).to.equal(0); + }); + + it('rollbacks and calls error handleor if error gets thrown', function() { + var doc = getDocumentFromXML(''), + err = new Error(), + spy = sinon.spy(); + + doc.transaction(function() { + doc.root.append({tagName: 'div'}); + throw err; + }, {error: spy}); + + expect(spy.args[0][0]).to.equal(err); + expect(doc.root.contents().length).to.equal(0); + expect(doc.undoStack.length).to.equal(0); + }); + }); + + describe('Regression tests', function() { + it('redos correctly after running its own undo followed by unaware transformation undo', function() { + var doc = getDocumentFromXML('
'); + + doc.registerExtension({elementNode: {transformations: { + unaware: function() { + this.triggerChangeEvent(); + }, + test: { + impl: function() { + this._$.attr('t', 1); + this.triggerChangeEvent(); + }, + undo: function() { + this._$.attr('t', 0); + } + } + }}}); + doc.root.unaware(); + doc.root.test(); + doc.undo(); + doc.undo(); + doc.redo(); + doc.redo(); + expect(doc.root.getAttr('t')).to.equal('1'); + }); + it('can perform undo of an operation performed after automatic transaction rollback', function() { + var doc = getDocumentFromXML('
'), + extension = {document: {transformations: { + throwingTransformation: function() { throw new Error(); } + }}}; + + doc.registerExtension(extension); + + doc.throwingTransformation(); + + doc.transaction(function() { + doc.root.setAttr('x', '2'); + }); + + expect(doc.undoStack.length).to.equal(1); + expect(doc.root.getAttr('x')).to.equal('2'); + + doc.undo(); + + expect(doc.undoStack.length).to.equal(0); + expect(doc.root.getAttr('x')).to.be.undefined; + + }); }); });