/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You 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.apache.cocoon; import com.gargoylesoftware.htmlunit.Page; import com.gargoylesoftware.htmlunit.SubmitMethod; import com.gargoylesoftware.htmlunit.WebClient; import com.gargoylesoftware.htmlunit.WebRequestSettings; import com.gargoylesoftware.htmlunit.WebResponse; import com.gargoylesoftware.htmlunit.html.HtmlPage; import com.gargoylesoftware.htmlunit.html.xpath.HtmlUnitXPath; import com.gargoylesoftware.htmlunit.xml.XmlPage; import junit.framework.TestCase; import org.apache.avalon.framework.logger.ConsoleLogger; import org.apache.avalon.framework.logger.Logger; import org.apache.commons.httpclient.methods.DeleteMethod; import org.apache.commons.httpclient.methods.PutMethod; import org.apache.commons.httpclient.methods.StringRequestEntity; import org.apache.commons.io.FileUtils; import org.jaxen.SimpleNamespaceContext; import org.jaxen.XPath; import org.jaxen.dom.DOMXPath; import org.w3c.dom.Document; import java.io.File; import java.net.URL; import java.util.HashMap; import java.util.Map; /** * Base class to run test cases on Cocoon samples. * <p> * This class extends the JUnit TestCase class to setup an environment which * makes it possible to easily test Cocoon pages. * First call one of the load methods and then assert on the response object, * XML document (@see loadXmlPage), or HTML document (@see loadHtmlPage). * </p> * <p> * Examples: * </p> * <pre> * public void testStatus() { * loadResponse("/samples/test/status"); * assertEquals("Status code", 200, response.getStatusCode()); * } * * public void testTitle() { * loadHtmlPage("/samples/test/title"); * assertXPath("html/head/title"", "The Title"); * } * </pre> * * <p> * For loading XML and HTML documents currently on GET requests with * optional querystring are supported. Please add POST requests and * request parameters when you need them. * </p> * @version $Id: $ */ public abstract class HtmlUnitTestCase extends TestCase { /** * Logger for informative output by test cases. * The default log level is WARN but may be changed by setting the * property junit.test.loglevel to a different numeric value. */ protected Logger logger; /** * Base URL of the running Cocoon server which is to be tested. * Set by property htmlunit.test.baseurl usually as http://localhost:8888/. */ protected URL baseURL; /** * Low-level access to WebClient object. */ protected WebClient webClient; /** * Low-level access to WebResponse object. */ protected WebResponse response; /** * Low-level access to XML document (org.w3c.dom.Document) or HTML document * (com.gargoylesoftware.htmlunit.html.HtmlPage). */ protected Object document; /** * Low-level access to namespace mappings for XPath expressions. */ protected Map namespaces = new HashMap(); /* (non-Javadoc) * @see junit.framework.TestCase#setUp() */ protected void setUp() throws Exception { super.setUp(); String level = System.getProperty("junit.test.loglevel", "" + ConsoleLogger.LEVEL_WARN); this.logger = new ConsoleLogger(Integer.parseInt(level)); String baseurl = System.getProperty("htmlunit.test.baseurl"); this.baseURL = new URL(baseurl); this.webClient = new WebClient(); this.webClient.setRedirectEnabled(false); this.namespaces.clear(); } /* (non-Javadoc) * @see junit.framework.TestCase#tearDown() */ protected void tearDown() throws Exception { this.response = null; this.document = null; super.tearDown(); } /** * Sends HTTP GET request and loads response object. */ protected void loadResponse(String pageURL) throws Exception { WebRequestSettings webRequestSettings = new WebRequestSettings(new URL(baseURL, pageURL), SubmitMethod.GET); this.response = webClient.loadWebResponse(webRequestSettings); } /** * Sends HTTP DELETE request and loads response object. */ protected void loadDeleteResponse(String pageURL) throws Exception { URL url = new URL(baseURL, pageURL); DeleteMethod method = new DeleteMethod(url.toExternalForm()); this.response = new HttpClientResponse(url, method); } /** * Sends HTTP PUT request and loads response object. */ protected void loadPutResponse(String pageURL, String content) throws Exception { URL url = new URL(baseURL, pageURL); PutMethod method = new PutMethod(url.toExternalForm()); method.setRequestEntity(new StringRequestEntity(content)); this.response = new HttpClientResponse(url, method); } /** * Sends HTTP request and parses response as HTML document. */ protected void loadHtmlPage(String pageURL) throws Exception { URL url = new URL(baseURL, pageURL); Page page = webClient.getPage(url); this.response = page.getWebResponse(); assertTrue("Response should be an HTML page", page instanceof HtmlPage); this.document = page; assertNotNull("Response contains invalid HTML", this.document); } /** * Sends HTTP request and parses response as XML document. */ protected void loadXmlPage(String pageURL) throws Exception { URL url = new URL(baseURL, pageURL); Page page = webClient.getPage(url); this.response = page.getWebResponse(); assertTrue("Response should be an XML page", page instanceof XmlPage); XmlPage xmlPage = (XmlPage)page; this.document = xmlPage.getXmlDocument(); assertNotNull("Response contains invalid XML", this.document); } /** * Returns XPath expression as string. * * @param xpathExpr XPath expression * * @return Value of XPath expression in current document. * Empty string if XPath not matched. */ protected String evalXPath(String xpathExpr) throws Exception { XPath xpath = null; if( document == null ) return null; else if( document instanceof HtmlPage ) xpath = new HtmlUnitXPath(xpathExpr); else if( document instanceof Document ) xpath = new DOMXPath(xpathExpr); else fail("Document type "+document.getClass().getName()); xpath.setNamespaceContext(new SimpleNamespaceContext(namespaces)); return xpath.stringValueOf(document); } /** * Add a namespace mapping for XPath expressions. */ protected void addNamespace(String prefix, String uri) throws Exception { namespaces.put(prefix, uri); } /** * Assert that XPath expression result matches exactly expected value. */ protected void assertXPath(String xpathExpr, String expected) throws Exception { assertEquals(xpathExpr, expected, evalXPath(xpathExpr)); } /** * Copy file from webapp source to deployment area filtering content * to replace parameter by value. * The source and deployment directories are defined by the properties * htmlunit.test.source-dir and htmlunit.test.deploy-dir. * * This method is most useful for testing the automatic reloading of * changed files. */ protected void copyWebappFile(String filename, String param, String value) throws Exception { String srcdir = System.getProperty("htmlunit.test.source-dir"); String dstdir = System.getProperty("htmlunit.test.deploy-dir"); File srcfile = new File(srcdir+"/"+filename); File dstfile = new File(dstdir+"/"+filename); final String encoding = "ISO-8859-1"; StringBuffer content = new StringBuffer(FileUtils.readFileToString(srcfile, encoding)); int index = content.indexOf(param); while( index != -1 ) { content.replace(index, index+param.length(), value); index = content.indexOf(param, index+1); } FileUtils.writeStringToFile(dstfile, content.toString(), encoding); // Leave server some time to realize that file has changed. Thread.sleep(1000); } }