Package com.canoo.webtest.engine

Source Code of com.canoo.webtest.engine.WebClientContext$StoredResponse

package com.canoo.webtest.engine;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Stack;

import org.apache.log4j.Logger;

import com.canoo.webtest.steps.Step;
import com.canoo.webtest.steps.request.SelectWebClient;
import com.canoo.webtest.util.Checker;
import com.gargoylesoftware.htmlunit.Page;
import com.gargoylesoftware.htmlunit.TopLevelWindow;
import com.gargoylesoftware.htmlunit.WebClient;
import com.gargoylesoftware.htmlunit.WebResponse;
import com.gargoylesoftware.htmlunit.WebWindow;
import com.gargoylesoftware.htmlunit.WebWindowEvent;
import com.gargoylesoftware.htmlunit.WebWindowListener;
import com.gargoylesoftware.htmlunit.html.FrameWindow;
import com.gargoylesoftware.htmlunit.html.HtmlForm;
import com.gargoylesoftware.htmlunit.html.HtmlPage;

/*
* Contains all context information bound to a WebClient.
* Except if selectWebClient is used test runs with only 1 web client
* and only one instance of this class is used.
* @author Marc Guillemot
*/
public class WebClientContext {
  private static final Logger LOG = Logger.getLogger(WebClientContext.class);
  private WebClient fWebClient;
  private String fSavedUserName;
  private String fSavedPassword;
  private StoredResponse fPreviousResponse = NO_STORED_RESPONSE;
  private StoredResponse fCurrentResponse = NO_STORED_RESPONSE;
  private static final StoredResponse NO_STORED_RESPONSE = new StoredResponse(null);
  private final WebWindowListener fWindowListener = new CurrentWindowTracker();
  private final Stack fWindows = new Stack();
  private HtmlForm fCurrentForm;
  private final String fName;

  /**
   * Used to capture the state of the (current and previous) responses to be able to
   * restore it later. Uses the Memento pattern.
   */
  public static final class StoredResponses {
    private final StoredResponse fPreviousResponse;
    private final StoredResponse fCurrentResponse;

    private StoredResponses(final WebClientContext context) {
      fPreviousResponse = context.fPreviousResponse;
      fCurrentResponse = context.fCurrentResponse;
    }
  }


  /**
   * Keeps a Page and its stored file together.
   */
  static class StoredResponse {
    private final Page fPage;
    private String fFile;

    StoredResponse(final Page page) {
      fPage = page;
    }

    public Page getPage() {
      return fPage;
    }

    public String getFile() {
      return fFile;
    }

    public void setFile(final String file) {
      fFile = file;
    }
  }

  /**
   * Tracks window event to determine the "current" response
   */
  class CurrentWindowTracker implements WebWindowListener {
    public void webWindowClosed(final WebWindowEvent event) {
      fWindows.remove(event.getWebWindow());
      // don't change currentResponse here else it causes problems with <previousResponse>
      LOG.debug("Window closed (contains: " + event.getWebWindow().getEnclosedPage().getWebResponse().getRequestUrl()
          + ")");
    }

    public void webWindowContentChanged(final WebWindowEvent event) {
      final WebWindow window = event.getWebWindow();
      final WebResponse webResp = event.getNewPage().getWebResponse();
            LOG.info("Content of window changed to " + webResp.getRequestUrl() + " (" + webResp.getContentType() + ")");

      final boolean takeItAsNew;
      if (window instanceof TopLevelWindow && event.getOldPage() == null) {
        takeItAsNew = true;
        LOG.info("Content loaded in newly opened window, its content will become current response");
      } else if (fCurrentResponse.getPage() != null
          && fCurrentResponse.getPage().getEnclosingWindow() == window) {
        takeItAsNew = true;
        LOG.info("Content of current window changed, it will become current response");
      }
      // content loaded in an other window as the "current" one
      // by js becomes "current" only if new top window is opened
      else if (getWebClient().getJavaScriptEngine() == null
          || !getWebClient().getJavaScriptEngine().isScriptRunning()) {
        if (window instanceof FrameWindow
            && !HtmlPage.READY_STATE_COMPLETE.equals(
            ((FrameWindow) window).getEnclosingPage().getDocumentElement().getReadyState())) {
          LOG.info("Content of frame window has changed without javascript while enclosing page is loading, "
              + "it will NOT become current response");
          LOG.debug("Enclosing page's state: " + ((FrameWindow) window).getEnclosingPage().getDocumentElement().getReadyState());
          LOG.debug("Enclosing page's url: " + ((FrameWindow) window).getEnclosingPage().getWebResponse().getRequestUrl());
          takeItAsNew = false;
        } else {
          LOG.info("Content of window changed without javascript, it will become current response");
          takeItAsNew = true;
        }
      } else {
        LOG.info("Content of window changed with javascript, it will NOT become current response");
        takeItAsNew = false;
      }

      if (takeItAsNew) {
        saveResponseAsCurrent(window.getEnclosedPage());
      }
    }

    /**
     * @see com.gargoylesoftware.htmlunit.WebWindowListener#webWindowOpened
     */
    public void webWindowOpened(final WebWindowEvent event) {
      fWindows.push(event.getWebWindow());
      // page is not loaded yet, don't set it now as current window
    }
  }

  public WebClientContext(final String _name)
  {
    fName = _name;
  }
 
  /**
   * Gets the name of this instance. Used by {@link SelectWebClient}.
   */
  public String getName()
  {
    return fName;
  }

  public WebClient getWebClient() {
    return fWebClient;
  }

  public String getSavedUserName() {
    return fSavedUserName;
  }

  public String getSavedPassword() {
    return fSavedPassword;
  }

  /**
   * Gets the current responses (currentResponse and previousResponse) of
   * the context to be able to restore them later.
   * <p/>
   * MG: may probably be problematic (like previousResponse) when windows have been closed
   *
   * @return the status
   */
  public StoredResponses getResponses() {
    return new StoredResponses(this);
  }

  /**
   * Restore the responses to a previously saved value.
   *
   * @param savedResponses the responses to restore
   */
  public void restoreResponses(final StoredResponses savedResponses) {
    fPreviousResponse = savedResponses.fPreviousResponse;
    fCurrentResponse = savedResponses.fCurrentResponse;
    LOG.info("Responses restored");
  }

  /**
   * Gets the response on which actions and verifications will occur.
   *
   * @return the response
   */
  public Page getCurrentResponse() {
    // test if window of current response has not been closed
    if (fCurrentResponse.getPage() != null
        && !fWebClient.getWebWindows().contains(fCurrentResponse.getPage().getEnclosingWindow())) {
      LOG.info("The window containing current response has been closed, "
          + "the content of the last opened window will become the current response");
      final WebWindow window = (WebWindow) fWindows.peek();
      saveResponseAsCurrent(window.getEnclosedPage());
    }
    return fCurrentResponse.getPage();
  }

  public String getCurrentResponseFile() {
    return fCurrentResponse.getFile();
  }

  public void setCurrentResponseFile(final String name) {
    fCurrentResponse.setFile(name);
  }


  /**
   * Gets the current response as {@link com.gargoylesoftware.htmlunit.html.HtmlPage}
   *
   * @param step
   * @throws StepExecutionException if the current response isn't an html page
   */
  public HtmlPage getCurrentHtmlResponse(final Step step) {
    if (!(getCurrentResponse() instanceof HtmlPage)) {
      throw new StepExecutionException("Current response is not an HTML page but of type "
          + getCurrentResponse().getWebResponse().getContentType(), step);
    }
    return (HtmlPage) getCurrentResponse();
  }

  public void restorePreviousResponse() {
    final WebWindow window = fPreviousResponse.getPage().getEnclosingWindow();
    if (!fWebClient.getWebWindows().contains(window)) {
      // register the window "back" to the browser
      fWebClient.registerWebWindow(window);
    }
    saveResponseAsCurrent(fPreviousResponse);
  }


  /**
   * Sets the current and previous response for this context and this step.
   *
   * @param page The page to become the current response.
   */
  public void saveResponseAsCurrent(final Page page) {
    saveResponseAsCurrent(new StoredResponse(page));
  }

  /**
   * Sets the current and previous response for this context and this step
   * with the associated files (if any)
   *
   * @param current The future current response.
   */
  protected void saveResponseAsCurrent(final StoredResponse current) {
    Checker.assertFalse(current == null || current.getPage() == null, "Illegal new current response");
    setCurrentForm(null); // reset current form
    fPreviousResponse = fCurrentResponse;
    fCurrentResponse = current;

    LOG.info("Current response now: " + current.getPage().getWebResponse().getRequestUrl());
    LOG.debug("Previous response: " + (fPreviousResponse.getPage() != null ?
        fPreviousResponse.getPage().getWebResponse().getRequestUrl() : null));
  }

  public void setWebClient(final WebClient webClient) {
    fWebClient = webClient;
    restoreWindowListener();
    fWindows.push(webClient.getCurrentWindow());
  }

  public void suspendWindowListener() {
    fWebClient.removeWebWindowListener(fWindowListener);
  }

  public void restoreWindowListener() {
    fWebClient.addWebWindowListener(fWindowListener);
  }

  public void setSavedUserName(final String userName) {
    fSavedUserName = userName;
  }

  public void setSavedPassword(final String password) {
    fSavedPassword = password;
  }

  /**
   * Gets the current form in the current response. This is the one that has been selected by setting the last form field.
   *
   * @return <code>null</code> if no current form available
   */
  public HtmlForm getCurrentForm() {
    return fCurrentForm;
  }

  /**
   * Sets the form that has to be used as the default one for setting fields.
   *
   * @param form new current form or if null then set current form to none (reset)
   */
  public void setCurrentForm(final HtmlForm form) {
    fCurrentForm = form;
    if (form != null) {
      LOG.info("Current form set to (action=" + form.getActionAttribute() + ")");
    } else {
      LOG.info("Current form set to none");
    }
  }

  /**
   * Closes the WebClient. This ensures that running js scripts are stopped.
   * This instance should be used once this method has been called.
   */
  public void destroy()
  {
    suspendWindowListener();
    // first get the top windows and then close them to avoid ConcurrentModificationException
    final List topWindows = new ArrayList();
    for (final Iterator iter=fWebClient.getWebWindows().iterator(); iter.hasNext();)
    {
      final WebWindow window = (WebWindow) iter.next();
      if (window instanceof TopLevelWindow)
      {
        topWindows.add(window);
      }
    }
    for (final Iterator iter=topWindows.iterator(); iter.hasNext();)
    {
      final TopLevelWindow window = (TopLevelWindow) iter.next();
      window.close();
    }
   
    fWebClient = null;
    fPreviousResponse = null;
    fCurrentResponse = null;
    fWindows.empty();
    fCurrentForm = null;
  }
}
TOP

Related Classes of com.canoo.webtest.engine.WebClientContext$StoredResponse

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.