blob: 6dafda4f9e0d5766859631baa5da9f0e535e8f21 [file] [log] [blame]
/*
* Copyright 2010 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 com.google.gwt.user.client.ui;
import com.google.gwt.core.client.GWT;
import com.google.gwt.core.client.GWT.UncaughtExceptionHandler;
import com.google.gwt.core.client.Scheduler;
import com.google.gwt.core.client.Scheduler.ScheduledCommand;
import com.google.gwt.dom.client.BodyElement;
import com.google.gwt.dom.client.Document;
import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.NativeEvent;
import com.google.gwt.dom.client.Text;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.junit.DoNotRunWith;
import com.google.gwt.junit.Platform;
import com.google.gwt.junit.client.GWTTestCase;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.Event;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
/**
* Tests standard DOM operations in the {@link DOM} class.
*/
public class DOMTest extends GWTTestCase {
public static void assertEndsWith(String ending, String testStr) {
if (ending != testStr && (testStr == null || !testStr.endsWith(ending))) {
fail("expected ending=" + ending + " actual=" + testStr);
}
}
/**
* Helper method to return the denormalized child count of a DOM Element. For
* example, child nodes which have a nodeType of Text are included in the
* count, whereas <code>DOM.getChildCount(Element parent)</code> only counts
* the child nodes which have a nodeType of Element.
*
* @param elem the DOM element to check the child count for
* @return The number of child nodes
*/
public static native int getDenormalizedChildCount(Element elem) /*-{
return (elem.childNodes.length);
}-*/;
@Override
public String getModuleName() {
return "com.google.gwt.user.UserTest";
}
/**
* Test DOM.get/set/removeElementAttribute() methods.
*/
public void testElementAttribute() {
Element div = DOM.createDiv();
DOM.setElementAttribute(div, "class", "testClass");
String cssClass = DOM.getElementAttribute(div, "class");
assertEquals("testClass", cssClass);
DOM.removeElementAttribute(div, "class");
cssClass = DOM.getElementAttribute(div, "class");
assertEquals("", cssClass);
}
@Override
protected void reportUncaughtException(Throwable ex) {
if (!ex.getMessage().contains("_expected_")) {
super.reportUncaughtException(ex);
}
}
/**
* Tests that {@link DOM#eventGetCurrentEvent()} returns the event to the
* {@link UncaughtExceptionHandler}.
*/
public void testEventGetCurrentEventOnException() {
Button button = new Button("test", new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
// Intentionally trigger an error
throw new IllegalArgumentException("_expected_");
}
});
RootPanel.get().add(button);
// Verify the exception is captured
final List<String> ret = new ArrayList<String>();
GWT.setUncaughtExceptionHandler(new UncaughtExceptionHandler() {
@Override
public void onUncaughtException(Throwable e) {
Event event = DOM.eventGetCurrentEvent();
if (event == null) {
ret.add("Event is null");
return;
}
if (event.getTypeInt() != Event.ONCLICK) {
ret.add("Event is not a click event");
return;
}
ret.add("Success");
}
});
NativeEvent clickEvent = Document.get().createClickEvent(0, 0, 0, 0, 0,
false, false, false, false);
button.getElement().dispatchEvent(clickEvent);
assertEquals(1, ret.size());
assertEquals("Success", ret.get(0));
RootPanel.get().remove(button);
}
/**
* Tests {@link DOM#getAbsoluteLeft(Element)} and
* {@link DOM#getAbsoluteTop(Element)}.
*/
public void testGetAbsolutePosition() {
final int border = 8;
final int margin = 9;
final int padding = 10;
final int top = 15;
final int left = 14;
final Element elem = DOM.createDiv();
DOM.appendChild(RootPanel.getBodyElement(), elem);
DOM.setStyleAttribute(elem, "position", "absolute");
DOM.setStyleAttribute(elem, "border", border + "px solid #000");
DOM.setStyleAttribute(elem, "padding", padding + "px");
DOM.setStyleAttribute(elem, "margin", margin + "px");
Document doc = Document.get();
DOM.setStyleAttribute(elem, "top", (top - doc.getBodyOffsetLeft()) + "px");
DOM.setStyleAttribute(elem, "left", (left - doc.getBodyOffsetTop()) + "px");
delayTestFinish(1000);
Scheduler.get().scheduleDeferred(new ScheduledCommand() {
@Override
public void execute() {
assertEquals(top + margin, DOM.getAbsoluteTop(elem));
assertEquals(left + margin, DOM.getAbsoluteLeft(elem));
finishTest();
}
});
}
/**
* Tests {@link DOM#getAbsoluteTop(Element)} and
* {@link DOM#getAbsoluteLeft(Element)} for consistency when the element
* contains children and has scrollbars. See issue #1093 for more details.
*
*/
@DoNotRunWith(Platform.HtmlUnitLayout)
public void testGetAbsolutePositionWhenScrolled() {
final Element outer = DOM.createDiv();
final Element inner = DOM.createDiv();
DOM.setStyleAttribute(outer, "position", "absolute");
DOM.setStyleAttribute(outer, "top", "0px");
DOM.setStyleAttribute(outer, "left", "0px");
DOM.setStyleAttribute(outer, "overflow", "auto");
DOM.setStyleAttribute(outer, "width", "200px");
DOM.setStyleAttribute(outer, "height", "200px");
DOM.setStyleAttribute(inner, "marginTop", "800px");
DOM.setStyleAttribute(inner, "marginLeft", "800px");
DOM.appendChild(outer, inner);
DOM.appendChild(RootPanel.getBodyElement(), outer);
DOM.setInnerText(inner, ":-)");
DOM.scrollIntoView(inner);
// Ensure that we are scrolled.
assertTrue(DOM.getElementPropertyInt(outer, "scrollTop") > 0);
assertTrue(DOM.getElementPropertyInt(outer, "scrollLeft") > 0);
Document doc = Document.get();
assertEquals(doc.getBodyOffsetLeft(), DOM.getAbsoluteLeft(outer));
assertEquals(doc.getBodyOffsetTop(), DOM.getAbsoluteTop(outer));
}
/**
* Tests {@link DOM#getAbsoluteTop(Element)} and
* {@link DOM#getAbsoluteLeft(Element)} for consistency when the parent
* element has a border.
*
*/
public void testGetAbsolutePositionWithPixelBorders() {
final Element outer = DOM.createDiv();
final Element inner = DOM.createDiv();
outer.getStyle().setProperty("position", "relative");
outer.getStyle().setProperty("width", "200px");
outer.getStyle().setProperty("height", "200px");
inner.getStyle().setProperty("position", "absolute");
inner.getStyle().setProperty("top", "30px");
inner.getStyle().setProperty("left", "40px");
inner.setInnerText("inner");
outer.appendChild(inner);
RootPanel.getBodyElement().appendChild(outer);
// Get the position without a border
int absTop = inner.getAbsoluteTop();
int absLeft = inner.getAbsoluteLeft();
// Get the position with a border
outer.getStyle().setProperty("border", "2px solid blue");
assertEquals(2, inner.getAbsoluteTop() - absTop);
assertEquals(2, inner.getAbsoluteLeft() - absLeft);
}
/**
* Tests getAbsoluteLeft/Top() for the document.body element. This used to
* cause exceptions to be thrown on Opera (see issue 1556).
*/
public void testGetBodyAbsolutePosition() {
try {
// The body's absolute left/top depends upon the browser, but we just
// need to make sure nothing goes wrong reading them.
BodyElement body = Document.get().getBody();
body.getAbsoluteLeft();
body.getAbsoluteTop();
} catch (Throwable e) {
fail("Exception occurred getting the absolute position of the body");
}
}
/**
* Tests the ability to do a parent-ward walk in the DOM.
*/
public void testGetParent() {
Element element = RootPanel.get().getElement();
int i = 0;
while (i < 10 && element != null) {
element = DOM.getParent(element);
i++;
}
// If we got here we looped "forever" or passed, as no exception was thrown.
if (i == 10) {
fail("Cyclic parent structure detected.");
}
// If we get here, we pass, because we encountered no errors going to the
// top of the parent hierarchy.
}
/**
* Tests {@link DOM#insertChild(Element, Element, int)}.
*
*/
public void testInsertChild() {
Element parent = RootPanel.get().getElement();
Element div = DOM.createDiv();
DOM.insertChild(parent, div, Integer.MAX_VALUE);
Element child = DOM.getChild(RootPanel.get().getElement(),
DOM.getChildCount(parent) - 1);
assertTrue(div == child);
}
/**
* Tests that {@link DOM#isOrHasChild(Element, Element)} works consistently
* across browsers.
*/
public void testIsOrHasChild() {
Document doc = Document.get();
Element div = DOM.createDiv();
Element childDiv = DOM.createDiv();
Text text = Document.get().createTextNode("text");
// unattached, not related
assertFalse(div.isOrHasChild(childDiv));
assertFalse(div.isOrHasChild(text));
assertTrue(div.isOrHasChild(div));
assertTrue(text.isOrHasChild(text));
assertFalse(doc.isOrHasChild(div));
assertFalse(doc.isOrHasChild(text));
assertFalse(div.isOrHasChild(doc));
assertFalse(text.isOrHasChild(doc));
// unattached, related
div.appendChild(childDiv);
childDiv.appendChild(text);
assertTrue(div.isOrHasChild(childDiv));
assertTrue(childDiv.isOrHasChild(text));
assertFalse(childDiv.isOrHasChild(div));
assertFalse(text.isOrHasChild(childDiv));
assertFalse(doc.isOrHasChild(div));
assertFalse(doc.isOrHasChild(text));
assertFalse(div.isOrHasChild(doc));
assertFalse(text.isOrHasChild(doc));
// attached, related
DOM.appendChild(RootPanel.getBodyElement(), div);
assertTrue(div.isOrHasChild(childDiv));
assertTrue(childDiv.isOrHasChild(text));
assertTrue(div.isOrHasChild(div));
assertTrue(text.isOrHasChild(text));
assertFalse(childDiv.isOrHasChild(div));
assertFalse(text.isOrHasChild(childDiv));
assertTrue(doc.isOrHasChild(div));
assertTrue(doc.isOrHasChild(text));
assertFalse(div.isOrHasChild(Document.get()));
assertFalse(text.isOrHasChild(Document.get()));
}
/**
* Tests that {@link DOM#setInnerText(Element, String)} works consistently
* across browsers.
*/
public void testSetInnerText() {
Element tableElem = DOM.createTable();
Element trElem = DOM.createTR();
Element tdElem = DOM.createTD();
DOM.setInnerText(tdElem, "Some Table Heading Data");
// Add a <em> element as a child to the td element
Element emElem = DOM.createElement("em");
DOM.setInnerText(emElem, "Some emphasized text");
DOM.appendChild(tdElem, emElem);
DOM.appendChild(trElem, tdElem);
DOM.appendChild(tableElem, trElem);
DOM.appendChild(RootPanel.getBodyElement(), tableElem);
DOM.setInnerText(tdElem, null);
// Once we set the inner text on an element to null, all of the element's
// child nodes
// should be deleted, including any text nodes, for all supported browsers.
assertTrue(getDenormalizedChildCount(tdElem) == 0);
}
/**
* Tests {@link DOM#toString(Element)} against likely failure points.
*/
public void testToString() {
Button b = new Button("abcdef");
assertTrue(b.toString().indexOf("abcdef") != -1);
assertTrue(b.toString().toLowerCase(Locale.ROOT).indexOf("button") != -1);
// Test <img src="http://.../logo.gif" />
Element image = DOM.createImg();
String imageUrl = "http://www.google.com/images/logo.gif";
DOM.setImgSrc(image, imageUrl);
String imageToString = DOM.toString(image).trim().toLowerCase(Locale.ROOT);
assertTrue(imageToString.startsWith("<img"));
assertTrue(imageToString.indexOf(imageUrl) != -1);
// Test <input name="flinks" />
Element input = DOM.createInputText();
DOM.setElementProperty(input, "name", "flinks");
final String inputToString = DOM.toString(input).trim().toLowerCase(Locale.ROOT);
assertTrue(inputToString.startsWith("<input"));
// Test <select><option>....</select>
Element select = DOM.createSelect();
for (int i = 0; i < 10; i++) {
final Element option = DOM.createElement("option");
DOM.appendChild(select, option);
DOM.setInnerText(option, "item #" + i);
}
String selectToString = DOM.toString(select).trim().toLowerCase(Locale.ROOT);
assertTrue(selectToString.startsWith("<select"));
for (int i = 0; i < 10; i++) {
assertTrue(selectToString.indexOf("item #" + i) != -1);
}
// Test <meta name="robots" />
Element meta = DOM.createElement("meta");
DOM.setElementProperty(meta, "name", "robots");
String metaToString = DOM.toString(meta).trim().toLowerCase(Locale.ROOT);
assertTrue(metaToString.startsWith("<meta"));
}
}