gt:       + "] was found.");
context: /**
   * Copyright (c) 2002-2012, JWebUnit team.
   *
   * This file is part of JWebUnit.
   *
   * JWebUnit is free software: you can redistribute it and/or modify
   * it under the terms of the GNU Lesser General Public License as published by
   * the Free Software Foundation, either version 3 of the License, or
   * (at your option) any later version.
   *
   * JWebUnit is distributed in the hope that it will be useful,
   * but WITHOUT ANY WARRANTY; without even the implied warranty of
   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   * GNU General Public License for more details.
   *
   * You should have received a copy of the GNU Lesser General Public License
   * along with JWebUnit.  If not, see <http://www.gnu.org/licenses/>.
   */
  package net.sourceforge.jwebunit.htmlunit;
  
  import com.gargoylesoftware.htmlunit.AlertHandler;
  import com.gargoylesoftware.htmlunit.BrowserVersion;
  import com.gargoylesoftware.htmlunit.ConfirmHandler;
  import com.gargoylesoftware.htmlunit.DefaultCredentialsProvider;
  import com.gargoylesoftware.htmlunit.ElementNotFoundException;
  import com.gargoylesoftware.htmlunit.FailingHttpStatusCodeException;
  import com.gargoylesoftware.htmlunit.ImmediateRefreshHandler;
  import com.gargoylesoftware.htmlunit.JavaScriptPage;
  import com.gargoylesoftware.htmlunit.Page;
  import com.gargoylesoftware.htmlunit.PromptHandler;
  import com.gargoylesoftware.htmlunit.RefreshHandler;
  import com.gargoylesoftware.htmlunit.TextPage;
  import com.gargoylesoftware.htmlunit.TopLevelWindow;
  import com.gargoylesoftware.htmlunit.UnexpectedPage;
  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.WebWindowNotFoundException;
  import com.gargoylesoftware.htmlunit.html.DomComment;
  import com.gargoylesoftware.htmlunit.html.DomNode;
  import com.gargoylesoftware.htmlunit.html.FrameWindow;
  import com.gargoylesoftware.htmlunit.html.HtmlAnchor;
  import com.gargoylesoftware.htmlunit.html.HtmlButton;
  import com.gargoylesoftware.htmlunit.html.HtmlButtonInput;
  import com.gargoylesoftware.htmlunit.html.HtmlCheckBoxInput;
  import com.gargoylesoftware.htmlunit.html.HtmlElement;
  import com.gargoylesoftware.htmlunit.html.HtmlForm;
  import com.gargoylesoftware.htmlunit.html.HtmlHiddenInput;
  import com.gargoylesoftware.htmlunit.html.HtmlImageInput;
  import com.gargoylesoftware.htmlunit.html.HtmlInput;
  import com.gargoylesoftware.htmlunit.html.HtmlOption;
  import com.gargoylesoftware.htmlunit.html.HtmlPage;
  import com.gargoylesoftware.htmlunit.html.HtmlRadioButtonInput;
  import com.gargoylesoftware.htmlunit.html.HtmlResetInput;
  import com.gargoylesoftware.htmlunit.html.HtmlSelect;
  import com.gargoylesoftware.htmlunit.html.HtmlSubmitInput;
  import com.gargoylesoftware.htmlunit.html.HtmlTable;
  import com.gargoylesoftware.htmlunit.html.HtmlTableCell;
  import com.gargoylesoftware.htmlunit.html.HtmlTableRow;
  import com.gargoylesoftware.htmlunit.html.HtmlTableRow.CellIterator;
  import com.gargoylesoftware.htmlunit.html.HtmlTextArea;
  import com.gargoylesoftware.htmlunit.util.Cookie;
  import com.gargoylesoftware.htmlunit.util.NameValuePair;
  import com.gargoylesoftware.htmlunit.xml.XmlPage;
  import net.sourceforge.jwebunit.api.HttpHeader;
  import net.sourceforge.jwebunit.api.IElement;
  import net.sourceforge.jwebunit.api.ITestingEngine;
  import net.sourceforge.jwebunit.exception.ExpectedJavascriptAlertException;
  import net.sourceforge.jwebunit.exception.ExpectedJavascriptConfirmException;
  import net.sourceforge.jwebunit.exception.ExpectedJavascriptPromptException;
  import net.sourceforge.jwebunit.exception.TestingEngineResponseException;
  import net.sourceforge.jwebunit.exception.UnableToSetFormException;
  import net.sourceforge.jwebunit.exception.UnexpectedJavascriptAlertException;
  import net.sourceforge.jwebunit.exception.UnexpectedJavascriptConfirmException;
  import net.sourceforge.jwebunit.exception.UnexpectedJavascriptPromptException;
  import net.sourceforge.jwebunit.html.Cell;
  import net.sourceforge.jwebunit.html.Row;
  import net.sourceforge.jwebunit.html.Table;
  import net.sourceforge.jwebunit.javascript.JavascriptAlert;
  import net.sourceforge.jwebunit.javascript.JavascriptConfirm;
  import net.sourceforge.jwebunit.javascript.JavascriptPrompt;
  import net.sourceforge.jwebunit.util.TestContext;
  import org.apache.http.auth.AuthScope;
  import org.apache.regexp.RE;
  import org.apache.regexp.RESyntaxException;
  import org.slf4j.Logger;
  import org.slf4j.LoggerFactory;
  import org.w3c.dom.Node;
  import org.w3c.dom.NodeList;
  
  import java.io.IOException;
  import java.io.InputStream;
  import java.net.InetAddress;
  import java.net.URL;
  import java.net.UnknownHostException;
  import java.util.ArrayList;
  import java.util.Arrays;
  import java.util.Calendar;
  import java.util.Date;
  import java.util.LinkedList;
  import java.util.List;
  import java.util.Map;
  import java.util.Set;
  
  /**
   * Acts as the wrapper for HtmlUnit access. A testing engine is initialized with a given URL, and maintains conversational state
   * as the dialog progresses through link navigation, form submission, etc.
   *
   * @author Julien Henry
   *
   */
  public class HtmlUnitTestingEngineImpl implements ITestingEngine {
    /**
     * Logger for this class.
     */
    private final Logger logger = LoggerFactory.getLogger(HtmlUnitTestingEngineImpl.class);
  
    /**
     * holder for alternative refresh handler.
     */
    private RefreshHandler refreshHandler;
    /**
     * Encapsulate browser abilities.
     */
    private WebClient wc;
  
    /**
     * The currently selected window.
     */
    private WebWindow win;
  
    /**
     * A ref to the test context.
     */
    private TestContext testContext;
  
    /**
     * The currently selected form.
     */
    private HtmlForm form;
  
    /**
     * Is Javascript enabled.
     */
    private boolean jsEnabled = true;
  
    /**
     * Should throw exception on Javascript error.
     */
    private boolean throwExceptionOnScriptError = true;
  
    /**
     * Javascript alerts.
     */
    private List<JavascriptAlert> expectedJavascriptAlerts = new LinkedList<JavascriptAlert>();
  
    /**
     * Javascript confirms.
     */
    private List<JavascriptConfirm> expectedJavascriptConfirms = new LinkedList<JavascriptConfirm>();
  
    /**
     * Javascript prompts.
     */
    private List<JavascriptPrompt> expectedJavascriptPrompts = new LinkedList<JavascriptPrompt>();
  
    /**
     * The default browser version.
     */
    private BrowserVersion defaultBrowserVersion = BrowserVersion.FIREFOX_3_6;
  
    /**
       * Should we ignore failing status codes?
       */
    private boolean ignoreFailingStatusCodes = false;
  
    /**
    * Do we provide a timeout limit (in seconds)? Default 0 = unlimited timeout.
    */
    private int timeout = 0;
  
    // Implementation of IJWebUnitDialog
  
    /**
     * Initializes default HtmlUnit testing engine implementation.
     */
    public HtmlUnitTestingEngineImpl() {
    }
  
    /**
     * Initializes HtmlUnit testing engine implementation with web client.
     *
     * @param client web client
     */
    HtmlUnitTestingEngineImpl(WebClient client) {
      this.wc = client;
    }
  
    /**
     * Begin a dialog with an initial URL and test client context.
     *
     * @param initialURL absolute url at which to begin dialog.
     * @param context contains context information for the test client.
     * @throws TestingEngineResponseException
     */
    public void beginAt(URL initialURL, TestContext context)
        throws TestingEngineResponseException {
      this.setTestContext(context);
      initWebClient();
      gotoPage(initialURL);
    }
  
    /**
     * Close the browser and check that all expected Javascript alerts, confirms and
     * prompts have been taken care of.
     */
    public void closeBrowser() throws ExpectedJavascriptAlertException,
        ExpectedJavascriptConfirmException,
        ExpectedJavascriptPromptException {
      if (wc != null) {
        wc.closeAllWindows();
        wc = null;
      }
      form = null; // reset current form
      if (this.expectedJavascriptAlerts.size() > 0) {
        throw new ExpectedJavascriptAlertException(
            ((JavascriptAlert) (expectedJavascriptAlerts.get(0)))
                .getMessage());
      }
      if (this.expectedJavascriptConfirms.size() > 0) {
        throw new ExpectedJavascriptConfirmException(
            ((JavascriptConfirm) (expectedJavascriptConfirms.get(0)))
                .getMessage());
      }
      if (this.expectedJavascriptPrompts.size() > 0) {
        throw new ExpectedJavascriptPromptException(
            ((JavascriptPrompt) (expectedJavascriptPrompts.get(0)))
                .getMessage());
      }
  
    }
  
    /**
     * Go to a particular page.
     *
     * @throws TestingEngineResponseException if an error response code is encountered
     * 	and ignoreFailingStatusCodes is not enabled.
     */
    public void gotoPage(URL initialURL) throws TestingEngineResponseException {
      try {
        wc.getPage(initialURL);
        win = wc.getCurrentWindow();
        form = null;
      } catch (FailingHttpStatusCodeException ex) {
        throw new TestingEngineResponseException(ex.getStatusCode(),
            "unexpected status code [" + ex.getStatusCode() + "] at URL: [" + initialURL + "]", ex);
      } catch (IOException ex) {
        throw new RuntimeException(ex);
      }
    }
  
    /**
     * @see net.sourceforge.jwebunit.api.IJWebUnitDialog#setScriptingEnabled(boolean)
     */
    public void setScriptingEnabled(boolean value) {
      // This variable is used to set Javascript before wc is instancied
      jsEnabled = value;
      if (wc != null) {
        wc.getOptions().setJavaScriptEnabled(value);
      }
    }
  
    public void setThrowExceptionOnScriptError(boolean value) {
      throwExceptionOnScriptError = value;
      if (wc != null) {
        wc.getOptions().setThrowExceptionOnScriptError(value);
      }
    }
  
    public List<javax.servlet.http.Cookie> getCookies() {
      List<javax.servlet.http.Cookie> result = new LinkedList<javax.servlet.http.Cookie>();
      Set<Cookie> cookies = wc.getCookieManager().getCookies();
      for (Cookie cookie : cookies) {
        javax.servlet.http.Cookie c = new javax.servlet.http.Cookie(
            cookie.getName(), cookie.getValue());
        c.setComment(cookie.toHttpClient().getComment());
        c.setDomain(cookie.getDomain());
        Date expire = cookie.toHttpClient().getExpiryDate();
        if (expire == null) {
          c.setMaxAge(-1);
        } else {
          Date now = Calendar.getInstance().getTime();
          // Convert milli-second to second
          Long second = Long.valueOf((expire.getTime() - now.getTime()) / 1000);
          c.setMaxAge(second.intValue());
        }
        c.setPath(cookie.getPath());
        c.setSecure(cookie.toHttpClient().isSecure());
        c.setVersion(cookie.toHttpClient().getVersion());
        result.add(c);
      }
      return result;
    }
  
    public boolean hasWindow(String windowName) {
      try {
        getWindow(windowName);
      } catch (WebWindowNotFoundException e) {
        return false;
      }
      return true;
    }
  
    public boolean hasWindowByTitle(String title) {
      return getWindowByTitle(title) != null;
    }
  
    /**
     * Make the window with the given name in the current conversation active.
     *
     * @param windowName
     */
    public void gotoWindow(String windowName) {
      setMainWindow(getWindow(windowName));
    }
  
    public void gotoWindow(int windowID) {
      setMainWindow((WebWindow) wc.getWebWindows().get(windowID));
    }
  
    public int getWindowCount() {
      return wc.getWebWindows().size();
    }
  
    /**
     * Goto first window with the given title.
     *
     * @param title
     */
    public void gotoWindowByTitle(String title) {
      WebWindow window = getWindowByTitle(title);
      if (window != null) {
        setMainWindow(window);
      }
      else {
        throw new RuntimeException("No window found with title [" + title + "]");
      }
    }
  
    /**
     * Close the current window.
     */
    public void closeWindow() {
      if (win != null) {
        ((TopLevelWindow) win.getTopWindow()).close();
        win = wc.getCurrentWindow();
        form = null;
      }
  
    }
  
    /**
     * {@inheritDoc}
     */
    public boolean hasFrame(String frameNameOrId) {
      return getFrame(frameNameOrId) != null;
    }
  
    /**
     * {@inheritDoc}
     */
    public void gotoFrame(String frameNameOrId) {
      WebWindow frame = getFrame(frameNameOrId);
      if (frame == null) {
        throw new RuntimeException("No frame found in current page with name or id [" + frameNameOrId + "]");
      }
      win = frame;
    }
  
    /**
     * {@inheritDoc}
     */
    public void setWorkingForm(int index) {
      setWorkingForm(getForm(index));
    }
  
    /**
     * {@inheritDoc}
     */
    public void setWorkingForm(String nameOrId, int index) {
      setWorkingForm(getForm(nameOrId, index));
    }
  
    /**
     * Return true if the current response contains a form.
     */
    public boolean hasForm() {
      return ((HtmlPage) win.getEnclosedPage()).getForms().size() > 0;
    }
  
    /**
     * Return true if the current response contains a specific form.
     *
     * @param nameOrID name of id of the form to check for.
     */
    public boolean hasForm(String nameOrID) {
      return getForm(nameOrID) != null;
    }
  
    public boolean hasFormParameterNamed(String paramName) {
      for (HtmlElement e : getCurrentPage().getHtmlElementDescendants()) {
        if (e.getAttribute("name").equals(paramName)) {
          // set the working form if none has been set
          if (e.getEnclosingForm() != null && getWorkingForm() == null)
            setWorkingForm(e.getEnclosingForm());
          return true;
        }
      }
      return false;
    }
  
    /**
     * Return the current value of a text input element with name <code>paramName</code>.
     *
     * @param paramName name of the input element. TODO: Find a way to handle multiple text input element with same
     *            name.
     */
    public String getTextFieldValue(String paramName) {
      // first try the current form
      if (form != null) {
        for (HtmlElement e : form.getHtmlElementDescendants()) {
          if (e instanceof HtmlInput && e.getAttribute("name").equals(paramName)) {
            // we found it
            return ((HtmlInput) e).getValueAttribute();
          }
          if (e instanceof HtmlTextArea && e.getAttribute("name").equals(paramName)) {
            // we found it
            return ((HtmlTextArea) e).getText();
          }
        }
      }
  
      // not in the current form: try *all* elements
      HtmlElement outside_element = getHtmlElementWithAttribute("name", paramName);
      if (outside_element != null) {
        if (outside_element instanceof HtmlInput) {
          // set current form if not null
          if (outside_element.getEnclosingForm() != null)
            form = outside_element.getEnclosingForm();
          return ((HtmlInput) outside_element).getValueAttribute();
        }
        if (outside_element instanceof HtmlTextArea) {
          // set current form if not null
          if (outside_element.getEnclosingForm() != null)
            form = outside_element.getEnclosingForm();
          return ((HtmlTextArea) outside_element).getText();
        }
      }
  
      // we can't find it anywhere
      throw new RuntimeException(
          "getTextFieldParameterValue failed, text field with name ["
            + paramName + "] does not exist.");
    }
  
    /**
     * Return the current value of a hidden input element with name <code>paramName</code>.
     *
     * @param paramName name of the input element. TODO: Find a way to handle multiple hidden input element with same
     *            name.
     */
    public String getHiddenFieldValue(String paramName) {
      // first try the current form
      if (form != null) {
        for (HtmlElement e : form.getHtmlElementDescendants()) {
          if (e instanceof HtmlHiddenInput && e.getAttribute("name").equals(paramName)) {
            // we found it
            return ((HtmlInput) e).getValueAttribute();
          }
        }
      }
  
      // not in the current form: try *all* elements
      HtmlElement outside_element = getHtmlElementWithAttribute("name", paramName);
      if (outside_element != null) {
        if (outside_element instanceof HtmlHiddenInput) {
          // set current form if not null
          if (outside_element.getEnclosingForm() != null)
            form = outside_element.getEnclosingForm();
          return ((HtmlHiddenInput) outside_element).getValueAttribute();
        }
      }
  
      // we can't find it anywhere
      throw new RuntimeException("No hidden field with name [" + paramName
        + "] was found.");
    }
  
    /**
     * Set a form text, password input element or textarea to the provided value.
     *
     * @param fieldName name of the input element or textarea
     * @param text parameter value to submit for the element.
     */
    public void setTextField(String paramName, String text) {
      // first try the current form
      if (form != null) {
        for (HtmlElement e : form.getHtmlElementDescendants()) {
          if (e instanceof HtmlInput && e.getAttribute("name").equals(paramName)) {
            // we found it
            ((HtmlInput) e).setValueAttribute(text);
            return;
          }
          if (e instanceof HtmlTextArea && e.getAttribute("name").equals(paramName)) {
            // we found it
            ((HtmlTextArea) e).setText(text);
            return;
          }
        }
      }
  
      // not in the current form: try *all* elements
      HtmlElement outside_element = getHtmlElementWithAttribute("name", paramName);
      if (outside_element != null) {
        if (outside_element instanceof HtmlInput) {
          ((HtmlInput) outside_element).setValueAttribute(text);
          // set current form if not null
          if (outside_element.getEnclosingForm() != null)
            form = outside_element.getEnclosingForm();
          return;
        }
        if (outside_element instanceof HtmlTextArea) {
          ((HtmlTextArea) outside_element).setText(text);
          // set current form if not null
          if (outside_element.getEnclosingForm() != null)
            form = outside_element.getEnclosingForm();
          return;
        }
      }
  
      // we can't find it anywhere
      throw new RuntimeException("No text field with name [" + paramName
  