Package org.apache.wicket.util.tester

Source Code of org.apache.wicket.util.tester.BaseWicketTester

/*
* 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.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.Iterator;
import java.util.List;

import org.apache.wicket.Component;
import org.apache.wicket.Page;
import org.apache.wicket.PageParameters;
import org.apache.wicket.RequestCycle;
import org.apache.wicket.Session;
import org.apache.wicket.WicketRuntimeException;
import org.apache.wicket.ajax.AjaxEventBehavior;
import org.apache.wicket.ajax.AjaxRequestTarget;
import org.apache.wicket.ajax.form.AjaxFormSubmitBehavior;
import org.apache.wicket.ajax.markup.html.AjaxFallbackLink;
import org.apache.wicket.ajax.markup.html.AjaxLink;
import org.apache.wicket.ajax.markup.html.form.AjaxSubmitLink;
import org.apache.wicket.behavior.AbstractAjaxBehavior;
import org.apache.wicket.behavior.IBehavior;
import org.apache.wicket.feedback.FeedbackMessage;
import org.apache.wicket.feedback.FeedbackMessages;
import org.apache.wicket.feedback.IFeedbackMessageFilter;
import org.apache.wicket.markup.html.basic.Label;
import org.apache.wicket.markup.html.form.Button;
import org.apache.wicket.markup.html.form.CheckGroup;
import org.apache.wicket.markup.html.form.Form;
import org.apache.wicket.markup.html.form.FormComponent;
import org.apache.wicket.markup.html.form.RadioGroup;
import org.apache.wicket.markup.html.link.AbstractLink;
import org.apache.wicket.markup.html.link.BookmarkablePageLink;
import org.apache.wicket.markup.html.link.IPageLink;
import org.apache.wicket.markup.html.link.Link;
import org.apache.wicket.markup.html.link.PageLink;
import org.apache.wicket.markup.html.list.ListView;
import org.apache.wicket.markup.html.panel.Panel;
import org.apache.wicket.protocol.http.MockHttpServletResponse;
import org.apache.wicket.protocol.http.MockWebApplication;
import org.apache.wicket.protocol.http.WebApplication;
import org.apache.wicket.protocol.http.WebRequestCycle;
import org.apache.wicket.util.diff.DiffUtil;
import org.apache.wicket.util.lang.Classes;
import org.apache.wicket.util.string.Strings;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;


/**
* 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 Logger log = LoggerFactory.getLogger(BaseWicketTester.class);

  /**
   * @author jcompagner
   */
  private static final class TestPageSource implements ITestPageSource
  {
    private final Page page;
    private static final long serialVersionUID = 1L;

    /**
     * Construct.
     *
     * @param page
     */
    private TestPageSource(Page page)
    {
      this.page = page;
    }

    public Page getTestPage()
    {
      return page;
    }
  }

  /**
   * @author frankbille
   */
  public static class DummyWebApplication extends WebApplication
  {
    public Class getHomePage()
    {
      return DummyHomePage.class;
    }

    protected void outputDevelopmentModeWarning()
    {
      // Do nothing.
    }
  }

  /**
   * 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 org.apache.wicket.Application#getHomePage()
       */
      public Class getHomePage()
      {
        return homePage;
      }

      protected void outputDevelopmentModeWarning()
      {
        // Do nothing.
      }
    }, 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 org.apache.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();
    CharSequence url = behavior.getCallbackUrl(false);
    setupRequestAndResponse();
    cycle = createRequestCycle();
    getServletRequest().setRequestToRedirectString(url.toString());
    processRequestCycle(cycle);
  }

  /**
   * Render the page
   *
   * @param page
   * @return The page rendered
   */
  public final Page startPage(final Page page)
  {
    return startPage(new TestPageSource(page));
  }

  /**
   * 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)
  {
    processRequestCycle(pageClass);
    return getLastRenderedPage();
  }

  /**
   * create a {@link FormTester} for the form at path, and fill all child
   * {@link org.apache.wicket.markup.html.form.FormComponent}s with blank
   * String initially.
   *
   * @param path
   *            path to {@link Form} component
   * @return FormTester A FormTester instance for testing form
   * @see #newFormTester(String, boolean)
   */
  public FormTester newFormTester(String path)
  {
    return newFormTester(path, true);
  }

  /**
   * create a {@link FormTester} for the form at path.
   *
   * @param path
   *            path to {@link Form} component
   * @param fillBlankString
   *            specify whether fill all child
   *            {@link org.apache.wicket.markup.html.form.FormComponent}s
   *            with blankString initially.
   * @return FormTester A FormTester instance for testing form
   * @see FormTester
   */
  public FormTester newFormTester(String path, boolean fillBlankString)
  {
    return new FormTester(path, (Form)getComponentFromLastRenderedPage(path), this,
        fillBlankString);
  }

  /**
   * 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);
  }

    /**
     * A helper method for starting a component for a test without attaching it to a Page.
     *
     * Components which are somehow dependent on the page structure can not be currently tested
     * with this method.
     *
     * Example:
     *
     * UserDataView view = new UserDataView("view", new ListDataProvider(userList));
     * tester.startComponent(view);
     * assertEquals(4, view.size());
     *
     * @param component
     */
    public void startComponent(Component component)
    {
        component.attach();
        if (component instanceof FormComponent)
        {
                ((FormComponent) component).processInput();
        }
        component.beforeRender();
    }

  /**
   * 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 org.apache.wicket.MarkupContainer#get(String)
   */
  public Component getComponentFromLastRenderedPage(String path)
  {
    final Component component = getLastRenderedPage().get(path);
    if (component == null)
    {
      fail("path: '" + path + "' does not 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 AbstractLink)
    {
      AbstractLink link = (AbstractLink)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)
  {
    Page page = getLastRenderedPage();
    if (page == null)
    {
      return Result.fail("page was null");
    }
    if (!page.getClass().isAssignableFrom(expectedRenderedPageClass))
    {
      return isEqual(Classes.simpleName(expectedRenderedPageClass), Classes.simpleName(page
          .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());
  }

  /**
   * 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 = Session.get().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);

    // initialize the request only if needed to allow the user to pass request parameters, see WICKET-254
    WebRequestCycle requestCycle;
    if (RequestCycle.get() == null)
      requestCycle = setupRequestAndResponse();
    else
      requestCycle = (WebRequestCycle)RequestCycle.get();

    // 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
    processRequestCycle(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();

          // Set request parameter with the field value, but do not modify an existing request parameter explicitly set using FormTester.setValue()
          if (getServletRequest().getParameterMap().get(name) == null)
            getServletRequest().setParameter(name, value);
        }
      }
    });
  }

  /**
   *
   * @return
   */
  public String getContentTypeFromResponseHeader()
  {
    String contentType = ((MockHttpServletResponse)getWicketResponse().getHttpServletResponse())
        .getHeader("Content-Type");
    if (contentType == null)
    {
      throw new WicketRuntimeException("No Content-Type header found");
    }
    return contentType;
  }

  /**
   *
   * @return
   */
  public int getContentLengthFromResponseHeader()
  {
    String contentLength = ((MockHttpServletResponse)getWicketResponse()
        .getHttpServletResponse()).getHeader("Content-Length");
    if (contentLength == null)
    {
      throw new WicketRuntimeException("No Content-Length header found");
    }
    return Integer.parseInt(contentLength);
  }

  /**
   *
   * @return
   */
  public String getLastModifiedFromResponseHeader()
  {
    return ((MockHttpServletResponse)getWicketResponse().getHttpServletResponse())
        .getHeader("Last-Modified");
  }

  /**
   *
   * @return
   */
  public String getContentDispositionFromResponseHeader()
  {
    return ((MockHttpServletResponse)getWicketResponse().getHttpServletResponse())
        .getHeader("Content-Disposition");
  }

  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);
  }
}
TOP

Related Classes of org.apache.wicket.util.tester.BaseWicketTester

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.