You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@wicket.apache.org by ja...@apache.org on 2007/03/22 12:24:45 UTC

svn commit: r521220 - in /incubator/wicket/branches/wicket-1.x/jdk-1.4/wicket/src/main/java/wicket/util/tester: BaseWicketTester.java Result.java

Author: janne
Date: Thu Mar 22 04:24:44 2007
New Revision: 521220

URL: http://svn.apache.org/viewvc?view=rev&rev=521220
Log:
WICKET-226:  WicketTester version which does not depend on junit

Added:
    incubator/wicket/branches/wicket-1.x/jdk-1.4/wicket/src/main/java/wicket/util/tester/BaseWicketTester.java
    incubator/wicket/branches/wicket-1.x/jdk-1.4/wicket/src/main/java/wicket/util/tester/Result.java

Added: incubator/wicket/branches/wicket-1.x/jdk-1.4/wicket/src/main/java/wicket/util/tester/BaseWicketTester.java
URL: http://svn.apache.org/viewvc/incubator/wicket/branches/wicket-1.x/jdk-1.4/wicket/src/main/java/wicket/util/tester/BaseWicketTester.java?view=auto&rev=521220
==============================================================================
--- incubator/wicket/branches/wicket-1.x/jdk-1.4/wicket/src/main/java/wicket/util/tester/BaseWicketTester.java (added)
+++ incubator/wicket/branches/wicket-1.x/jdk-1.4/wicket/src/main/java/wicket/util/tester/BaseWicketTester.java Thu Mar 22 04:24:44 2007
@@ -0,0 +1,1138 @@
+/*
+ * 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 wicket.util.tester;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.List;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import wicket.Component;
+import wicket.Page;
+import wicket.PageParameters;
+import wicket.RequestCycle;
+import wicket.WicketRuntimeException;
+import wicket.ajax.AjaxEventBehavior;
+import wicket.ajax.AjaxRequestTarget;
+import wicket.ajax.form.AjaxFormSubmitBehavior;
+import wicket.ajax.markup.html.AjaxFallbackLink;
+import wicket.ajax.markup.html.AjaxLink;
+import wicket.ajax.markup.html.form.AjaxSubmitLink;
+import wicket.behavior.AbstractAjaxBehavior;
+import wicket.behavior.IBehavior;
+import wicket.feedback.FeedbackMessage;
+import wicket.feedback.FeedbackMessages;
+import wicket.feedback.IFeedbackMessageFilter;
+import wicket.markup.html.basic.Label;
+import wicket.markup.html.form.Button;
+import wicket.markup.html.form.CheckGroup;
+import wicket.markup.html.form.Form;
+import wicket.markup.html.form.FormComponent;
+import wicket.markup.html.form.RadioGroup;
+import wicket.markup.html.link.BookmarkablePageLink;
+import wicket.markup.html.link.IPageLink;
+import wicket.markup.html.link.Link;
+import wicket.markup.html.link.PageLink;
+import wicket.markup.html.list.ListView;
+import wicket.markup.html.panel.Panel;
+import wicket.protocol.http.MockWebApplication;
+import wicket.protocol.http.WebApplication;
+import wicket.protocol.http.WebRequestCycle;
+import wicket.util.diff.DiffUtil;
+import wicket.util.lang.Classes;
+import wicket.util.string.Strings;
+
+/**
+ * A helper to ease unit testing of Wicket applications without the need for a
+ * servlet container. See javadoc of WicketTester for example usage. This class
+ * can be used as is, but JUnit users should use derived class WicketTester.
+ *
+ * @see WicketTester
+ *
+ * @author Ingram Chen
+ * @author Juergen Donnerstag
+ * @author Frank Bille
+ */
+public class BaseWicketTester extends MockWebApplication
+{
+	/** log. */
+	private static final Log log = LogFactory.getLog(BaseWicketTester.class);
+
+	/**
+	 * @author frankbille
+	 */
+	public static class DummyWebApplication extends WebApplication
+	{
+		public Class getHomePage()
+		{
+			return DummyHomePage.class;
+		}
+	}
+
+	/**
+	 * Create WicketTester and automatically create a WebApplication, but the
+	 * tester will have no home page.
+	 */
+	public BaseWicketTester()
+	{
+		this(new DummyWebApplication(), null);
+	}
+
+	/**
+	 * Create WicketTester and automatically create a WebApplication.
+	 *
+	 * @param homePage
+	 */
+	public BaseWicketTester(final Class homePage)
+	{
+		this(new WebApplication()
+		{
+			/**
+			 * @see wicket.Application#getHomePage()
+			 */
+			public Class getHomePage()
+			{
+				return homePage;
+			}
+		}, null);
+	}
+
+	/**
+	 * Create WicketTester
+	 *
+	 * @param application
+	 *            The wicket tester object
+	 */
+	public BaseWicketTester(final WebApplication application)
+	{
+		this(application, null);
+	}
+
+	/**
+	 * Create WicketTester to help unit testing
+	 *
+	 * @param application
+	 *            The wicket tester object
+	 * @param path
+	 *            The absolute path on disk to the web application contents
+	 *            (e.g. war root) - may be null
+	 *
+	 * @see wicket.protocol.http.MockWebApplication#MockWebApplication(String)
+	 */
+	public BaseWicketTester(final WebApplication application, final String path)
+	{
+		super(application, path);
+	}
+
+	/**
+	 * Render a page defined in <code>TestPageSource</code>. This is usually
+	 * used when a page does not have default constructor. For example, a
+	 * <code>ViewBook</code> page requires a <code>Book</code> instance:
+	 *
+	 * <pre>
+	 * tester.startPage(new TestPageSource()
+	 * {
+	 * 	public Page getTestPage()
+	 * 	{
+	 * 		Book mockBook = new Book(&quot;myBookName&quot;);
+	 * 		return new ViewBook(mockBook);
+	 * 	}
+	 * });
+	 * </pre>
+	 *
+	 * @param testPageSource
+	 *            a page factory that creating test page instance
+	 * @return Page rendered page
+	 */
+	public final Page startPage(final ITestPageSource testPageSource)
+	{
+		startPage(DummyHomePage.class);
+		DummyHomePage page = (DummyHomePage)getLastRenderedPage();
+		page.setTestPageSource(testPageSource);
+
+		executeListener(page.getTestPageLink());
+		return getLastRenderedPage();
+	}
+
+	/**
+	 * Builds and processes a request suitable for invoking a listener. The
+	 * component must implement any of the known *Listener interfaces.
+	 *
+	 * @param component the listener to invoke
+	 */
+	public void executeListener(Component component)
+	{
+		setupRequestAndResponse();
+		getServletRequest().setRequestToComponent(component);
+		processRequestCycle();
+	}
+
+	/**
+	 * Builds and processes a request suitable for executing an ajax behavior.
+	 *
+	 * @param behavior the ajax behavior to execute
+	 */
+	public void executeBehavior(final AbstractAjaxBehavior behavior)
+	{
+		setupRequestAndResponse();
+		WebRequestCycle cycle = createRequestCycle();
+		getServletRequest().setRequestToRedirectString(
+				behavior.getCallbackUrl(false, false).toString());
+		processRequestCycle(cycle);
+	}
+
+	/**
+	 * Render the page
+	 *
+	 * @param page
+	 * @return The page rendered
+	 */
+	public final Page startPage(final Page page)
+	{
+		processRequestCycle(page);
+
+		Page last = getLastRenderedPage();
+		//
+		// createRequestCycle();
+		// getWicketSession().touch(page);
+		// if (page != last)
+		// {
+		// getWicketSession().touch(last);
+		// }
+		return last;
+	}
+
+	/**
+	 * Render a page from its default constructor.
+	 *
+	 * @param pageClass
+	 *            a test page class with default constructor
+	 * @return Page Rendered Page
+	 */
+	public final Page startPage(Class pageClass)
+	{
+		setupRequestAndResponse();
+		processRequestCycle(pageClass);
+		return getLastRenderedPage();
+	}
+
+	/**
+	 * Render a panel defined in <code>TestPanelSource</code>. The usage is
+	 * similar with {@link #startPage(ITestPageSource)}. Please note that
+	 * testing panel must use supplied <code>panelId<code> as component id.
+	 *
+	 * <pre>
+	 * tester.startPanel(new TestPanelSource()
+	 * {
+	 * 	public Panel getTestPanel(String panelId)
+	 * 	{
+	 * 		MyData mockMyData = new MyData();
+	 * 		return new MyPanel(panelId, mockMyData);
+	 * 	}
+	 * });
+	 * </pre>
+	 *
+	 * @param testPanelSource
+	 *            a panel factory that creating test panel instance
+	 * @return Panel rendered panel
+	 */
+	public final Panel startPanel(final TestPanelSource testPanelSource)
+	{
+		return (Panel)startPage(new ITestPageSource()
+		{
+			private static final long serialVersionUID = 1L;
+
+			public Page getTestPage()
+			{
+				return new DummyPanelPage(testPanelSource);
+			}
+		}).get(DummyPanelPage.TEST_PANEL_ID);
+	}
+
+	/**
+	 * Render a panel from <code>Panel(String id)</code> constructor.
+	 *
+	 * @param panelClass
+	 *            a test panel class with <code>Panel(String id)</code>
+	 *            constructor
+	 * @return Panel rendered panel
+	 */
+	public final Panel startPanel(final Class panelClass)
+	{
+		return (Panel)startPage(new ITestPageSource()
+		{
+			private static final long serialVersionUID = 1L;
+
+			public Page getTestPage()
+			{
+				return new DummyPanelPage(new TestPanelSource()
+				{
+					private static final long serialVersionUID = 1L;
+
+					public Panel getTestPanel(String panelId)
+					{
+						try
+						{
+							Constructor c = panelClass.getConstructor(new Class[] { String.class });
+							return (Panel)c.newInstance(new Object[] { panelId });
+						}
+						catch (SecurityException e)
+						{
+							throw convertoUnexpect(e);
+						}
+						catch (NoSuchMethodException e)
+						{
+							throw convertoUnexpect(e);
+						}
+						catch (InstantiationException e)
+						{
+							throw convertoUnexpect(e);
+						}
+						catch (IllegalAccessException e)
+						{
+							throw convertoUnexpect(e);
+						}
+						catch (InvocationTargetException e)
+						{
+							throw convertoUnexpect(e);
+						}
+					}
+				});
+			}
+		}).get(DummyPanelPage.TEST_PANEL_ID);
+	}
+
+	/**
+	 * Throw "standard" WicketRuntimeException
+	 *
+	 * @param e
+	 * @return RuntimeException
+	 */
+	private RuntimeException convertoUnexpect(Exception e)
+	{
+		return new WicketRuntimeException("tester: unexpected", e);
+	}
+
+	/**
+	 * Gets the component with the given path from last rendered page. This
+	 * method fails in case the component couldn't be found, and it will return
+	 * null if the component was found, but is not visible.
+	 *
+	 * @param path
+	 *            Path to component
+	 * @return The component at the path
+	 * @see wicket.MarkupContainer#get(String)
+	 */
+	public Component getComponentFromLastRenderedPage(String path)
+	{
+		final Component component = getLastRenderedPage().get(path);
+		if (component == null)
+		{
+			fail("path: '" + path + "' does no exist for page: "
+					+ Classes.simpleName(getLastRenderedPage().getClass()));
+			return component;
+		}
+		if (component.isVisibleInHierarchy())
+		{
+			return component;
+		}
+		return null;
+	}
+
+	/**
+	 * assert the text of <code>Label</code> component.
+	 *
+	 * @param path
+	 *            path to <code>Label</code> component
+	 * @param expectedLabelText
+	 *            expected label text
+	 * @return
+	 */
+	public Result hasLabel(String path, String expectedLabelText)
+	{
+		Label label = (Label)getComponentFromLastRenderedPage(path);
+		return isEqual(expectedLabelText, label.getModelObjectAsString());
+	}
+
+	/**
+	 * assert <code>PageLink</code> link to page class.
+	 *
+	 * @param path
+	 *            path to <code>PageLink</code> component
+	 * @param expectedPageClass
+	 *            expected page class to link
+	 * @return
+	 */
+	public Result isPageLink(String path, Class expectedPageClass)
+	{
+		PageLink pageLink = (PageLink)getComponentFromLastRenderedPage(path);
+		try
+		{
+			Field iPageLinkField = pageLink.getClass().getDeclaredField("pageLink");
+			iPageLinkField.setAccessible(true);
+			IPageLink iPageLink = (IPageLink)iPageLinkField.get(pageLink);
+			return isEqual(expectedPageClass, iPageLink.getPageIdentity());
+		}
+		catch (SecurityException e)
+		{
+			throw convertoUnexpect(e);
+		}
+		catch (NoSuchFieldException e)
+		{
+			throw convertoUnexpect(e);
+		}
+		catch (IllegalAccessException e)
+		{
+			throw convertoUnexpect(e);
+		}
+	}
+
+	/**
+	 * assert component class
+	 *
+	 * @param path
+	 *            path to component
+	 * @param expectedComponentClass
+	 *            expected component class
+	 * @return
+	 */
+	public Result isComponent(String path, Class expectedComponentClass)
+	{
+		Component component = getComponentFromLastRenderedPage(path);
+		return isTrue("component '" + Classes.simpleName(component.getClass())
+				+ "' is not type:" + Classes.simpleName(expectedComponentClass),
+				expectedComponentClass.isAssignableFrom(component.getClass()));
+	}
+
+	/**
+	 * assert component visible.
+	 *
+	 * @param path
+	 *            path to component
+	 * @return
+	 */
+	public Result isVisible(String path)
+	{
+		Component component = getLastRenderedPage().get(path);
+		if (component == null)
+		{
+			fail("path: '" + path + "' does no exist for page: "
+					+ Classes.simpleName(getLastRenderedPage().getClass()));
+		}
+
+		return isTrue("component '" + path + "' is not visible", component.isVisible());
+	}
+
+	/**
+	 * assert component invisible.
+	 *
+	 * @param path
+	 *            path to component
+	 * @return
+	 */
+	public Result isInvisible(String path)
+	{
+		return isNull("component '" + path + "' is visible",
+				getComponentFromLastRenderedPage(path));
+	}
+
+	/**
+	 * assert the content of last rendered page contains(matches) regex pattern.
+	 *
+	 * @param pattern
+	 *            reqex pattern to match
+	 * @return
+	 */
+	public Result ifContains(String pattern)
+	{
+		return isTrue("pattern '" + pattern + "' not found", getServletResponse().getDocument()
+				.matches("(?s).*" + pattern + ".*"));
+	}
+
+	/**
+	 * assert the model of {@link ListView} use expectedList
+	 *
+	 * @param path
+	 *            path to {@link ListView} component
+	 * @param expectedList
+	 *            expected list in the model of {@link ListView}
+	 */
+	public void assertListView(String path, List expectedList)
+	{
+		ListView listView = (ListView)getComponentFromLastRenderedPage(path);
+		WicketTesterHelper.assertEquals(expectedList, listView.getList());
+	}
+
+	/**
+	 * Click the {@link Link} in the last rendered Page.
+	 * <p>
+	 * Simulate that AJAX is enabled.
+	 *
+	 * @see WicketTester#clickLink(String, boolean)
+	 * @param path
+	 *            Click the <code>Link</code> in the last rendered Page.
+	 */
+	public void clickLink(String path)
+	{
+		clickLink(path, true);
+	}
+
+	/**
+	 * Click the {@link Link} in the last rendered Page.
+	 * <p>
+	 * This method also works for {@link AjaxLink}, {@link AjaxFallbackLink}
+	 * and {@link AjaxSubmitLink}.
+	 * <p>
+	 * On AjaxLinks and AjaxFallbackLinks the onClick method is invoked with a
+	 * valid AjaxRequestTarget. In that way you can test the flow of your
+	 * application when using AJAX.
+	 * <p>
+	 * When clicking an AjaxSubmitLink the form, which the AjaxSubmitLink is
+	 * attached to is first submitted, and then the onSubmit method on
+	 * AjaxSubmitLink is invoked. If you have changed some values in the form
+	 * during your test, these will also be submitted. This should not be used
+	 * as a replacement for the {@link FormTester} to test your forms. It should
+	 * be used to test that the code in your onSubmit method in AjaxSubmitLink
+	 * actually works.
+	 * <p>
+	 * This method is also able to simulate that AJAX (javascript) is disabled
+	 * on the client. This is done by setting the isAjax parameter to false. If
+	 * you have an AjaxFallbackLink you can then check that it doesn't fail when
+	 * invoked as a normal link.
+	 *
+	 * @param path
+	 *            path to <code>Link</code> component
+	 * @param isAjax
+	 *            Whether to simulate that AJAX (javascript) is enabled or not.
+	 *            If it's false then AjaxLink and AjaxSubmitLink will fail,
+	 *            since it wouldn't work in real life. AjaxFallbackLink will be
+	 *            invoked with null as the AjaxRequestTarget parameter.
+	 */
+	public void clickLink(String path, boolean isAjax)
+	{
+		Component linkComponent = getComponentFromLastRenderedPage(path);
+
+		// if the link is an AjaxLink, we process it differently
+		// than a normal link
+		if (linkComponent instanceof AjaxLink)
+		{
+			// If it's not ajax we fail
+			if (isAjax == false)
+			{
+				fail("Link " + path + "is an AjaxLink and will "
+						+ "not be invoked when AJAX (javascript) is disabled.");
+			}
+
+			AjaxLink link = (AjaxLink)linkComponent;
+
+			setupRequestAndResponse();
+			RequestCycle requestCycle = createRequestCycle();
+			AjaxRequestTarget target = new AjaxRequestTarget();
+			requestCycle.setRequestTarget(target);
+
+			link.onClick(target);
+
+			// process the request target
+			target.respond(requestCycle);
+		}
+		// AjaxFallbackLinks is processed like an AjaxLink if isAjax is true
+		// If it's not handling of the linkComponent is passed through to the
+		// Link.
+		else if (linkComponent instanceof AjaxFallbackLink && isAjax)
+		{
+			AjaxFallbackLink link = (AjaxFallbackLink)linkComponent;
+
+			setupRequestAndResponse();
+			RequestCycle requestCycle = createRequestCycle();
+			AjaxRequestTarget target = new AjaxRequestTarget();
+			requestCycle.setRequestTarget(target);
+
+			link.onClick(target);
+
+			// process the request target
+			target.respond(requestCycle);
+		}
+		// if the link is an AjaxSubmitLink, we need to find the form
+		// from it using reflection so we know what to submit.
+		else if (linkComponent instanceof AjaxSubmitLink)
+		{
+			// If it's not ajax we fail
+			if (isAjax == false)
+			{
+				fail("Link " + path + "is an AjaxSubmitLink and "
+						+ "will not be invoked when AJAX (javascript) is disabled.");
+			}
+
+			AjaxSubmitLink link = (AjaxSubmitLink)linkComponent;
+
+			// We cycle through the attached behaviors and select the
+			// LAST matching behavior as the one we handle.
+			List behaviors = link.getBehaviors();
+			AjaxFormSubmitBehavior ajaxFormSubmitBehavior = null;
+			for (Iterator iter = behaviors.iterator(); iter.hasNext();)
+			{
+				Object behavior = iter.next();
+				if (behavior instanceof AjaxFormSubmitBehavior)
+				{
+					AjaxFormSubmitBehavior submitBehavior = (AjaxFormSubmitBehavior)behavior;
+					ajaxFormSubmitBehavior = submitBehavior;
+				}
+			}
+
+			String failMessage = "No form submit behavior found on the submit link. Strange!!";
+			notNull(failMessage, ajaxFormSubmitBehavior);
+
+			setupRequestAndResponse();
+			RequestCycle requestCycle = createRequestCycle();
+
+			submitAjaxFormSubmitBehavior(ajaxFormSubmitBehavior);
+
+			// Ok, finally we "click" the link
+			ajaxFormSubmitBehavior.onRequest();
+
+			// process the request target
+			requestCycle.getRequestTarget().respond(requestCycle);
+		}
+		// if the link is a normal link (or ResourceLink)
+		else if (linkComponent instanceof Link)
+		{
+			Link link = (Link)linkComponent;
+
+			/*
+			 * If the link is a bookmarkable link, then we need to transfer the
+			 * parameters to the next request.
+			 */
+			if (link instanceof BookmarkablePageLink)
+			{
+				BookmarkablePageLink bookmarkablePageLink = (BookmarkablePageLink)link;
+				try
+				{
+					Field parametersField = BookmarkablePageLink.class
+					.getDeclaredField("parameters");
+					parametersField.setAccessible(true);
+					PageParameters parameters = (PageParameters)parametersField
+					.get(bookmarkablePageLink);
+					setParametersForNextRequest(parameters);
+				}
+				catch (Exception e)
+				{
+					fail("Internal error in WicketTester. "
+							+ "Please report this in Wickets Issue Tracker.");
+				}
+
+			}
+
+			executeListener(link);
+		}
+		else
+		{
+			fail("Link " + path
+					+ " is not a Link, AjaxLink, AjaxFallbackLink or AjaxSubmitLink");
+		}
+	}
+
+	/**
+	 * submit the <code>Form</code> in the last rendered Page.
+	 *
+	 * @param path
+	 *            path to <code>Form</code> component
+	 */
+	public void submitForm(String path)
+	{
+		Form form = (Form)getComponentFromLastRenderedPage(path);
+		executeListener(form);
+	}
+
+	/**
+	 * Sets a parameter for the component with the given path to be used with
+	 * the next request. NOTE: this method only works when a page was rendered
+	 * first.
+	 *
+	 * @param componentPath
+	 *            path of the component
+	 * @param value
+	 *            the parameter value to set
+	 */
+	public void setParameterForNextRequest(String componentPath, Object value)
+	{
+		if (getLastRenderedPage() == null)
+		{
+			fail("before using this method, at least one page has to be rendered");
+		}
+
+		Component c = getComponentFromLastRenderedPage(componentPath);
+		if (c == null)
+		{
+			fail("component " + componentPath + " was not found");
+			return;
+		}
+
+		if (c instanceof FormComponent)
+		{
+			getParametersForNextRequest().put(((FormComponent)c).getInputName(), value);
+		}
+		else
+		{
+			getParametersForNextRequest().put(c.getPath(), value);
+		}
+
+	}
+
+	/**
+	 * assert last rendered Page class
+	 *
+	 * FIXME explain why the code is so complicated to compare two classes, or simplify
+	 *
+	 * @param expectedRenderedPageClass
+	 *            expected class of last renered page
+	 * @return
+	 */
+	public Result isRenderedPage(Class expectedRenderedPageClass)
+	{
+		if (!getLastRenderedPage().getClass().isAssignableFrom(expectedRenderedPageClass))
+		{
+			return isEqual(Classes.simpleName(expectedRenderedPageClass), Classes
+					.simpleName(getLastRenderedPage().getClass()));
+		}
+		return Result.pass();
+	}
+
+	/**
+	 * assert last rendered Page against an expected HTML document
+	 * <p>
+	 * Use <code>-Dwicket.replace.expected.results=true</code> to
+	 * automatically replace the expected output file.
+	 * </p>
+	 *
+	 * @param pageClass
+	 *            Used to load the file (relative to clazz package)
+	 * @param filename
+	 *            Expected output
+	 * @throws Exception
+	 */
+	public void assertResultPage(final Class pageClass, final String filename) throws Exception
+	{
+		// Validate the document
+		String document = getServletResponse().getDocument();
+		DiffUtil.validatePage(document, pageClass, filename, true);
+	}
+
+	/**
+	 * assert last rendered Page against an expected HTML document as a String
+	 *
+	 * @param expectedDocument
+	 *            Expected output
+	 * @return
+	 * @throws Exception
+	 */
+	public Result isResultPage(final String expectedDocument) throws Exception
+	{
+		// Validate the document
+		String document = getServletResponse().getDocument();
+		return isTrue("expected rendered page equals", document.equals(expectedDocument));
+	}
+
+	/**
+	 * assert no error feedback messages
+	 * @return
+	 */
+	public Result hasNoErrorMessage()
+	{
+		List messages = getMessages(FeedbackMessage.ERROR);
+		return isTrue("expect no error message, but contains\n"
+				+ WicketTesterHelper.asLined(messages), messages.isEmpty());
+	}
+
+	/**
+	 * assert no info feedback messages
+	 * @return
+	 */
+	public Result hasNoInfoMessage()
+	{
+		List messages = getMessages(FeedbackMessage.INFO);
+		return isTrue("expect no info message, but contains\n"
+				+ WicketTesterHelper.asLined(messages), messages.isEmpty());
+	}
+
+	/**
+	 * assert error feedback messages
+	 *
+	 * @param expectedErrorMessages
+	 *            expected error messages
+	 */
+	public void assertErrorMessages(String[] expectedErrorMessages)
+	{
+		List actualMessages = getMessages(FeedbackMessage.ERROR);
+		List msgs = new ArrayList();
+		for (Iterator iterator = actualMessages.iterator(); iterator.hasNext();)
+		{
+			msgs.add(iterator.next().toString());
+		}
+		WicketTesterHelper.assertEquals(Arrays.asList(expectedErrorMessages), msgs);
+	}
+
+	/**
+	 * assert info feedback message
+	 *
+	 * @param expectedInfoMessages
+	 *            expected info messages
+	 */
+	public void assertInfoMessages(String[] expectedInfoMessages)
+	{
+		List actualMessages = getMessages(FeedbackMessage.INFO);
+		WicketTesterHelper.assertEquals(Arrays.asList(expectedInfoMessages), actualMessages);
+	}
+
+	/**
+	 * get feedback messages
+	 *
+	 * @param level
+	 *            level of feedback message, ex.
+	 *            <code>FeedbackMessage.DEBUG or FeedbackMessage.INFO.. etc</code>
+	 * @return List list of messages (in String)
+	 * @see FeedbackMessage
+	 */
+	public List getMessages(final int level)
+	{
+		FeedbackMessages feedbackMessages = getLastRenderedPage().getFeedbackMessages();
+		List allMessages = feedbackMessages.messages(new IFeedbackMessageFilter()
+		{
+			private static final long serialVersionUID = 1L;
+
+			public boolean accept(FeedbackMessage message)
+			{
+				return message.getLevel() == level;
+			}
+		});
+		List actualMessages = new ArrayList();
+		for (Iterator iter = allMessages.iterator(); iter.hasNext();)
+		{
+			actualMessages.add(((FeedbackMessage)iter.next()).getMessage());
+		}
+		return actualMessages;
+	}
+
+	/**
+	 * dump the source of last rendered page
+	 */
+	public void dumpPage()
+	{
+		log.info(getServletResponse().getDocument());
+	}
+
+	/**
+	 * dump component trees
+	 */
+	public void debugComponentTrees()
+	{
+		debugComponentTrees("");
+	}
+
+
+	/**
+	 * Dump the component trees to log.
+	 *
+	 * @param filter
+	 *            Show only the components, which path contains the
+	 *            filterstring.
+	 */
+	public void debugComponentTrees(String filter)
+	{
+		log.info("debugging ----------------------------------------------");
+		for (Iterator iter = WicketTesterHelper.getComponentData(getLastRenderedPage()).iterator(); iter
+		.hasNext();)
+		{
+			WicketTesterHelper.ComponentData obj = (WicketTesterHelper.ComponentData)iter.next();
+			if (obj.path.matches(".*" + filter + ".*"))
+			{
+				log.info("path\t" + obj.path + " \t" + obj.type + " \t[" + obj.value + "]");
+			}
+		}
+	}
+
+	/**
+	 * Test that a component has been added to a AjaxRequestTarget, using
+	 * {@link AjaxRequestTarget#addComponent(Component)}. This method actually
+	 * tests that a component is on the AJAX response sent back to the client.
+	 * <p>
+	 * PLEASE NOTE! This method doesn't actually insert the component in the
+	 * client DOM tree, using javascript. But it shouldn't be needed because you
+	 * have to trust that the Wicket Ajax Javascript just works.
+	 *
+	 * @param component
+	 *            The component to test whether it's on the response.
+	 * @return
+	 */
+	public Result isComponentOnAjaxResponse(Component component)
+	{
+		String failMessage = "A component which is null could not have been added to the AJAX response";
+		notNull(failMessage, component);
+
+		// Get the AJAX response
+		String ajaxResponse = getServletResponse().getDocument();
+
+		// Test that the previous response was actually a AJAX response
+		failMessage = "The Previous response was not an AJAX response. "
+			+ "You need to execute an AJAX event, using clickLink, before using this assert";
+		boolean isAjaxResponse = ajaxResponse
+		.startsWith("<?xml version=\"1.0\" encoding=\"UTF-8\"?><ajax-response>");
+		Result result = isTrue(failMessage, isAjaxResponse);
+		if(result.wasFailed()) return result;
+
+		// See if the component has a markup id
+		String markupId = component.getMarkupId();
+
+		failMessage = "The component doesn't have a markup id, "
+			+ "which means that it can't have been added to the AJAX response";
+		result = isTrue(failMessage, !Strings.isEmpty(markupId));
+		if(result.wasFailed()) return result;
+
+		// Look for that the component is on the response, using the markup id
+		boolean isComponentInAjaxResponse = ajaxResponse.matches(".*<component id=\"" + markupId
+				+ "\" ?>.*");
+		failMessage = "Component wasn't found in the AJAX response";
+		return isTrue(failMessage, isComponentInAjaxResponse);
+	}
+
+	/**
+	 * Simulate that an AJAX event has been fired.
+	 *
+	 * @see #executeAjaxEvent(Component, String)
+	 *
+	 * @since 1.2.3
+	 * @param componentPath
+	 *            The component path.
+	 * @param event
+	 *            The event which we simulate is fired. If the event is null,
+	 *            the test will fail.
+	 */
+	public void executeAjaxEvent(String componentPath, String event)
+	{
+		Component component = getComponentFromLastRenderedPage(componentPath);
+		executeAjaxEvent(component, event);
+	}
+
+	/**
+	 * Simulate that an AJAX event has been fired. You add an AJAX event to a
+	 * component by using:
+	 *
+	 * <pre>
+	 *     ...
+	 *     component.add(new AjaxEventBehavior(&quot;ondblclick&quot;) {
+	 *         public void onEvent(AjaxRequestTarget) {}
+	 *     });
+	 *     ...
+	 * </pre>
+	 *
+	 * You can then test that the code inside onEvent actually does what it's
+	 * supposed to, using the WicketTester:
+	 *
+	 * <pre>
+	 *     ...
+	 *     tester.executeAjaxEvent(component, &quot;ondblclick&quot;);
+	 *
+	 *     // Test that the code inside onEvent is correct.
+	 *     ...
+	 * </pre>
+	 *
+	 * This also works with AjaxFormSubmitBehavior, where it will "submit" the
+	 * form before executing the command.
+	 * <p>
+	 * PLEASE NOTE! This method doesn't actually insert the component in the
+	 * client DOM tree, using javascript.
+	 *
+	 *
+	 * @param component
+	 *            The component which has the AjaxEventBehavior we wan't to
+	 *            test. If the component is null, the test will fail.
+	 * @param event
+	 *            The event which we simulate is fired. If the event is null,
+	 *            the test will fail.
+	 */
+	public void executeAjaxEvent(Component component, String event)
+	{
+		String failMessage = "Can't execute event on a component which is null.";
+		notNull(failMessage, component);
+
+		failMessage = "event must not be null";
+		notNull(failMessage, event);
+
+		// Run through all the behavior and select the LAST ADDED behavior which
+		// matches the event parameter.
+		AjaxEventBehavior ajaxEventBehavior = null;
+		List behaviors = component.getBehaviors();
+		for (Iterator iter = behaviors.iterator(); iter.hasNext();)
+		{
+			IBehavior behavior = (IBehavior)iter.next();
+
+			// AjaxEventBehavior is the one to look for
+			if (behavior instanceof AjaxEventBehavior)
+			{
+				AjaxEventBehavior tmp = (AjaxEventBehavior)behavior;
+
+				if (event.equals(tmp.getEvent()))
+				{
+					ajaxEventBehavior = tmp;
+				}
+			}
+		}
+
+		// If there haven't been found any event behaviors on the component
+		// which maches the parameters we fail.
+		failMessage = "No AjaxEventBehavior found on component: " + component.getId()
+		+ " which matches the event: " + event.toString();
+		notNull(failMessage, ajaxEventBehavior);
+
+		setupRequestAndResponse();
+		RequestCycle requestCycle = createRequestCycle();
+
+		// If the event is an FormSubmitBehavior then also "submit" the form
+		if (ajaxEventBehavior instanceof AjaxFormSubmitBehavior)
+		{
+			AjaxFormSubmitBehavior ajaxFormSubmitBehavior = (AjaxFormSubmitBehavior)ajaxEventBehavior;
+			submitAjaxFormSubmitBehavior(ajaxFormSubmitBehavior);
+		}
+
+		ajaxEventBehavior.onRequest();
+
+		// process the request target
+		requestCycle.getRequestTarget().respond(requestCycle);
+	}
+
+	/**
+	 * Get a TagTester based on a wicket:id. If more components exists with the
+	 * same wicket:id in the markup only the first one is returned.
+	 *
+	 * @param wicketId
+	 *            The wicket:id to search for.
+	 * @return The TagTester for the tag which has the given wicket:id.
+	 */
+	public TagTester getTagByWicketId(String wicketId)
+	{
+		return TagTester.createTagByAttribute(getServletResponse().getDocument(), "wicket:id",
+				wicketId);
+	}
+
+	/**
+	 * Get a TagTester based on an dom id. If more components exists with the
+	 * same id in the markup only the first one is returned.
+	 *
+	 * @param id
+	 *            The dom id to search for.
+	 * @return The TagTester for the tag which has the given dom id.
+	 */
+	public TagTester getTagById(String id)
+	{
+		return TagTester.createTagByAttribute(getServletResponse().getDocument(), "id", id);
+	}
+
+	/**
+	 * Helper method for all the places where an AjaxCall should submit an
+	 * associated form.
+	 *
+	 * @param behavior
+	 *            The AjaxFormSubmitBehavior with the form to "submit"
+	 */
+	private void submitAjaxFormSubmitBehavior(AjaxFormSubmitBehavior behavior)
+	{
+		// We need to get the form submitted, using reflection.
+		// It needs to be "submitted".
+		Form form = null;
+		try
+		{
+			Field formField = AjaxFormSubmitBehavior.class.getDeclaredField("form");
+			formField.setAccessible(true);
+			form = (Form)formField.get(behavior);
+		}
+		catch (Exception e)
+		{
+			fail(e.getMessage());
+		}
+
+		String failMessage = "No form attached to the submitlink.";
+		notNull(failMessage, form);
+
+		form.visitFormComponents(new FormComponent.AbstractVisitor()
+		{
+			public void onFormComponent(FormComponent formComponent)
+			{
+				if (!(formComponent instanceof Button) && !(formComponent instanceof RadioGroup)
+						&& !(formComponent instanceof CheckGroup))
+				{
+					String name = formComponent.getInputName();
+					String value = formComponent.getValue();
+
+					getServletRequest().setParameter(name, value);
+				}
+			}
+		});
+	}
+
+	private Result isTrue(String message, boolean condition)
+	{
+		if (condition)
+		{
+			return Result.pass();
+		}
+		return Result.fail(message);
+	}
+
+	private Result isEqual(Object expected, Object actual)
+	{
+		if (expected == null && actual == null)
+		{
+			return Result.pass();
+		}
+		if (expected != null && expected.equals(actual))
+		{
+			return Result.pass();
+		}
+		String message = "expected:<" + expected + "> but was:<" + actual + ">";
+		return Result.fail(message);
+	}
+
+	private void notNull(String message, Object object)
+	{
+		if (object == null) {
+			fail(message);
+		}
+	}
+
+	private Result isNull(String message, Object object)
+	{
+		if (object != null) {
+			return Result.fail(message);
+		}
+		return Result.pass();
+	}
+
+	private void fail(String message)
+	{
+		throw new WicketRuntimeException(message);
+	}
+
+	private void fail(String message, Throwable t)
+	{
+		throw new WicketRuntimeException(message, t);
+	}
+}

Added: incubator/wicket/branches/wicket-1.x/jdk-1.4/wicket/src/main/java/wicket/util/tester/Result.java
URL: http://svn.apache.org/viewvc/incubator/wicket/branches/wicket-1.x/jdk-1.4/wicket/src/main/java/wicket/util/tester/Result.java?view=auto&rev=521220
==============================================================================
--- incubator/wicket/branches/wicket-1.x/jdk-1.4/wicket/src/main/java/wicket/util/tester/Result.java (added)
+++ incubator/wicket/branches/wicket-1.x/jdk-1.4/wicket/src/main/java/wicket/util/tester/Result.java Thu Mar 22 04:24:44 2007
@@ -0,0 +1,72 @@
+/*
+ * 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 wicket.util.tester;
+
+/**
+ *
+ */
+public class Result
+{
+	private static final Result PASS = new Result(false);
+	private final boolean failed;
+	private final String message;
+
+	private Result(boolean failed)
+	{
+		this.failed = failed;
+		this.message = "";
+	}
+
+	private Result(boolean failed, String message)
+	{
+		this.failed = failed;
+		this.message = message;
+	}
+
+	/**
+	 * @param message the error message for the user
+	 * @return a Result which failed
+	 */
+	static Result fail(String message)
+	{
+		return new Result(true, message);
+	}
+
+	/**
+	 * @return a Result which passed
+	 */
+	static Result pass()
+	{
+		return PASS;
+	}
+
+	/**
+	 * @return true if the result was failed
+	 */
+	public boolean wasFailed()
+	{
+		return failed;
+	}
+
+	/**
+	 * @return the error message for the user
+	 */
+	public String getMessage()
+	{
+		return message;
+	}
+}