/**
* Copyright 2008 Google Inc.
*
* Licensed 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.
*
*/
package org.waveprotocol.wave.client.editor.extract;
import com.google.gwt.dom.client.Document;
import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.Node;
import com.google.gwt.junit.client.GWTTestCase;
import org.waveprotocol.wave.client.editor.content.ContentDocument;
import org.waveprotocol.wave.client.editor.content.ContentElement;
import org.waveprotocol.wave.client.editor.content.ContentNode;
import org.waveprotocol.wave.client.editor.content.ContentRawDocument;
import org.waveprotocol.wave.client.editor.content.ContentTextNode;
import org.waveprotocol.wave.client.editor.testing.TestEditors;
import org.waveprotocol.wave.model.document.ReadableDocument;
import org.waveprotocol.wave.model.document.util.Point;
import org.waveprotocol.wave.model.document.util.Point.El;
/**
* Test the repairer
*
* When testing the revert method, we directly access revertInner, because the normal
* method has some more failsafe code to handle the case of invalid arguments. This could
* skew our test cases.
*
* @author danilatos@google.com (Daniel Danilatos)
*/
public class RepairerGwtTest extends GWTTestCase {
ContentRawDocument c = null;
Repairer r = null;
ContentDocument dom = null;
ContentElement root, p, b;
ContentTextNode t1, t2, t3;
MockRepairListener l = new MockRepairListener();
@Override
public String getModuleName() {
return "org.waveprotocol.wave.client.editor.extract.Tests";
}
public void testRevertEmptyElement() {
init();
initEmptyP();
c.insertBefore(root, p, null);
// essentially a noop
assertTrue(p.isConsistent());
p.revertImplementation();
assertTrue(p.isConsistent());
// add some random text node
p.getImplNodelet().appendChild(Document.get().createTextNode("blah"));
p.revertImplementation();
assertEquals(null, p.getImplNodelet().getFirstChild());
p.getImplNodelet().appendChild(ca());
p.revertImplementation();
assertEquals(null, p.getImplNodelet().getFirstChild());
p.getImplNodelet().appendChild(ca());
p.revertImplementation();
assertEquals(null, p.getImplNodelet().getFirstChild());
l.check(0, 0); // calls through revertInner, so none reported
}
public void testRevertComplicatedStructure() {
init();
initHelloThere();
// no-op
p.revertImplementation();
assertEquals("hello <b>th</b>ere", p.getImplNodelet().getInnerHTML());
// blow it all away
p.getImplNodelet().setInnerHTML("");
assertEquals("", p.getImplNodelet().getInnerHTML());
p.revertImplementation();
assertEquals("hello <b>th</b>ere", p.getImplNodelet().getInnerHTML());
// insert stuff at start and end
p.getImplNodelet().appendChild(ca());
t1.getImplNodelet().getParentNode().insertBefore(ca(), t1.getImplNodelet());
assertEquals("<a></a>hello <b>th</b>ere<a></a>", p.getImplNodelet().getInnerHTML());
p.revertImplementation();
assertEquals("hello <b>th</b>ere", p.getImplNodelet().getInnerHTML());
// insert stuff inside inner element
b.getImplNodelet().appendChild(ca());
p.revertImplementation();
assertEquals("hello <b>th</b>ere", p.getImplNodelet().getInnerHTML());
// reorganise stuff
b.getImplNodelet().appendChild(t3.getImplNodelet());
assertEquals("hello <b>there</b>", p.getImplNodelet().getInnerHTML());
p.revertImplementation();
assertEquals("hello <b>th</b>ere", p.getImplNodelet().getInnerHTML());
l.check(0, 0); // calls through revertInner, so none reported
}
public void testRevertRange() {
init();
initHelloThere();
// blow it all away
p.getImplNodelet().setInnerHTML("");
assertEquals("", p.getImplNodelet().getInnerHTML());
r.revertInner(Point.before(c, t1), Point.after(c, t3));
assertEquals("hello <b>th</b>ere", p.getImplNodelet().getInnerHTML());
// insert stuff at start and end, revert whole p
p.getImplNodelet().appendChild(ca());
t1.getImplNodelet().getParentNode().insertBefore(ca(), t1.getImplNodelet());
r.revertInner(Point.before(c, p), Point.after(c, p));
assertEquals("hello <b>th</b>ere", p.getImplNodelet().getInnerHTML());
// insert stuff at start and end, revert parts of p
p.getImplNodelet().appendChild(ca());
t1.getImplNodelet().getParentNode().insertBefore(ca(), t1.getImplNodelet());
r.revertInner(Point.before(c, t1), Point.before(c, t3));
assertEquals("hello <b>th</b>ere<a></a>", p.getImplNodelet().getInnerHTML());
r.revertInner(Point.before(c, t1), Point.end((ContentNode)p));
assertEquals("hello <b>th</b>ere", p.getImplNodelet().getInnerHTML());
// insert stuff inside inner element
b.getImplNodelet().appendChild(ca());
r.revertInner(Point.after(c, t1), Point.before(c, t3));
assertEquals("hello <b>th</b>ere", p.getImplNodelet().getInnerHTML());
// reorganise stuff
b.getImplNodelet().appendChild(t3.getImplNodelet());
assertEquals("hello <b>there</b>", p.getImplNodelet().getInnerHTML());
r.revertInner(Point.before(c, t1), Point.after(c, t3));
assertEquals("hello <b>th</b>ere", p.getImplNodelet().getInnerHTML());
// wrap stuff randomly
b.getImplNodelet().appendChild(t3.getImplNodelet());
assertEquals("hello <b>there</b>", p.getImplNodelet().getInnerHTML());
Element wrapper = ca();
Node bit = t1.getImplNodelet().splitText(2);
bit.getParentNode().insertBefore(wrapper, bit);
wrapper.appendChild(bit);
r.revertInner(Point.before(c, t1), Point.after(c, t3));
assertEquals("hello <b>th</b>ere", p.getImplNodelet().getInnerHTML());
l.check(0, 0); // calls through revertInner, so none reported
}
public void testReporting() {
init();
initHelloThere();
l.check(0, 0);
// fix and revert a range
p.getImplNodelet().appendChild(ca());
t1.getImplNodelet().getParentNode().insertBefore(ca(), t1.getImplNodelet());
r.revert(Point.before(c, t1), Point.before(c, t3));
assertEquals("hello <b>th</b>ere<a></a>", p.getImplNodelet().getInnerHTML());
l.check(0, 1);
// revert everything
r.revert(c, c.getDocumentElement());
assertEquals("hello <b>th</b>ere", p.getImplNodelet().getInnerHTML());
l.check(1, 1);
}
protected Element ca() {
return Document.get().createAnchorElement();
}
protected void init() {
dom = TestEditors.createTestDocument();
Repairer.debugRepairIsFatal = false;
r = new Repairer(dom.getContext().persistentView(),
dom.getContext().rendering().getRenderedContentView(),
dom.getStrippingHtmlView(), l);
c = dom.debugGetRawDocument();
root = c.getDocumentElement();
l.clear();
}
protected void initEmptyP() {
p = c.createElement("q", root, null);
}
protected void initHelloThere() {
initEmptyP();
b = c.createElement("b", p, null);
t2 = c.createTextNode("th", b, null);
t1 = c.createTextNode("hello ", p, b);
t3 = c.createTextNode("ere", p, null);
}
public class MockRepairListener implements RepairListener {
private int full = 0;
private int range = 0;
@Override
public void onFullDocumentRevert(
ReadableDocument<ContentNode, ContentElement, ContentTextNode> doc) {
full++;
}
@Override
public void onRangeRevert(El<ContentNode> start, El<ContentNode> end) {
range++;
}
public void check(int full, int range) {
assertEquals(full, this.full);
assertEquals(range, this.range);
}
public void clear() {
full = 0;
range = 0;
}
public void fail() {
RepairerGwtTest.this.fail("Ended up with " + full + ", " + range);
}
}
}