Java tutorial
/** * Copyright (c) 2002-2015, 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 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; 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; /** * 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<>(); /** * Javascript confirms. */ private List<JavascriptConfirm> expectedJavascriptConfirms = new LinkedList<>(); /** * Javascript prompts. */ private List<JavascriptPrompt> expectedJavascriptPrompts = new LinkedList<>(); /** * The default browser version. */ private BrowserVersion defaultBrowserVersion = BrowserVersion.FIREFOX_38; /** * 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 */ @Override 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. */ @Override public void closeBrowser() throws ExpectedJavascriptAlertException, ExpectedJavascriptConfirmException, ExpectedJavascriptPromptException { if (wc != null) { wc.close(); wc = null; } form = null; // reset current form if (this.expectedJavascriptAlerts.size() > 0) { throw new ExpectedJavascriptAlertException((expectedJavascriptAlerts.get(0)).getMessage()); } if (this.expectedJavascriptConfirms.size() > 0) { throw new ExpectedJavascriptConfirmException((expectedJavascriptConfirms.get(0)).getMessage()); } if (this.expectedJavascriptPrompts.size() > 0) { throw new ExpectedJavascriptPromptException((expectedJavascriptPrompts.get(0)).getMessage()); } } /** * Go to a particular page. * * @throws TestingEngineResponseException if an error response code is encountered * and ignoreFailingStatusCodes is not enabled. */ @Override 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) */ @Override 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); } } @Override public void setThrowExceptionOnScriptError(boolean value) { throwExceptionOnScriptError = value; if (wc != null) { wc.getOptions().setThrowExceptionOnScriptError(value); } } @Override public List<javax.servlet.http.Cookie> getCookies() { List<javax.servlet.http.Cookie> result = new LinkedList<>(); 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; } @Override public boolean hasWindow(String windowName) { try { getWindow(windowName); } catch (WebWindowNotFoundException e) { return false; } return true; } @Override public boolean hasWindowByTitle(String title) { return getWindowByTitle(title) != null; } /** * Make the window with the given name in the current conversation active. * * @param windowName */ @Override public void gotoWindow(String windowName) { setMainWindow(getWindow(windowName)); } @Override public void gotoWindow(int windowID) { setMainWindow(wc.getWebWindows().get(windowID)); } @Override public int getWindowCount() { return wc.getWebWindows().size(); } /** * Goto first window with the given title. * * @param title */ @Override 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. */ @Override public void closeWindow() { if (win != null) { ((TopLevelWindow) win.getTopWindow()).close(); win = wc.getCurrentWindow(); form = null; } } /** * {@inheritDoc} */ @Override public boolean hasFrame(String frameNameOrId) { return getFrame(frameNameOrId) != null; } /** * {@inheritDoc} */ @Override 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} */ @Override public void setWorkingForm(int index) { HtmlForm newForm = getForm(index); if (newForm == null) { throw new UnableToSetFormException("No form found in current page with index " + index); } setWorkingForm(newForm); } /** * {@inheritDoc} */ @Override public void setWorkingForm(String nameOrId, int index) { HtmlForm newForm = getForm(nameOrId, index); if (newForm == null) { throw new UnableToSetFormException( "No form found in current page with name or id [" + nameOrId + "] and index " + index); } setWorkingForm(newForm); } /** * Return true if the current response contains a form. */ @Override 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. */ @Override public boolean hasForm(String nameOrID) { return getForm(nameOrID) != null; } @Override public boolean hasForm(String nameOrID, int index) { return getForm(nameOrID, index) != null; } @Override 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. */ @Override 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. */ @Override 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. */ @Override 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 + "] was found."); } /** * Set a form hidden element to the provided value. * * @param fieldName name of the hidden input element * @param paramValue parameter value to submit for the element. */ @Override public void setHiddenField(String fieldName, String text) { // first try the current form if (form != null) { for (HtmlElement e : form.getHtmlElementDescendants()) { if (e instanceof HtmlHiddenInput && e.getAttribute("name").equals(fieldName)) { // we found it ((HtmlHiddenInput) e).setValueAttribute(text); return; } } } // not in the current form: try *all* elements HtmlElement outside_element = getHtmlElementWithAttribute("name", fieldName); if (outside_element != null) { if (outside_element instanceof HtmlHiddenInput) { ((HtmlHiddenInput) outside_element).setValueAttribute(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 hidden field with name [" + fieldName + "] was found."); } /** * Return a string array of select box option values. * * @param selectName name of the select box. */ @Override public String[] getSelectOptionValues(String selectName) { HtmlSelect sel = getForm().getSelectByName(selectName); ArrayList<String> result = new ArrayList<>(); for (HtmlOption opt : sel.getOptions()) { result.add(opt.getValueAttribute()); } return result.toArray(new String[result.size()]); } /** * Return a string array of the Nth select box option values. * * @param selectName name of the select box. * @param index the 0-based index when more than one * select with the same name is expected. */ @Override public String[] getSelectOptionValues(String selectName, int index) { List<HtmlSelect> sels = getForm().getSelectsByName(selectName); if (sels == null || sels.size() < index + 1) { throw new RuntimeException("Did not find select with name [" + selectName + "] at index " + index); } HtmlSelect sel = sels.get(index); ArrayList<String> result = new ArrayList<>(); for (HtmlOption opt : sel.getOptions()) { result.add(opt.getValueAttribute()); } return result.toArray(new String[result.size()]); } private String[] getSelectedOptions(HtmlSelect sel) { String[] result = new String[sel.getSelectedOptions().size()]; int i = 0; for (HtmlOption opt : sel.getSelectedOptions()) { result[i++] = opt.getValueAttribute(); } return result; } @Override public String[] getSelectedOptions(String selectName) { HtmlSelect sel = getForm().getSelectByName(selectName); return getSelectedOptions(sel); } @Override public String[] getSelectedOptions(String selectName, int index) { List<HtmlSelect> sels = getForm().getSelectsByName(selectName); if (sels == null || sels.size() < index + 1) { throw new RuntimeException("Did not find select with name [" + selectName + "] at index " + index); } HtmlSelect sel = sels.get(index); return getSelectedOptions(sel); } private String getSelectOptionValueForLabel(HtmlSelect sel, String label) { for (HtmlOption opt : sel.getOptions()) { if (opt.asText().equals(label)) { return opt.getValueAttribute(); } } throw new RuntimeException("Unable to find option " + label + " for " + sel.getNameAttribute()); } @Override public String getSelectOptionValueForLabel(String selectName, String label) { HtmlSelect sel = getForm().getSelectByName(selectName); return getSelectOptionValueForLabel(sel, label); } @Override public String getSelectOptionValueForLabel(String selectName, int index, String label) { List<HtmlSelect> sels = getForm().getSelectsByName(selectName); if (sels == null || sels.size() < index + 1) { throw new RuntimeException("Did not find select with name [" + selectName + "] at index " + index); } HtmlSelect sel = sels.get(index); return getSelectOptionValueForLabel(sel, label); } private String getSelectOptionLabelForValue(HtmlSelect sel, String value) { for (HtmlOption opt : sel.getOptions()) { if (opt.getValueAttribute().equals(value)) { return opt.asText(); } } throw new RuntimeException("Unable to find option " + value + " for " + sel.getNameAttribute()); } @Override public String getSelectOptionLabelForValue(String selectName, String value) { HtmlSelect sel = getForm().getSelectByName(selectName); return getSelectOptionLabelForValue(sel, value); } @Override public String getSelectOptionLabelForValue(String selectName, int index, String value) { List<HtmlSelect> sels = getForm().getSelectsByName(selectName); if (sels == null || sels.size() < index + 1) { throw new RuntimeException("Did not find select with name [" + selectName + "] at index " + index); } HtmlSelect sel = sels.get(index); return getSelectOptionLabelForValue(sel, value); } @Override public URL getPageURL() { return win.getEnclosedPage().getWebResponse().getWebRequest().getUrl(); } @Override public String getPageSource() { return win.getEnclosedPage().getWebResponse().getContentAsString(); } @Override public String getPageTitle() { return getCurrentPageTitle(); } @Override public String getPageText() { Page page = win.getEnclosedPage(); if (page instanceof HtmlPage) { return ((HtmlPage) page).getBody().asText(); } if (page instanceof TextPage) { return ((TextPage) page).getContent(); } if (page instanceof JavaScriptPage) { return ((JavaScriptPage) page).getContent(); } if (page instanceof XmlPage) { return ((XmlPage) page).getTextContent(); } if (page instanceof UnexpectedPage) { return ((UnexpectedPage) page).getWebResponse().getContentAsString(); } throw new RuntimeException("Unexpected error in getPageText(). This method need to be updated."); } @Override public String getServerResponse() { StringBuffer result = new StringBuffer(); WebResponse wr = wc.getCurrentWindow().getEnclosedPage().getWebResponse(); result.append(wr.getStatusCode()).append(" ").append(wr.getStatusMessage()).append("\n"); result.append("Location: ").append(wr.getWebRequest().getUrl()).append("\n"); for (NameValuePair h : wr.getResponseHeaders()) { result.append(h.getName()).append(": ").append(h.getValue()).append("\n"); } result.append("\n"); result.append(wr.getContentAsString()); return result.toString(); } @Override public InputStream getInputStream() { try { return wc.getCurrentWindow().getEnclosedPage().getWebResponse().getContentAsStream(); } catch (IOException e) { throw new RuntimeException(e); } } @Override public InputStream getInputStream(URL resourceUrl) throws TestingEngineResponseException { WebWindow imageWindow = null; try { // as far as I can tell, there is no such thing as an iframe/object kind of "window" in htmlunit, so I'm // opening a fake new window here imageWindow = wc.openWindow(resourceUrl, "for_stream"); Page page = imageWindow.getEnclosedPage(); return page.getWebResponse().getContentAsStream(); } catch (FailingHttpStatusCodeException aException) { throw new TestingEngineResponseException(aException.getStatusCode(), aException); } catch (IOException e) { throw new RuntimeException(e); } finally { if (imageWindow != null) { wc.deregisterWebWindow(imageWindow); } } } /** * Create the {@link WebClient} that will be used for this test. * Subclasses should only override this method if they need to override * the default {@link WebClient}. * * <p>Also see issue 2697234. * * @author Jevon * @return A newly created {@link WebClient} */ protected WebClient createWebClient() { /* * The user agent string is now provided by default to new test cases. * It can still be overridden if testContext.getUserAgent() is not * null (i.e. has been set manually.) * * @author Jevon */ BrowserVersion bv; if (testContext.getUserAgent() != null) { bv = BrowserVersion.FIREFOX_38.clone(); bv.setUserAgent(testContext.getUserAgent()); } else { bv = defaultBrowserVersion; // use default (which includes a full UserAgent string) } if (getTestContext().getProxyHost() != null && getTestContext().getProxyPort() > 0) { // Proxy configuration return new WebClient(bv, getTestContext().getProxyHost(), getTestContext().getProxyPort()); } else { return new WebClient(bv); } } /** * Initialise the web client before accessing a page. */ private void initWebClient() { wc = createWebClient(); wc.getOptions().setJavaScriptEnabled(jsEnabled); wc.getOptions().setThrowExceptionOnFailingStatusCode(!ignoreFailingStatusCodes); wc.getOptions().setThrowExceptionOnScriptError(throwExceptionOnScriptError); wc.getOptions().setRedirectEnabled(true); wc.getOptions().setUseInsecureSSL(true); if (refreshHandler == null) { wc.setRefreshHandler(new ImmediateRefreshHandler()); } else { wc.setRefreshHandler(refreshHandler); } wc.getOptions().setTimeout(timeout); DefaultCredentialsProvider creds = new DefaultCredentialsProvider(); if (getTestContext().hasAuthorization()) { creds.addCredentials(getTestContext().getUser(), getTestContext().getPassword()); } if (getTestContext().hasNTLMAuthorization()) { InetAddress netAddress; String address; try { netAddress = InetAddress.getLocalHost(); address = netAddress.getHostName(); } catch (UnknownHostException e) { address = ""; } creds.addNTLMCredentials(getTestContext().getUser(), getTestContext().getPassword(), "", -1, address, getTestContext().getDomain()); } if (getTestContext().hasProxyAuthorization()) { creds.addCredentials(getTestContext().getProxyUser(), getTestContext().getProxyPasswd(), getTestContext().getProxyHost(), getTestContext().getProxyPort(), AuthScope.ANY_REALM); } wc.setCredentialsProvider(creds); wc.addWebWindowListener(new WebWindowListener() { @Override public void webWindowClosed(WebWindowEvent event) { if (win == null || event.getOldPage().equals(win.getEnclosedPage())) { win = wc.getCurrentWindow(); form = null; } String win = event.getWebWindow().getName(); Page oldPage = event.getOldPage(); String oldPageTitle = "no_html"; if (oldPage instanceof HtmlPage) { oldPageTitle = ((HtmlPage) oldPage).getTitleText(); } logger.debug("Window {} closed : {}", win, oldPageTitle); } @Override public void webWindowContentChanged(WebWindowEvent event) { form = null; String winName = event.getWebWindow().getName(); Page oldPage = event.getOldPage(); Page newPage = event.getNewPage(); String oldPageTitle = "no_html"; if (oldPage instanceof HtmlPage) { oldPageTitle = ((HtmlPage) oldPage).getTitleText(); } String newPageTitle = "no_html"; if (newPage instanceof HtmlPage) { newPageTitle = ((HtmlPage) newPage).getTitleText(); } logger.debug("Window \"{}\" changed : \"{}\" became \"{}", new Object[] { winName, oldPageTitle, newPageTitle }); } @Override public void webWindowOpened(WebWindowEvent event) { String win = event.getWebWindow().getName(); Page newPage = event.getNewPage(); if (newPage instanceof HtmlPage) { logger.debug("Window {} opened : {}", win, ((HtmlPage) newPage).getTitleText()); } else { logger.info("Window {} opened", win); } } }); // Add Javascript Alert Handler wc.setAlertHandler(new AlertHandler() { @Override public void handleAlert(Page page, String msg) { if (expectedJavascriptAlerts.size() < 1) { throw new UnexpectedJavascriptAlertException(msg); } else { JavascriptAlert expected = expectedJavascriptAlerts.remove(0); if (!msg.equals(expected.getMessage())) { throw new UnexpectedJavascriptAlertException(msg); } } } }); // Add Javascript Confirm Handler wc.setConfirmHandler(new ConfirmHandler() { @Override public boolean handleConfirm(Page page, String msg) { if (expectedJavascriptConfirms.size() < 1) { throw new UnexpectedJavascriptConfirmException(msg); } else { JavascriptConfirm expected = expectedJavascriptConfirms.remove(0); if (!msg.equals(expected.getMessage())) { throw new UnexpectedJavascriptConfirmException(msg); } else { return expected.getAction(); } } } }); // Add Javascript Prompt Handler wc.setPromptHandler(new PromptHandler() { @Override public String handlePrompt(Page page, String msg) { if (expectedJavascriptPrompts.size() < 1) { throw new UnexpectedJavascriptPromptException(msg); } else { JavascriptPrompt expected = expectedJavascriptPrompts.remove(0); if (!msg.equals(expected.getMessage())) { throw new UnexpectedJavascriptPromptException(msg); } else { return expected.getInput(); } } } }); // Deal with cookies for (javax.servlet.http.Cookie c : getTestContext().getCookies()) { // If Path==null, cookie is not send to the server. wc.getCookieManager().addCookie(new Cookie(c.getDomain() != null ? c.getDomain() : "", c.getName(), c.getValue(), c.getPath() != null ? c.getPath() : "", c.getMaxAge(), c.getSecure())); } // Deal with custom request header Map<String, String> requestHeaders = getTestContext().getRequestHeaders(); for (Map.Entry<String, String> requestHeader : requestHeaders.entrySet()) { wc.addRequestHeader(requestHeader.getKey(), requestHeader.getValue()); } } /** * Return the window with the given name in the current conversation. * * @param windowName * @throws WebWindowNotFoundException if the window could not be found */ public WebWindow getWindow(String windowName) { return wc.getWebWindowByName(windowName); } /** * Return the currently opened window (issue 2697234). * * @return the currently opened window */ public WebWindow getCurrentWindow() { return win; } /** * Return the current web client (issue 2697234). * * @return the current web client */ public WebClient getWebClient() { return wc; } private HtmlElement getHtmlElement(String anID) { try { return ((HtmlPage) win.getEnclosedPage()).getHtmlElementById(anID); } catch (ElementNotFoundException e) { return null; } } private HtmlElement getHtmlElementByXPath(String xpath) { return getHtmlElementByXPath(getCurrentPage(), xpath); } private HtmlElement getHtmlElementByXPath(DomNode parent, String xpath) { return (HtmlElement) parent.getFirstByXPath(xpath); } /** * Get all the comments in a document, as a list of strings. */ @Override public List<String> getComments() { List<String> comments = new ArrayList<>(); getComments(comments, ((HtmlPage) win.getEnclosedPage())); return comments; } /** * Recursively find comments for all child nodes. * * @param comments * @param node */ private void getComments(List<String> comments, Node node) { NodeList nodes = node.getChildNodes(); for (int i = 0; i < nodes.getLength(); i++) { Node n = nodes.item(i); if (n instanceof DomComment) { comments.add(((DomComment) n).getData().trim()); } // add all child nodes getComments(comments, n); } } /** * Return the first open window with the given title. */ private WebWindow getWindowByTitle(String title) { for (WebWindow window : wc.getWebWindows()) { if (window.getEnclosedPage() instanceof HtmlPage && ((HtmlPage) window.getEnclosedPage()).getTitleText().equals(title)) { return window; } } return null; } /** * Return the page title of the current response page, encoded as specified by the current * {@link net.sourceforge.jwebunit.util.TestContext}. */ public String getCurrentPageTitle() { if (win.getEnclosedPage() instanceof HtmlPage) { return ((HtmlPage) win.getEnclosedPage()).getTitleText(); } return ""; } /** * <p> * Return the current form active for the dialog. * </p> * <p> * The active form can also be explicitly set by {@link #setWorkingForm}. * </p> * <p> * If this method is called without the form having been implicitly or explicitly set, it will attempt to return the * default first form on the page. * </p> * * @exception UnableToSetFormException This runtime assertion failure will be raised if there is no form on the * response. * @return HtmlForm object representing the current active form from the response. */ protected HtmlForm getForm() { if (form == null) { if (hasForm()) { setWorkingForm(getForm(0)); return getForm(0); } else { throw new RuntimeException("No form in current page"); } } else { return form; } } private HtmlForm getForm(int formIndex) { return ((HtmlPage) win.getEnclosedPage()).getForms().get(formIndex); } private HtmlForm getForm(String nameOrID) { try { return (HtmlForm) ((HtmlPage) win.getEnclosedPage()).getHtmlElementById(nameOrID); } catch (ElementNotFoundException e) { } try { return ((HtmlPage) win.getEnclosedPage()).getFormByName(nameOrID); } catch (ElementNotFoundException e) { } return null; } private HtmlForm getForm(String nameOrID, int index) { List<HtmlForm> forms = new ArrayList<>(); for (HtmlForm form : getCurrentPage().getForms()) { if (nameOrID.equals(form.getId()) || nameOrID.equals(form.getNameAttribute())) { forms.add(form); } } if (forms.size() > index) { return forms.get(index); } else { return null; } } private List<HtmlForm> getForms() { HtmlPage page = (HtmlPage) win.getEnclosedPage(); return page.getForms(); } protected HtmlPage getCurrentPage() { Page page = win.getEnclosedPage(); if (page instanceof HtmlPage) { return (HtmlPage) page; } throw new RuntimeException("Non HTML content"); } private void setWorkingForm(HtmlForm newForm) { form = newForm; } private HtmlForm getWorkingForm() { return form; } /** * Does an element with a certain attribute value exist in this page? * * @param attributeName attribute name to search for * @param value value to search for * @return true if the element is found */ private boolean hasHtmlElementWithAttribute(String attributeName, String value) { return getHtmlElementWithAttribute(attributeName, value) != null; } /** * Get an element with a certain attribute value. * * @param attributeName attribute name to search for * @param value value to search for * @return the element found, or null */ private HtmlElement getHtmlElementWithAttribute(String attributeName, String value) { for (HtmlElement e : getCurrentPage().getHtmlElementDescendants()) { if (e.getAttribute(attributeName).equals(value)) { return e; } } return null; } /** * Return true if a form parameter (input element) is present on the current response. * * @param selectName name of the input element to check for */ public boolean hasFormSelectNamed(String selectName) { return hasHtmlElementWithAttribute("name", selectName); } /** * Return the HtmlUnit submit button with a given name. * * @param buttonName name of button. * @return the button */ public HtmlElement getSubmitButton(String buttonName) { List<HtmlElement> btns = new LinkedList<>(); if (form != null) { btns.addAll(getForm().getInputsByName(buttonName)); btns.addAll(getForm().getButtonsByName(buttonName)); } else { for (HtmlForm f : getCurrentPage().getForms()) { btns.addAll(f.getInputsByName(buttonName)); btns.addAll(f.getButtonsByName(buttonName)); } } for (HtmlElement o : btns) { if (o instanceof HtmlSubmitInput) { HtmlSubmitInput btn = (HtmlSubmitInput) o; if (form == null) { form = btn.getEnclosingFormOrDie(); } return btn; } if (o instanceof HtmlImageInput) { HtmlImageInput btn = (HtmlImageInput) o; if (form == null) { form = btn.getEnclosingFormOrDie(); } return btn; } if (o instanceof HtmlButton) { HtmlButton btn = (HtmlButton) o; if (btn.getTypeAttribute().equals("submit")) { if (form == null) { form = btn.getEnclosingFormOrDie(); } return btn; } } } return null; } public HtmlElement getResetButton(String buttonName) { List<HtmlElement> btns = new LinkedList<>(); if (form != null) { btns.addAll(getForm().getInputsByName(buttonName)); btns.addAll(getForm().getButtonsByName(buttonName)); } else { for (HtmlForm f : getCurrentPage().getForms()) { btns.addAll(f.getInputsByName(buttonName)); btns.addAll(f.getButtonsByName(buttonName)); } } for (HtmlElement o : btns) { if (o instanceof HtmlResetInput) { HtmlResetInput btn = (HtmlResetInput) o; if (form == null) { form = btn.getEnclosingFormOrDie(); } return btn; } if (o instanceof HtmlButton) { HtmlButton btn = (HtmlButton) o; if (btn.getTypeAttribute().equals("reset")) { if (form == null) { form = btn.getEnclosingFormOrDie(); } return btn; } } } return null; } /** * Return the HtmlUnit submit button with a given name and value. * * @param buttonName button name. * @param buttonValue button value. * @return HtmlSubmitInput, HtmlImageInput or HtmlButton */ public HtmlElement getSubmitButton(String buttonName, String buttonValue) { List<HtmlElement> btns = new LinkedList<>(); if (form != null) { btns.addAll(getForm().getInputsByName(buttonName)); btns.addAll(getForm().getButtonsByName(buttonName)); } else { for (HtmlForm f : getCurrentPage().getForms()) { btns.addAll(f.getInputsByName(buttonName)); btns.addAll(f.getButtonsByName(buttonName)); } } for (HtmlElement o : btns) { if (o instanceof HtmlSubmitInput) { HtmlSubmitInput btn = (HtmlSubmitInput) o; if (btn.getValueAttribute().equals(buttonValue)) { if (form == null) { form = btn.getEnclosingFormOrDie(); } return btn; } } if (o instanceof HtmlImageInput) { HtmlImageInput btn = (HtmlImageInput) o; if (btn.getValueAttribute().equals(buttonValue)) { if (form == null) { form = btn.getEnclosingFormOrDie(); } return btn; } } if (o instanceof HtmlButton) { HtmlButton btn = (HtmlButton) o; if (btn.getValueAttribute().equals(buttonValue) && btn.getTypeAttribute().equals("submit")) { if (form == null) { form = btn.getEnclosingFormOrDie(); } return btn; } } } return null; } private HtmlElement getSubmitButton() { List<HtmlElement> btns = new LinkedList<>(); if (form != null) { btns.addAll(getForm().getElementsByAttribute("input", "type", "submit")); btns.addAll(getForm().getElementsByAttribute("input", "type", "image")); btns.addAll(getForm().getElementsByAttribute("button", "type", "submit")); } else { for (HtmlForm f : getCurrentPage().getForms()) { btns.addAll(f.getElementsByAttribute("input", "type", "submit")); btns.addAll(f.getElementsByAttribute("input", "type", "image")); btns.addAll(f.getElementsByAttribute("button", "type", "submit")); } } for (HtmlElement o : btns) { if (o instanceof HtmlSubmitInput) { HtmlSubmitInput btn = (HtmlSubmitInput) o; if (form == null) { form = btn.getEnclosingFormOrDie(); } return btn; } if (o instanceof HtmlImageInput) { HtmlImageInput btn = (HtmlImageInput) o; if (form == null) { form = btn.getEnclosingFormOrDie(); } return btn; } if (o instanceof HtmlButton) { HtmlButton btn = (HtmlButton) o; if (btn.getTypeAttribute().equals("submit")) { if (form == null) { form = btn.getEnclosingFormOrDie(); } return btn; } } } return null; } /** * {@inheritDoc} */ @Override public boolean hasSubmitButton() { return getSubmitButton() != null; } /** * {@inheritDoc} */ @Override public boolean hasSubmitButton(String buttonName) { return getSubmitButton(buttonName) != null; } /** * {@inheritDoc} */ @Override public boolean hasSubmitButton(String buttonName, String buttonValue) { try { return getSubmitButton(buttonName, buttonValue) != null; } catch (UnableToSetFormException e) { return false; } } @Override public boolean hasResetButton() { HtmlForm form = getForm(); List<?> l = form.getByXPath("//input[@type='reset']"); List<?> l2 = form.getByXPath("//button[@type='reset']"); return (l.size() > 0 || l2.size() > 0); } @Override public boolean hasResetButton(String buttonName) { return getResetButton(buttonName) != null; } /** * Return the HtmlUnit Button with a given id. * * @param buttonId */ private HtmlElement getButton(String buttonId) { HtmlElement btn = null; try { btn = getCurrentPage().getHtmlElementById(buttonId); if (btn instanceof HtmlButton || btn instanceof HtmlButtonInput || btn instanceof HtmlSubmitInput || btn instanceof HtmlResetInput) { return btn; } } catch (ElementNotFoundException e) { return null; } return null; } /** * Checks whether a button containing the specified text as its label exists. * For HTML input tags of type submit, reset, or button, this checks the * value attribute. For HTML button tags, this checks the element's * content by retrieving the text content. * * <p>This method does not check whether the button is currently visible to the * client. * * @param text the text of the button (between <button></button>) * or the value of the "value" attribute. * @return <code>true</code> when the button with text could be found. */ @Override public boolean hasButtonWithText(String text) { return getButtonWithText(text) != null ? true : false; } /** * Returns the first button that contains the specified text as its label. * For HTML input tags of type submit, reset, or button, this checks the * value attribute. For HTML button tags, this checks the element's * content by retrieving the text content. * * <p>This method does not check whether the button is currently visible to the * client. * * @param buttonValueText the text of the button (between <button></button>) * or the value of the "value" attribute. * @return the ClickableElement with the specified text or null if * no such button is found. */ public HtmlElement getButtonWithText(String buttonValueText) { if (buttonValueText == null) { throw new NullPointerException("Cannot search for button with null text"); } List<? extends HtmlElement> l = ((HtmlPage) win.getEnclosedPage()).getDocumentElement() .getHtmlElementsByTagNames(Arrays.asList(new String[] { "button", "input" })); for (HtmlElement e : l) { if (e instanceof HtmlButton) { // we cannot use asText(), as this returns an empty string if the // button is not currently displayed, resulting in different // behaviour as the <input> Buttons if (buttonValueText.equals(((HtmlButton) e).getTextContent())) { return e; } } else if (e instanceof HtmlButtonInput || e instanceof HtmlSubmitInput || e instanceof HtmlResetInput) { if (buttonValueText.equals(e.getAttribute("value"))) { return e; } } } return null; } /** * Returns if the button identified by <code>buttonId</code> is present. * * @param buttonId the id of the button * @return <code>true</code> when the button was found. */ @Override public boolean hasButton(String buttonId) { try { return getButton(buttonId) != null; } catch (UnableToSetFormException e) { return false; } } @Override public boolean isCheckboxSelected(String checkBoxName) { HtmlCheckBoxInput cb = getCheckbox(checkBoxName); return cb.isChecked(); } @Override public boolean isCheckboxSelected(String checkBoxName, String checkBoxValue) { HtmlCheckBoxInput cb = getCheckbox(checkBoxName, checkBoxValue); return cb.isChecked(); } /** * Return true if given text is present in a specified table of the response. * * @param tableSummaryOrId table summary or id to inspect for expected text. * @param text expected text to check for. */ public boolean isTextInTable(String tableSummaryOrId, String text) { HtmlTable table = getHtmlTable(tableSummaryOrId); if (table == null) { throw new RuntimeException("No table with summary or id [" + tableSummaryOrId + "] found in response."); } for (int row = 0; row < table.getRowCount(); row++) { for (int col = 0; table.getCellAt(row, col) != null; col++) { HtmlTableCell cell = table.getCellAt(row, col); if (cell != null) { String cellHtml = cell.asText(); if (cellHtml.indexOf(text) != -1) { return true; } } } } return false; } @Override public Table getTable(String tableSummaryNameOrId) { HtmlTable table = getHtmlTable(tableSummaryNameOrId); Table result = new Table(); for (int i = 0; i < table.getRowCount(); i++) { Row newRow = new Row(); HtmlTableRow htmlRow = table.getRow(i); CellIterator cellIt = htmlRow.getCellIterator(); while (cellIt.hasNext()) { HtmlTableCell htmlCell = cellIt.nextCell(); newRow.appendCell(new Cell(htmlCell.asText(), htmlCell.getColumnSpan(), htmlCell.getRowSpan())); } result.appendRow(newRow); } return result; } /** * Return the HtmlUnit WebTable object representing a specified table in the current response. Null is returned if a * parsing exception occurs looking for the table or no table with the id or summary could be found. * * @param tableSummaryNameOrId summary or id of the table to return. */ private HtmlTable getHtmlTable(String tableSummaryNameOrId) { try { return (HtmlTable) ((HtmlPage) win.getEnclosedPage()).getHtmlElementById(tableSummaryNameOrId); } catch (ElementNotFoundException e) { // Not found } try { return (HtmlTable) ((HtmlPage) win.getEnclosedPage()).getDocumentElement() .getOneHtmlElementByAttribute("table", "summary", tableSummaryNameOrId); } catch (ElementNotFoundException e) { // Not found } try { return (HtmlTable) ((HtmlPage) win.getEnclosedPage()).getDocumentElement() .getOneHtmlElementByAttribute("table", "name", tableSummaryNameOrId); } catch (ElementNotFoundException e) { // Not found } return null; } @Override public boolean hasTable(String tableSummaryNameOrId) { return getHtmlTable(tableSummaryNameOrId) != null; } /** * Submit the current form with the default submit button. See {@link #getForm}for an explanation of how the * current form is established. */ @Override public void submit() { HtmlElement btn = getSubmitButton(); if (btn == null) { throw new RuntimeException("No submit button found in current form."); } try { btn.click(); } catch (FailingHttpStatusCodeException e) { throw new TestingEngineResponseException(e.getStatusCode(), e); } catch (IOException e) { throw new RuntimeException("HtmlUnit Error submitting form using default submit button, " + "check that form has single submit button, otherwise use submit(name): \n", e); } } /** * Submit the current form with the specified submit button. See {@link #getForm}for an explanation of how the * current form is established. * * @param buttonName name of the button to use for submission. */ @Override public void submit(String buttonName) { HtmlElement btn = getSubmitButton(buttonName); if (btn == null) { throw new RuntimeException("No submit button found in current form."); } try { btn.click(); } catch (FailingHttpStatusCodeException e) { throw new TestingEngineResponseException(e.getStatusCode(), e); } catch (IOException e) { throw new RuntimeException("HtmlUnit Error submitting form using default submit button", e); } } /** * Submit the current form with the specifed submit button (by name and value). See {@link #getForm}for an * explanation of how the current form is established. * * @param buttonName name of the button to use for submission. * @param buttonValue value/label of the button to use for submission */ @Override public void submit(String buttonName, String buttonValue) { List<HtmlElement> l = new LinkedList<>(); l.addAll(getForm().getInputsByName(buttonName)); l.addAll(getForm().getButtonsByName(buttonName)); try { for (int i = 0; i < l.size(); i++) { Object o = l.get(i); if (o instanceof HtmlSubmitInput) { HtmlSubmitInput inpt = (HtmlSubmitInput) o; if (inpt.getValueAttribute().equals(buttonValue)) { inpt.click(); return; } } if (o instanceof HtmlImageInput) { HtmlImageInput inpt = (HtmlImageInput) o; if (inpt.getValueAttribute().equals(buttonValue)) { inpt.click(); return; } } if (o instanceof HtmlButton) { HtmlButton inpt = (HtmlButton) o; if (inpt.getTypeAttribute().equals("submit") && inpt.getValueAttribute().equals(buttonValue)) { inpt.click(); return; } } } } catch (FailingHttpStatusCodeException e) { // entirely possible that it can fail here throw new TestingEngineResponseException(e.getStatusCode(), e); } catch (IOException e) { throw new RuntimeException("HtmlUnit Error submitting form using submit button with name [" + buttonName + "] and value [" + buttonValue + "]", e); } throw new RuntimeException("No submit button found in current form with name [" + buttonName + "] and value [" + buttonValue + "]."); } /** * Reset the current form. See {@link #getForm}for an explanation of how the current form is established. */ @Override public void reset() { getForm().reset(); } /** * Return true if a link is present in the current response containing the specified text (note that HttpUnit uses * contains rather than an exact match - if this is a problem consider using ids on the links to uniquely identify * them). * * @param linkText text to check for in links on the response. * @param index The 0-based index, when more than one link with the same text is expected. */ @Override public boolean hasLinkWithText(String linkText, int index) { return getLinkWithText(linkText, index) != null; } @Override public boolean hasLinkWithExactText(String linkText, int index) { return getLinkWithExactText(linkText, index) != null; } @Override public boolean hasLinkWithImage(String imageFileName, int index) { return getLinkWithImage(imageFileName, index) != null; } /** * Return true if a link is present in the current response with the specified id. * * @param anId link id to check for. */ @Override public boolean hasLink(String anId) { try { ((HtmlPage) win.getEnclosedPage()).getHtmlElementById(anId); } catch (ElementNotFoundException e) { return false; } return true; } @Override public void clickLinkWithText(String linkText, int index) { HtmlAnchor link = getLinkWithText(linkText, index); if (link == null) { throw new RuntimeException("No Link found for \"" + linkText + "\" with index " + index); } try { link.click(); } catch (IOException e) { throw new RuntimeException("Click failed", e); } } @Override public void clickLinkWithExactText(String linkText, int index) { HtmlAnchor link = getLinkWithExactText(linkText, index); if (link == null) { throw new RuntimeException("No Link found for \"" + linkText + "\" with index " + index); } try { link.click(); } catch (IOException e) { throw new RuntimeException("Click failed", e); } } private HtmlCheckBoxInput getCheckbox(String checkBoxName) { Object[] l = getForm().getInputsByName(checkBoxName).toArray(); for (int i = 0; i < l.length; i++) { if (l[i] instanceof HtmlCheckBoxInput) { return (HtmlCheckBoxInput) l[i]; } } throw new RuntimeException("No checkbox with name [" + checkBoxName + "] was found in current form."); } private HtmlCheckBoxInput getCheckbox(String checkBoxName, String value) { Object[] l = getForm().getInputsByName(checkBoxName).toArray(); for (int i = 0; i < l.length; i++) { if ((l[i] instanceof HtmlCheckBoxInput) && ((HtmlCheckBoxInput) l[i]).getValueAttribute().equals(value)) { return (HtmlCheckBoxInput) l[i]; } } throw new RuntimeException("No checkbox with name [" + checkBoxName + "] and value [" + value + "] was found in current form."); } /** * Select a specified checkbox. If the checkbox is already checked then the checkbox will stay checked. * * @param checkBoxName name of checkbox to be deselected. */ @Override public void checkCheckbox(String checkBoxName) { HtmlCheckBoxInput cb = getCheckbox(checkBoxName); if (!cb.isChecked()) { try { cb.click(); } catch (IOException e) { throw new RuntimeException("checkCheckbox failed", e); } } } @Override public void checkCheckbox(String checkBoxName, String value) { HtmlCheckBoxInput cb = getCheckbox(checkBoxName, value); if (!cb.isChecked()) { try { cb.click(); } catch (IOException e) { e.printStackTrace(); throw new RuntimeException("checkCheckbox failed", e); } } } /** * Deselect a specified checkbox. If the checkbox is already unchecked then the checkbox will stay unchecked. * * @param checkBoxName name of checkbox to be deselected. */ @Override public void uncheckCheckbox(String checkBoxName) { HtmlCheckBoxInput cb = getCheckbox(checkBoxName); if (cb.isChecked()) { try { cb.click(); } catch (IOException e) { e.printStackTrace(); throw new RuntimeException("checkCheckbox failed", e); } } } @Override public void uncheckCheckbox(String checkBoxName, String value) { HtmlCheckBoxInput cb = getCheckbox(checkBoxName, value); if (cb.isChecked()) { try { cb.click(); } catch (IOException e) { e.printStackTrace(); throw new RuntimeException("uncheckCheckbox failed", e); } } } private HtmlRadioButtonInput getRadioOption(String radioGroup, String radioOption) { for (HtmlForm form : getForms()) { List<HtmlRadioButtonInput> buttons = form.getRadioButtonsByName(radioGroup); for (HtmlRadioButtonInput button : buttons) { if (button.getValueAttribute().equals(radioOption)) { return button; } } } return null; } /** * Clicks a radio option. Asserts that the radio option exists first. * * * @param radioGroup name of the radio group. * @param radioOption value of the option to check for. */ @Override public void clickRadioOption(String radioGroup, String radioOption) { HtmlRadioButtonInput rb = getRadioOption(radioGroup, radioOption); if (!rb.isChecked()) { try { rb.click(); } catch (IOException e) { e.printStackTrace(); throw new RuntimeException("checkCheckbox failed", e); } } } /** * Navigate by submitting a request based on a link with a given ID. A RuntimeException is thrown if no such link * can be found. * * @param anID id of link to be navigated. */ @Override public void clickLink(String anID) { clickElementByXPath("//a[@id=\"" + anID + "\"]"); } private HtmlAnchor getLinkWithImage(String filename, int index) { return (HtmlAnchor) getHtmlElementByXPath( "(//a[img[contains(@src,\"" + filename + "\")]])[" + (index + 1) + "]"); } private HtmlAnchor getLinkWithText(String linkText, int index) { List<HtmlAnchor> lnks = ((HtmlPage) win.getEnclosedPage()).getAnchors(); int count = 0; for (HtmlAnchor lnk : lnks) { if ((lnk.asText().indexOf(linkText) >= 0) && (count++ == index)) { return lnk; } } return null; } private HtmlAnchor getLinkWithExactText(String linkText, int index) { List<HtmlAnchor> lnks = ((HtmlPage) win.getEnclosedPage()).getAnchors(); int count = 0; for (HtmlAnchor lnk : lnks) { if ((lnk.asText().equals(linkText)) && (count++ == index)) { return lnk; } } return null; } /** * Navigate by submitting a request based on a link with a given image file name. A RuntimeException is thrown if no * such link can be found. * * @param imageFileName A suffix of the image's filename; for example, to match * <tt>"images/my_icon.png"<tt>, you could just pass in * <tt>"my_icon.png"<tt>. */ @Override public void clickLinkWithImage(String imageFileName, int index) { HtmlAnchor link = getLinkWithImage(imageFileName, index); if (link == null) { throw new RuntimeException("No Link found with filename \"" + imageFileName + "\" and index " + index); } try { link.click(); } catch (IOException e) { throw new RuntimeException("Click failed", e); } } @Override public boolean hasElement(String anID) { return getHtmlElement(anID) != null; } @Override public boolean hasElementByXPath(String xpath) { return getHtmlElementByXPath(xpath) != null; } @Override public void clickElementByXPath(String xpath) { HtmlElement e = getHtmlElementByXPath(xpath); if (e == null) { throw new RuntimeException("No element found with xpath \"" + xpath + "\""); } try { e.click(); } catch (IOException exp) { throw new RuntimeException("Click failed", exp); } } @Override public String getElementAttributByXPath(String xpath, String attribut) { HtmlElement e = getHtmlElementByXPath(xpath); if (e == null) { return null; } return e.getAttribute(attribut); } @Override public String getElementTextByXPath(String xpath) { HtmlElement e = getHtmlElementByXPath(xpath); if (e == null) { return null; } return e.asText(); } /** * Click the indicated button (input type=button). * * @param buttonId */ @Override public void clickButton(String buttonId) { HtmlElement btn = getButton(buttonId); try { btn.click(); } catch (Exception e) { throw new RuntimeException(e); } } /** * Clicks the first button that contains the specified text as its label. * For HTML input tags of type submit, reset, or button, this checks the * value attribute. For HTML button tags, this checks the element's * content by converting it to text. or an HTML <button> tag. */ @Override public void clickButtonWithText(String buttonValueText) { HtmlElement b = getButtonWithText(buttonValueText); if (b != null) { try { b.click(); } catch (Exception e) { throw new RuntimeException(e); } } else { throw new RuntimeException("No button found with text: " + buttonValueText); } } /** * Return true if a radio group contains the indicated option. * * @param radioGroup name of the radio group. * @param radioOption value of the option to check for. */ @Override public boolean hasRadioOption(String radioGroup, String radioOption) { return getRadioOption(radioGroup, radioOption) != null; } @Override public String getSelectedRadio(String radioGroup) { List<HtmlRadioButtonInput> radios = getForm().getRadioButtonsByName(radioGroup); for (HtmlRadioButtonInput radio : radios) { if (radio.isChecked()) { return radio.getValueAttribute(); } } throw new RuntimeException("Unexpected state: no radio button was selected in radio group [" + radioGroup + "]. Is it possible in a real browser?"); } /** * Return true if a select box contains the indicated option. * * @param selectName name of the select box. * @param optionLabel label of the option. */ @Override public boolean hasSelectOption(String selectName, String optionLabel) { String[] opts = getSelectOptionValues(selectName); for (int i = 0; i < opts.length; i++) { String label = getSelectOptionLabelForValue(selectName, opts[i]); if (label.equals(optionLabel)) { return true; } } return false; } /** * Return true if a select box contains the indicated option. * * @param selectName name of the select box. * @param optionValue value of the option. */ @Override public boolean hasSelectOptionValue(String selectName, String optionValue) { String[] opts = getSelectOptionValues(selectName); for (int i = 0; i < opts.length; i++) { if (opts[i].equals(optionValue)) { return true; } } return false; } /** * Select the specified set of options in the select element * with the provided name. * @param selectName name of the select box * @param options set of options to select. */ @Override public void selectOptions(String selectName, String[] options) { HtmlSelect sel = getForm().getSelectByName(selectName); if (!sel.isMultipleSelectEnabled() && options.length > 1) { throw new RuntimeException("Multiselect not enabled"); } for (String option : options) { boolean found = false; for (HtmlOption opt : sel.getOptions()) { if (opt.getValueAttribute().equals(option)) { sel.setSelectedAttribute(opt, true); found = true; break; } } if (!found) { throw new RuntimeException("Option " + option + " not found"); } } } /** * Return true if the Nth select box contains the indicated option. * * @param selectName name of the select box. * @param index the 0-based index of the select element when multiple * select elements are expected. * @param optionLabel label of the option. */ @Override public boolean hasSelectOption(String selectName, int index, String optionLabel) { String[] opts = getSelectOptionValues(selectName, index); for (int i = 0; i < opts.length; i++) { String label = getSelectOptionLabelForValue(selectName, index, opts[i]); if (label.equals(optionLabel)) { return true; } } return false; } /** * Return true if the Nth select box contains the indicated option. * * @param selectName name of the select box. * @param index the 0-based index of the select element when multiple * select elements are expected. * @param optionValue value of the option. */ @Override public boolean hasSelectOptionValue(String selectName, int index, String optionValue) { String[] opts = getSelectOptionValues(selectName, index); for (int i = 0; i < opts.length; i++) { if (opts[i].equals(optionValue)) { return true; } } return false; } /** * Select the specified set of options in the select element * with the provided name. * @param selectName name of the select box * @param index the 0-based index of the select element when multiple * select elements are expected. * @param options set of options to select. */ @Override public void selectOptions(String selectName, int index, String[] options) { List<HtmlSelect> sels = getForm().getSelectsByName(selectName); if (sels == null || sels.size() < index + 1) { throw new RuntimeException("Did not find select with name [" + selectName + "] at index " + index); } HtmlSelect sel = sels.get(index); if (!sel.isMultipleSelectEnabled() && options.length > 1) { throw new RuntimeException("Multiselect not enabled"); } for (String option : options) { boolean found = false; for (HtmlOption opt : sel.getOptions()) { if (opt.getValueAttribute().equals(option)) { sel.setSelectedAttribute(opt, true); found = true; break; } } if (!found) { throw new RuntimeException("Option " + option + " not found"); } } } @Override public void unselectOptions(String selectName, String[] options) { HtmlSelect sel = getForm().getSelectByName(selectName); if (!sel.isMultipleSelectEnabled() && options.length > 1) { throw new RuntimeException("Multiselect not enabled"); } for (String option : options) { boolean found = false; for (HtmlOption opt : sel.getOptions()) { if (opt.asText().equals(option)) { sel.setSelectedAttribute(opt, false); found = true; break; } } if (!found) { throw new RuntimeException("Option " + option + " not found"); } } } @Override public void unselectOptions(String selectName, int index, String[] options) { List<HtmlSelect> sels = getForm().getSelectsByName(selectName); if (sels == null || sels.size() < index + 1) { throw new RuntimeException("Did not find select with name [" + selectName + "] at index " + index); } HtmlSelect sel = sels.get(index); if (!sel.isMultipleSelectEnabled() && options.length > 1) { throw new RuntimeException("Multiselect not enabled"); } for (String option : options) { boolean found = false; for (HtmlOption opt : sel.getOptions()) { if (opt.asText().equals(option)) { sel.setSelectedAttribute(opt, false); found = true; break; } } if (!found) { throw new RuntimeException("Option " + option + " not found"); } } } @Override public boolean isTextInElement(String elementID, String text) { return isTextInElement(getHtmlElement(elementID), text); } /** * Return true if a given string is contained within the specified element. * * @param element org.w3c.com.Element to inspect. * @param text text to check for. */ private boolean isTextInElement(HtmlElement element, String text) { return element.asText().indexOf(text) >= 0; } @Override public boolean isMatchInElement(String elementID, String regexp) { return isMatchInElement(getHtmlElement(elementID), regexp); } /** * Return true if a given regexp is contained within the specified element. * * @param element org.w3c.com.Element to inspect. * @param regexp regexp to match. */ private boolean isMatchInElement(HtmlElement element, String regexp) { RE re = getRE(regexp); return re.match(element.asText()); } private RE getRE(String regexp) { try { return new RE(regexp, RE.MATCH_SINGLELINE); } catch (RESyntaxException e) { throw new RuntimeException(e); } } /** * {@inheritDoc} */ @Override public void gotoRootWindow() { win = win.getTopWindow(); } private void setMainWindow(WebWindow win) { wc.setCurrentWindow(win); this.win = win; } /** * Return the given frame in the current conversation. * * @param frameNameOrId Frame name or ID. * @return The frame found or null. */ private WebWindow getFrame(String frameNameOrId) { // First try ID for (FrameWindow frame : getCurrentPage().getFrames()) { if (frameNameOrId.equals(frame.getFrameElement().getId())) { return frame; } } // Now try with Name for (FrameWindow frame : getCurrentPage().getFrames()) { if (frameNameOrId.equals(frame.getName())) { return frame; } } // Nothing was found. return null; } /** * @param testContext The testContext to set. */ private void setTestContext(TestContext testContext) { this.testContext = testContext; } /** * @return Returns the testContext. */ protected TestContext getTestContext() { return testContext; } @Override public void setExpectedJavaScriptAlert(JavascriptAlert[] alerts) throws ExpectedJavascriptAlertException { if (this.expectedJavascriptAlerts.size() > 0) { throw new ExpectedJavascriptAlertException((expectedJavascriptAlerts.get(0)).getMessage()); } for (int i = 0; i < alerts.length; i++) { expectedJavascriptAlerts.add(alerts[i]); } } @Override public void setExpectedJavaScriptConfirm(JavascriptConfirm[] confirms) throws ExpectedJavascriptConfirmException { if (this.expectedJavascriptConfirms.size() > 0) { throw new ExpectedJavascriptConfirmException((expectedJavascriptConfirms.get(0)).getMessage()); } for (int i = confirms.length - 1; i >= 0; i--) { expectedJavascriptConfirms.add(confirms[i]); } } @Override public void setExpectedJavaScriptPrompt(JavascriptPrompt[] prompts) throws ExpectedJavascriptPromptException { if (this.expectedJavascriptPrompts.size() > 0) { throw new ExpectedJavascriptPromptException((expectedJavascriptPrompts.get(0)).getMessage()); } for (int i = prompts.length - 1; i >= 0; i--) { expectedJavascriptPrompts.add(prompts[i]); } } /* * (non-Javadoc) * * @see net.sourceforge.jwebunit.api.ITestingEngine#getElementByXPath(java.lang.String) */ @Override public IElement getElementByXPath(String xpath) { HtmlElement element = this.getHtmlElementByXPath(xpath); if (element != null) { return new HtmlUnitElementImpl(element); } return null; } /* * (non-Javadoc) * * @see net.sourceforge.jwebunit.api.ITestingEngine#getElementByID(java.lang.String) */ @Override public IElement getElementByID(String id) { HtmlElement element = this.getHtmlElement(id); if (element != null) { return new HtmlUnitElementImpl(element); } return null; } /* * (non-Javadoc) * * @see net.sourceforge.jwebunit.api.ITestingEngine#getElementsByXPath(java.lang.String) */ @Override public List<IElement> getElementsByXPath(String xpath) { List<IElement> children = new ArrayList<>(); for (Object child : getCurrentPage().getByXPath(xpath)) { if (child instanceof HtmlElement) { children.add(new HtmlUnitElementImpl((HtmlElement) child)); } } return children; } /* * (non-Javadoc) * * @see net.sourceforge.jwebunit.api.ITestingEngine#getServerResponseCode() */ @Override public int getServerResponseCode() { return getWebResponse().getStatusCode(); } /** * Get the last WebResponse from HtmlUnit. */ public WebResponse getWebResponse() { return wc.getCurrentWindow().getEnclosedPage().getWebResponse(); } /* * @param ignoreFailingStatusCodes the ignoreFailingStatusCodes to set */ @Override public void setIgnoreFailingStatusCodes(boolean ignore) { ignoreFailingStatusCodes = ignore; if (wc != null) { wc.getOptions().setThrowExceptionOnFailingStatusCode(!ignore); } } /* * (non-Javadoc) * * @see net.sourceforge.jwebunit.api.ITestingEngine#getHeader(java.lang.String) */ @Override public String getHeader(String name) { return getWebResponse().getResponseHeaderValue(name); } /* * (non-Javadoc) * * @see net.sourceforge.jwebunit.api.ITestingEngine#getAllHeaders() */ @Override @Deprecated public Map<String, String> getAllHeaders() { Map<String, String> map = new java.util.HashMap<>(); for (NameValuePair header : getWebResponse().getResponseHeaders()) { map.put(header.getName(), header.getValue()); } return map; } @Override public List<HttpHeader> getResponseHeaders() { List<HttpHeader> result = new LinkedList<>(); for (NameValuePair header : getWebResponse().getResponseHeaders()) { result.add(new HttpHeader(header.getName(), header.getValue())); } return result; } /** * An alternative to setting the {@link TestContext#setUserAgent(String) user agent string manually} * is to provide it with all the information for a complete browser version. * * @see com.gargoylesoftware.htmlunit.BrowserVersion * @return The default browser version */ public BrowserVersion getDefaultBrowserVersion() { return defaultBrowserVersion; } /** * An alternative to setting the {@link TestContext#setUserAgent(String) user agent string manually} * is to provide it with all the information for a complete browser version. * * @see com.gargoylesoftware.htmlunit.BrowserVersion * @param the browser version to set as default for this engine instance */ public void setDefaultBrowserVersion(BrowserVersion defaultBrowserVersion) { this.defaultBrowserVersion = defaultBrowserVersion; } @Override public void setTimeout(int milliseconds) { if (wc != null && wc.getWebConnection() != null) { throw new IllegalArgumentException( "Cannot set the timeout when the WebConnection has already been created."); } timeout = milliseconds; } public void setRefreshHandler(RefreshHandler handler) { this.refreshHandler = handler; if (wc != null) { wc.setRefreshHandler(refreshHandler); } } }