001: /*
002: * Copyright (c) 2002-2008 Gargoyle Software Inc. All rights reserved.
003: *
004: * Redistribution and use in source and binary forms, with or without
005: * modification, are permitted provided that the following conditions are met:
006: *
007: * 1. Redistributions of source code must retain the above copyright notice,
008: * this list of conditions and the following disclaimer.
009: * 2. Redistributions in binary form must reproduce the above copyright notice,
010: * this list of conditions and the following disclaimer in the documentation
011: * and/or other materials provided with the distribution.
012: * 3. The end-user documentation included with the redistribution, if any, must
013: * include the following acknowledgment:
014: *
015: * "This product includes software developed by Gargoyle Software Inc.
016: * (http://www.GargoyleSoftware.com/)."
017: *
018: * Alternately, this acknowledgment may appear in the software itself, if
019: * and wherever such third-party acknowledgments normally appear.
020: * 4. The name "Gargoyle Software" must not be used to endorse or promote
021: * products derived from this software without prior written permission.
022: * For written permission, please contact info@GargoyleSoftware.com.
023: * 5. Products derived from this software may not be called "HtmlUnit", nor may
024: * "HtmlUnit" appear in their name, without prior written permission of
025: * Gargoyle Software Inc.
026: *
027: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
028: * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
029: * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GARGOYLE
030: * SOFTWARE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
031: * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
032: * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
033: * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
034: * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
035: * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
036: * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
037: */
038: package com.gargoylesoftware.htmlunit.libraries;
039:
040: import java.net.URL;
041: import java.text.SimpleDateFormat;
042: import java.util.ArrayList;
043: import java.util.Calendar;
044: import java.util.HashMap;
045: import java.util.HashSet;
046: import java.util.List;
047: import java.util.Map;
048: import java.util.Set;
049:
050: import org.mortbay.jetty.Server;
051:
052: import com.gargoylesoftware.htmlunit.BrowserVersion;
053: import com.gargoylesoftware.htmlunit.CollectingAlertHandler;
054: import com.gargoylesoftware.htmlunit.HttpWebConnectionTest;
055: import com.gargoylesoftware.htmlunit.WebClient;
056: import com.gargoylesoftware.htmlunit.WebTestCase;
057: import com.gargoylesoftware.htmlunit.html.DomNode;
058: import com.gargoylesoftware.htmlunit.html.DomText;
059: import com.gargoylesoftware.htmlunit.html.HtmlAnchor;
060: import com.gargoylesoftware.htmlunit.html.HtmlButton;
061: import com.gargoylesoftware.htmlunit.html.HtmlDivision;
062: import com.gargoylesoftware.htmlunit.html.HtmlInput;
063: import com.gargoylesoftware.htmlunit.html.HtmlPage;
064: import com.gargoylesoftware.htmlunit.html.HtmlSelect;
065: import com.gargoylesoftware.htmlunit.html.HtmlSpan;
066: import com.gargoylesoftware.htmlunit.html.HtmlTableDataCell;
067: import com.gargoylesoftware.htmlunit.html.HtmlTableRow;
068: import com.gargoylesoftware.htmlunit.html.UnknownHtmlElement;
069:
070: /**
071: * Tests for 1.4 version of <a href="http://code.google.com/webtoolkit">Google Web Toolkit</a>.
072: *
073: * @version $Revision: 2132 $
074: * @author Ahmed Ashour
075: */
076: public class GWT14Test extends WebTestCase {
077:
078: private Server server_;
079:
080: /**
081: * Creates an instance.
082: *
083: * @param name The name of the test.
084: */
085: public GWT14Test(final String name) {
086: super (name);
087: }
088:
089: /**
090: * @throws Exception If an error occurs.
091: */
092: public void testHello() throws Exception {
093: final List collectedAlerts = new ArrayList();
094: final HtmlPage page = loadPage(BrowserVersion.getDefault(),
095: collectedAlerts);
096: final HtmlButton button = (HtmlButton) page
097: .getFirstByXPath("//button");
098: final DomText buttonLabel = (DomText) button.getChildIterator()
099: .next();
100: assertEquals("Click me", buttonLabel.getData());
101: button.click();
102: final String[] expectedAlerts = { "Hello, AJAX" };
103: assertEquals(expectedAlerts, collectedAlerts);
104: }
105:
106: /**
107: * Test I18N default language
108: *
109: * @throws Exception If an error occurs.
110: */
111: public void testI18N() throws Exception {
112: final HtmlPage page = loadPage(BrowserVersion.getDefault(),
113: null);
114: testI18N(page, "numberFormatOutputText", "31,415,926,535.898");
115:
116: String timeZone = new SimpleDateFormat("Z")
117: .format(new SimpleDateFormat("d MMMMMMMM yyyy")
118: .parse("13 September 1999"));
119: timeZone = timeZone.substring(0, 3) + ':'
120: + timeZone.substring(3);
121:
122: testI18N(page, "dateTimeFormatOutputText",
123: "Monday, September 13, 1999 12:00:00 AM GMT" + timeZone);
124: testI18N(
125: page,
126: "messagesFormattedOutputText",
127: "User 'amelie' has security clearance 'guest' and cannot access '/secure/blueprints.xml'");
128: testI18N(page, "constantsFirstNameText", "Amelie");
129: testI18N(page, "constantsLastNameText", "Crutcher");
130: testI18N(page, "constantsFavoriteColorList", new String[] {
131: "Red", "White", "Yellow", "Black", "Blue", "Green",
132: "Grey", "Light Grey" });
133: testI18N(page, "constantsWithLookupResultsText", "Red");
134: final Map map = new HashMap();
135: map.put("name", "Amelie Crutcher");
136: map.put("timeZone", "EST");
137: map.put("userID", "123");
138: map.put("lastLogOn", "2/2/2006");
139: testI18NDictionary(page, map);
140: }
141:
142: /**
143: * Test I18N French language
144: *
145: * @throws Exception If an error occurs.
146: */
147: public void testI18N_fr() throws Exception {
148: server_ = HttpWebConnectionTest
149: .startWebServer("src/test/resources/gwt/"
150: + getDirectory() + "/I18N");
151: final WebClient client = new WebClient();
152:
153: final String url = "http://localhost:"
154: + HttpWebConnectionTest.PORT + "/I18N.html?locale=fr";
155: final HtmlPage page = (HtmlPage) client.getPage(url);
156: page.getEnclosingWindow().getThreadManager().joinAll(10000);
157:
158: //visible space in browser is not normal space but '\u00A0' instead, as noted by the following test in browser:
159: /*
160: var tr = document.getElementById('numberFormatOutputText');
161: var value = tr.childNodes[0].childNodes[0].nodeValue;
162: var output = '';
163: for( var i=0; i < value.length; i++ ) {
164: output += value.charCodeAt(i) + ' ';
165: }
166: alert(output);
167: */
168: testI18N(page, "numberFormatOutputText",
169: "31\u00A0415\u00A0926\u00A0535,898");
170:
171: String timeZone = new SimpleDateFormat("Z")
172: .format(new SimpleDateFormat("d MMMMMMMM yyyy")
173: .parse("13 September 1999"));
174: timeZone = timeZone.substring(0, 3) + ':'
175: + timeZone.substring(3);
176:
177: testI18N(page, "dateTimeFormatOutputText",
178: "lundi 13 septembre 1999 00 h 00 GMT" + timeZone);
179: testI18N(
180: page,
181: "messagesFormattedOutputText",
182: "L'utilisateur 'amelie' a un niveau de securit\u00E9 'guest', "
183: + "et ne peut acc\u00E9der \u00E0 '/secure/blueprints.xml'");
184: testI18N(page, "constantsFirstNameText", "Amelie");
185: testI18N(page, "constantsLastNameText", "Crutcher");
186: testI18N(page, "constantsFavoriteColorList", new String[] {
187: "Rouge", "Blanc", "Jaune", "Noir", "Bleu", "Vert",
188: "Gris", "Gris clair" });
189: testI18N(page, "constantsWithLookupResultsText", "Rouge");
190: final Map map = new HashMap();
191: map.put("name", "Amelie Crutcher");
192: map.put("timeZone", "EST");
193: map.put("userID", "123");
194: map.put("lastLogOn", "2/2/2006");
195: testI18NDictionary(page, map);
196: }
197:
198: /**
199: * Test value inside {@link HtmlDivision} or {@link HtmlInput}
200: * @param page The page to load.
201: * @param id id of the element to search for.
202: * @param expectedValue Expected value of the value inside the element
203: * @throws Exception If the test fails.
204: */
205: private void testI18N(final HtmlPage page, final String id,
206: final String expectedValue) {
207: final HtmlTableDataCell cell = (HtmlTableDataCell) page
208: .getHtmlElementById(id);
209: testTableDataCell(cell, expectedValue);
210: }
211:
212: /**
213: * Test value inside {@link HtmlDivision}, {@link HtmlInput} or {@link DomText}
214: *
215: * @param cell the cells to search in.
216: * @param expectedValue Expected value of the value inside the cell
217: * @throws Exception If the test fails.
218: */
219: private void testTableDataCell(final HtmlTableDataCell cell,
220: final String expectedValue) {
221: final Object child = cell.getFirstDomChild();
222: if (child instanceof HtmlDivision) {
223: final HtmlDivision div = (HtmlDivision) child;
224: DomNode firstChild = div.getFirstDomChild();
225: if (firstChild instanceof UnknownHtmlElement
226: && (firstChild.getNodeName().equals("b") || firstChild
227: .getNodeName().equals("i"))) {
228: firstChild = firstChild.getFirstDomChild();
229: }
230: if (firstChild instanceof DomText) {
231: final DomText text = (DomText) firstChild;
232: assertEquals(expectedValue, text.getData());
233: } else {
234: fail("Could not find '" + expectedValue + "'");
235: }
236: } else if (child instanceof HtmlInput) {
237: final HtmlInput input = (HtmlInput) child;
238: assertEquals(expectedValue, input.getValueAttribute());
239: } else if (child instanceof DomText) {
240: final DomText text = (DomText) child;
241: assertEquals(expectedValue, text.getData());
242: } else {
243: fail("Could not find '" + expectedValue + "'");
244: }
245: }
246:
247: /**
248: * Test value of {@link HtmlSelect}
249: *
250: * @param page The page to load.
251: * @param id id of the element to search for.
252: * @param expectedValues Expected value of the value inside the select.
253: * @throws Exception If the test fails.
254: */
255: private void testI18N(final HtmlPage page, final String id,
256: final String[] expectedValues) {
257: final HtmlTableDataCell cell = (HtmlTableDataCell) page
258: .getHtmlElementById(id);
259: final Object child = cell.getFirstDomChild();
260: if (child instanceof HtmlSelect) {
261: final HtmlSelect select = (HtmlSelect) child;
262: assertEquals(expectedValues.length, select.getOptionSize());
263: for (int i = 0; i < expectedValues.length; i++) {
264: assertEquals(expectedValues[i], select.getOption(i)
265: .getFirstDomChild().getNodeValue());
266: }
267: } else {
268: fail("Could not find '" + expectedValues + "'");
269: }
270: }
271:
272: private void testI18NDictionary(final HtmlPage page,
273: final Map expectedMap) throws Exception {
274: final HtmlTableRow headerRow = (HtmlTableRow) page
275: .getFirstByXPath("//*[@class='i18n-dictionary-header-row']");
276: final HtmlTableRow valueRow = (HtmlTableRow) headerRow
277: .getNextDomSibling();
278: DomNode headerNode = headerRow.getFirstDomChild();
279: DomNode valueNode = valueRow.getFirstDomChild();
280: final Set foundHeaders = new HashSet();
281: for (int i = 0; i < expectedMap.size(); i++) {
282: final String header = headerNode.getFirstDomChild()
283: .getNodeValue();
284: final String value = valueNode.getFirstDomChild()
285: .getNodeValue();
286:
287: assertNotNull(expectedMap.get(header));
288: assertEquals(expectedMap.get(header), value);
289: foundHeaders.add(header);
290:
291: valueNode = valueNode.getNextDomSibling();
292: headerNode = headerNode.getNextDomSibling();
293: }
294: assertEquals(expectedMap.size(), foundHeaders.size());
295: }
296:
297: /**
298: * @throws Exception If an error occurs.
299: */
300: public void testSimpleXML() throws Exception {
301: final HtmlPage page = loadPage(BrowserVersion.getDefault(),
302: null);
303:
304: final String[] pendingOrders = { "123-2", "3 45122 34566",
305: "2/2/2004", "43 Butcher lane", "Atlanta", "Georgia",
306: "30366" };
307:
308: //try 20 times to wait .5 second each for filling the page.
309: for (int i = 0; i < 20; i++) {
310: if (page.getByXPath(
311: "//table[@class='userTable'][1]//tr[2]/td").size() == pendingOrders.length) {
312: break;
313: }
314: synchronized (page) {
315: page.wait(500);
316: }
317: }
318:
319: final List cells = page
320: .getByXPath("//table[@class='userTable'][1]//tr[2]/td");
321: assertEquals(pendingOrders.length, cells.size());
322: for (int i = 0; i < pendingOrders.length; i++) {
323: final HtmlTableDataCell cell = (HtmlTableDataCell) cells
324: .get(i);
325: testTableDataCell(cell, pendingOrders[i]);
326: }
327: }
328:
329: /**
330: * @throws Exception If an error occurs.
331: */
332: public void testMail() throws Exception {
333: final HtmlPage page = loadPage(BrowserVersion.getDefault(),
334: null);
335: final HtmlTableDataCell cell = (HtmlTableDataCell) page
336: .getFirstByXPath("//table[@class='mail-TopPanel']//div[@class='gwt-HTML']//..");
337: testTableDataCell(cell, "Welcome back, foo@example.com");
338:
339: final String[] selectedRow = { "markboland05",
340: "mark@example.com",
341: "URGENT -[Mon, 24 Apr 2006 02:17:27 +0000]" };
342:
343: final List selectedRowCells = page
344: .getByXPath("//tr[@class='mail-SelectedRow']/td");
345: assertEquals(selectedRow.length, selectedRowCells.size());
346: for (int i = 0; i < selectedRow.length; i++) {
347: final HtmlTableDataCell selectedRowCell = (HtmlTableDataCell) selectedRowCells
348: .get(i);
349: testTableDataCell(selectedRowCell, selectedRow[i]);
350: }
351:
352: final List detailsCells = page
353: .getByXPath("//div[@class='mail-DetailBody']/text()");
354: final String[] details = {
355: "Dear Friend,",
356: "I am Mr. Mark Boland the Bank Manager of ABN AMRO BANK 101 Moorgate, London, EC2M 6SB." };
357: for (int i = 0; i < details.length; i++) {
358: final DomText text = (DomText) detailsCells.get(i);
359: assertEquals(details[i], text.getData());
360: }
361: }
362:
363: /**
364: * @throws Exception If an error occurs.
365: */
366: public void testJSON() throws Exception {
367: final HtmlPage page = loadPage(BrowserVersion.getDefault(),
368: null);
369: final HtmlButton button = (HtmlButton) page
370: .getFirstByXPath("//button");
371: button.click();
372:
373: //try 20 times to wait .5 second each for filling the page.
374: for (int i = 0; i < 20; i++) {
375: if (page
376: .getFirstByXPath("//div[@class='JSON-JSONResponseObject']") != null) {
377: break;
378: }
379: synchronized (page) {
380: page.wait(500);
381: }
382: }
383:
384: final HtmlSpan span = (HtmlSpan) page
385: .getFirstByXPath("//div[@class='JSON-JSONResponseObject']/span/div/table//td[2]/span/span");
386: assertEquals("ResultSet", span.getFirstDomChild()
387: .getNodeValue());
388: }
389:
390: /**
391: * @throws Exception If an error occurs.
392: */
393: public void testDynaTable() throws Exception {
394: server_ = HttpWebConnectionTest.startWebServer(
395: "src/test/resources/gwt/" + getDirectory()
396: + "/DynaTable",
397: new String[] { "src/test/resources/gwt/"
398: + getDirectory() + "/gwt-servlet.jar" });
399:
400: final WebClient client = new WebClient();
401:
402: final String url = "http://localhost:"
403: + HttpWebConnectionTest.PORT + "/DynaTable.html";
404: final HtmlPage page = (HtmlPage) client.getPage(url);
405:
406: final String[] firstRow = { "Inman Mendez",
407: "Majoring in Phrenology",
408: "Mon 9:45-10:35, Tues 2:15-3:05, Fri 8:45-9:35, Fri 9:45-10:35" };
409:
410: //try 40 times to wait .5 second each for filling the page.
411: for (int i = 0; i < 40; i++) {
412: final List detailsCells = page
413: .getByXPath("//table[@class='table']//tr[2]/td");
414: if (detailsCells.size() == firstRow.length) {
415: final HtmlTableDataCell firstCell = (HtmlTableDataCell) detailsCells
416: .get(0);
417: if (firstCell.getFirstDomChild().getNodeValue().equals(
418: firstRow[0])) {
419: break;
420: }
421: }
422: synchronized (page) {
423: page.wait(500);
424: }
425: }
426:
427: final List detailsCells = page
428: .getByXPath("//table[@class='table']//tr[2]/td");
429: assertEquals(firstRow.length, detailsCells.size());
430: for (int i = 0; i < firstRow.length; i++) {
431: final HtmlTableDataCell cell = (HtmlTableDataCell) detailsCells
432: .get(i);
433: testTableDataCell(cell, firstRow[i]);
434: }
435: }
436:
437: /**
438: * @throws Exception If an error occurs.
439: */
440: public void testKitchenSink() throws Exception {
441: server_ = HttpWebConnectionTest
442: .startWebServer("src/test/resources/gwt/"
443: + getDirectory() + "/KitchenSink");
444: final WebClient client = new WebClient();
445:
446: final String url = "http://localhost:"
447: + HttpWebConnectionTest.PORT + "/KitchenSink.html";
448: final HtmlPage page = (HtmlPage) client.getPage(url);
449: page.getEnclosingWindow().getThreadManager().joinAll(3000);
450:
451: HtmlDivision infoDiv = (HtmlDivision) page
452: .getFirstByXPath("//div[@class='ks-Info']");
453: assertEquals("Introduction to the Kitchen Sink", infoDiv
454: .getFirstDomChild().getFirstDomChild().getNodeValue());
455:
456: final HtmlAnchor widgetAnchor = page
457: .getAnchorByHref("#Widgets");
458: widgetAnchor.click();
459:
460: infoDiv = (HtmlDivision) page
461: .getFirstByXPath("//div[@class='ks-Info']");
462: assertEquals("Basic Widgets", infoDiv.getFirstDomChild()
463: .getFirstDomChild().getNodeValue());
464: }
465:
466: /**
467: * Returns the GWT directory being tested.
468: * @return the GWT directory being tested.
469: */
470: protected String getDirectory() {
471: return "1.4.60";
472: }
473:
474: /**
475: * Loads the GWT unit test index page using the specified browser version, and test name.
476: *
477: * @param version The browser version to use.
478: * @param collectedAlerts The List to collect alerts into.
479: * @throws Exception if an error occurs.
480: * @return The loaded page.
481: */
482: protected HtmlPage loadPage(final BrowserVersion version,
483: final List collectedAlerts) throws Exception {
484: final String testName = getName().substring(4);
485: final String resource = "gwt/" + getDirectory() + "/"
486: + testName + "/" + testName + ".html";
487: final URL url = getClass().getClassLoader().getResource(
488: resource);
489: assertNotNull(url);
490:
491: final WebClient client = new WebClient(version);
492: if (collectedAlerts != null) {
493: client.setAlertHandler(new CollectingAlertHandler(
494: collectedAlerts));
495: }
496:
497: final HtmlPage page = (HtmlPage) client.getPage(url);
498: page.getEnclosingWindow().getThreadManager().joinAll(10000);
499: return page;
500: }
501:
502: /**
503: * {@inheritDoc}
504: */
505: protected void tearDown() throws Exception {
506: super .tearDown();
507: HttpWebConnectionTest.stopWebServer(server_);
508: server_ = null;
509: }
510:
511: /**
512: * Test javascript: 'new Date().getTimezoneOffset()' compared to java.text.SimpleDateFormat.format().
513: *
514: * @throws Exception if the test fails
515: */
516: public void testDateGetTimezoneOffset() throws Exception {
517: final String content = "<html><head><title>foo</title><script>\n"
518: + " function test() {\n"
519: + " var offset = Math.abs(new Date().getTimezoneOffset());\n"
520: + " var timezone = '' + (offset/60);\n"
521: + " if (timezone.length == 1)\n"
522: + " timezone = '0' + timezone;\n"
523: + " alert(timezone);\n"
524: + " }\n"
525: + "</script></head><body onload='test()'>\n"
526: + "</body></html>";
527: String timeZone = new SimpleDateFormat("Z").format(Calendar
528: .getInstance().getTime());
529: timeZone = timeZone.substring(1, 3);
530: final String[] expectedAlerts = { timeZone };
531: final List collectedAlerts = new ArrayList();
532: createTestPageForRealBrowserIfNeeded(content, expectedAlerts);
533: loadPage(content, collectedAlerts);
534: assertEquals(expectedAlerts, collectedAlerts);
535: }
536: }
|