/* * Copyright (c) 2002-2008 Gargoyle Software 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.gargoylesoftware.htmlunit; import static java.util.Arrays.asList; import static org.junit.Assert.assertSame; import static org.junit.Assert.fail; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.OutputStreamWriter; import java.lang.reflect.Constructor; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.net.URI; import java.net.URL; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; import org.apache.commons.httpclient.HttpStatus; import org.apache.commons.httpclient.NameValuePair; import org.apache.commons.io.FileUtils; import org.apache.commons.io.IOUtils; import org.apache.commons.lang.StringUtils; import org.junit.After; import org.junit.AfterClass; import org.junit.Assert; import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; import com.gargoylesoftware.base.testing.EventCatcher; import com.gargoylesoftware.htmlunit.html.HtmlAnchor; import com.gargoylesoftware.htmlunit.html.HtmlButton; import com.gargoylesoftware.htmlunit.html.HtmlElement; import com.gargoylesoftware.htmlunit.html.HtmlInlineFrame; import com.gargoylesoftware.htmlunit.html.HtmlPage; import com.gargoylesoftware.htmlunit.html.SubmittableElement; import com.gargoylesoftware.htmlunit.xml.XmlPage; /** * Tests for {@link WebClient}. * * @version $Revision$ * @author Mike Bowler * @author Christian Sell * @author Ben Curren * @author Marc Guillemot * @author David D. Kilzer * @author Chris Erskine * @author Hans Donner * @author Paul King * @author Ahmed Ashour * @author Daniel Gredler * @author Sudhan Moghe */ public class WebClientTest extends WebTestCase { /** * Tests if all JUnit 4 candidate test methods declare @Test annotation. * @throws Exception if the test fails */ @Test public void testTests() throws Exception { testTests(new File("src/test/java")); } private void testTests(final File dir) throws Exception { for (final File file : dir.listFiles()) { if (file.isDirectory()) { if (!file.getName().equals(".svn")) { testTests(file); } } else { if (file.getName().endsWith(".java")) { final int index = new File("src/test/java").getAbsolutePath().length(); String name = file.getAbsolutePath(); name = name.substring(index + 1, name.length() - 5); name = name.replace(File.separatorChar, '.'); final Class< ? > clazz = Class.forName(name); for (Constructor< ? > ctor : clazz.getConstructors()) { if (ctor.getParameterTypes().length == 0) { for (final Method method : clazz.getDeclaredMethods()) { if (Modifier.isPublic(method.getModifiers()) && method.getAnnotation(Before.class) == null && method.getAnnotation(BeforeClass.class) == null && method.getAnnotation(After.class) == null && method.getAnnotation(AfterClass.class) == null && method.getAnnotation(Test.class) == null && method.getReturnType() == Void.TYPE && method.getParameterTypes().length == 0) { fail("Method '" + method.getName() + "' in " + name + " does not declare @Test annotation"); } } } } } } } } /** * Test the situation where credentials are required but they haven't been specified. * * @throws Exception if something goes wrong */ @Test public void testCredentialProvider_NoCredentials() throws Exception { final String htmlContent = "foo\n" + "No access"; final WebClient client = new WebClient(); client.setPrintContentOnFailingStatusCode(false); final MockWebConnection webConnection = new MockWebConnection(client); webConnection.setDefaultResponse(htmlContent, 401, "Credentials missing or just plain wrong", "text/plain"); client.setWebConnection(webConnection); try { client.getPage(new WebRequestSettings(URL_GARGOYLE, HttpMethod.POST)); fail("Expected FailingHttpStatusCodeException"); } catch (final FailingHttpStatusCodeException e) { assertEquals(401, e.getStatusCode()); } } /** * Test that the {@link WebWindowEvent#CHANGE} window event gets fired at the * appropriate time. * @throws Exception if something goes wrong */ @Test public void testHtmlWindowEvents_changed() throws Exception { final String htmlContent = "foo\n" + "link to foo2\n" + ""; final WebClient client = new WebClient(); final EventCatcher eventCatcher = new EventCatcher(); eventCatcher.listenTo(client); final MockWebConnection webConnection = new MockWebConnection(client); webConnection.setDefaultResponse(htmlContent); client.setWebConnection(webConnection); final HtmlPage firstPage = (HtmlPage) client.getPage(URL_GARGOYLE); final HtmlAnchor anchor = (HtmlAnchor) firstPage.getHtmlElementById("a2"); final List firstExpectedEvents = Arrays.asList(new WebWindowEvent[] { new WebWindowEvent( client.getCurrentWindow(), WebWindowEvent.CHANGE, null, firstPage), }); assertEquals(firstExpectedEvents, eventCatcher.getEvents()); eventCatcher.clear(); final HtmlPage secondPage = (HtmlPage) anchor.click(); final List secondExpectedEvents = Arrays.asList(new WebWindowEvent[] { new WebWindowEvent( client.getCurrentWindow(), WebWindowEvent.CHANGE, firstPage, secondPage), }); assertEquals(secondExpectedEvents, eventCatcher.getEvents()); } /** * Test that the {@link WebWindowEvent#OPEN} window event gets fired at * the appropriate time. * @throws Exception if something goes wrong */ @Test public void testHtmlWindowEvents_opened() throws Exception { final String page1Content = "foo\n" + "\n" + "\n" + "link to foo2\n" + ""; final String page2Content = "foo"; final WebClient client = new WebClient(); final EventCatcher eventCatcher = new EventCatcher(); eventCatcher.listenTo(client); final MockWebConnection webConnection = new MockWebConnection(client); webConnection.setResponse(URL_FIRST, page1Content); webConnection.setResponse(URL_SECOND, page2Content); client.setWebConnection(webConnection); final HtmlPage firstPage = (HtmlPage) client.getPage(URL_FIRST); assertEquals("foo", firstPage.getTitleText()); final WebWindow firstWindow = client.getCurrentWindow(); final WebWindow secondWindow = client.getWebWindowByName("myNewWindow"); final List expectedEvents = Arrays.asList(new WebWindowEvent[] { new WebWindowEvent( secondWindow, WebWindowEvent.OPEN, null, null), new WebWindowEvent( secondWindow, WebWindowEvent.CHANGE, null, secondWindow.getEnclosedPage()), new WebWindowEvent( firstWindow, WebWindowEvent.CHANGE, null, firstPage), }); assertEquals(expectedEvents, eventCatcher.getEvents()); } /** * Test that the {@link WebWindowEvent#CLOSE} window event gets fired at * the appropriate time. * @throws Exception if something goes wrong */ @Test public void testHtmlWindowEvents_closedFromFrame() throws Exception { final String firstContent = "first\n" + ""; final List alerts = new ArrayList(); loadPage(html, alerts); assertTrue(alerts.isEmpty()); } /** * Test that '+' is not encoded in URLs. * @throws Exception if the test fails */ @Test public void testPlusNotEncodedInUrl() throws Exception { final URL url = new URL("http://host/search/my+category/"); final HtmlPage page = loadPage("", new ArrayList(), url); assertEquals("http://host/search/my+category/", page.getWebResponse().getUrl()); } /** * @throws Exception if an error occurs */ @Test public void testCssEnablementControlsCssLoading() throws Exception { final WebClient client = new WebClient(); final MockWebConnection conn = new MockWebConnection(client); client.setWebConnection(conn); final String html = "\n" + " \n" + " \n" + " \n" + " \n" + "
abc
\n" + " \n" + ""; conn.setResponse(URL_FIRST, html); final String css = ".foo { color: green; }"; conn.setResponse(URL_SECOND, css, 200, "OK", "text/css", new ArrayList()); final List actual = new ArrayList(); client.setAlertHandler(new CollectingAlertHandler(actual)); client.getPage(URL_FIRST); assertEquals(new String[]{"1"}, actual); actual.clear(); client.setCssEnabled(false); client.getPage(URL_FIRST); assertEquals(new String[]{"0"}, actual); actual.clear(); client.setCssEnabled(true); client.getPage(URL_FIRST); assertEquals(new String[]{"1"}, actual); } /** * @throws Exception if test fails */ @Test public void testGetPageJavascriptProtocol() throws Exception { final WebClient webClient = new WebClient(); final MockWebConnection webConnection = new MockWebConnection(webClient); webConnection.setDefaultResponse("Hello World"); webClient.setWebConnection(webConnection); final List collectedAlerts = new ArrayList(); webClient.setAlertHandler(new CollectingAlertHandler(collectedAlerts)); Page page = webClient.getPage("javascript:void(alert(document.location))"); assertEquals("about:blank", page.getWebResponse().getUrl()); assertEquals(new String[] {"about:blank"}, collectedAlerts); collectedAlerts.clear(); page = webClient.getPage(URL_FIRST); final Page page2 = webClient.getPage("javascript:void(alert(document.title))"); assertSame(page, page2); assertEquals(new String[] {"Hello World"}, collectedAlerts); webClient.getPage("javascript:void(document.body.setAttribute('foo', window.screen.availWidth))"); assertEquals("1024", ((HtmlPage) page).getBody().getAttribute("foo")); } /** * @throws Exception if the test fails */ @Test public void testJavaScriptTimeout() throws Exception { final long timeout = 2000; final long oldTimeout = WebClient.getJavaScriptTimeout(); WebClient.setJavaScriptTimeout(timeout); try { final WebClient client = new WebClient(); client.setThrowExceptionOnScriptError(false); final String content = ""; final MockWebConnection webConnection = new MockWebConnection(client); webConnection.setDefaultResponse(content); client.setWebConnection(webConnection); final Exception[] exceptions = {null}; final Thread runner = new Thread() { @Override public void run() { try { client.getPage(URL_FIRST); } catch (final Exception e) { exceptions[0] = e; } } }; runner.start(); runner.join(timeout * 2); if (runner.isAlive()) { runner.interrupt(); fail("Script was still running after timeout"); } assertNull(exceptions[0]); } finally { WebClient.setJavaScriptTimeout(oldTimeout); } } /** * Protects against the regression detailed in bug 1975445. * @throws Exception if an error occurs */ @Test public void testOpenWindowWithNullUrl() throws Exception { final WebClient client = new WebClient(); final WebWindow window = client.openWindow(null, "TestingWindow"); Assert.assertNotNull(window); } /** * Regression test for currentWindow_ * Previous window should become current window after current window is closed in onLoad event. * @throws Exception if an error occurs */ @Test public void testCurrentWindowAfterWindowCloseInOnload() throws Exception { final WebClient webClient = new WebClient(); final MockWebConnection webConnection = new MockWebConnection(webClient); final String firstContent = "First\n" + "
\n" + "