package br.com.caelum.seleniumdsl.htmlunit; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Scanner; import net.sourceforge.htmlunit.corejs.javascript.NativeArray; import org.apache.commons.lang.NotImplementedException; import org.apache.log4j.Logger; import br.com.caelum.seleniumdsl.ContentTag; import br.com.caelum.seleniumdsl.Form; import br.com.caelum.seleniumdsl.Page; import br.com.caelum.seleniumdsl.js.Array; import br.com.caelum.seleniumdsl.table.Table; import com.gargoylesoftware.htmlunit.ElementNotFoundException; import com.gargoylesoftware.htmlunit.ScriptException; import com.gargoylesoftware.htmlunit.ScriptResult; import com.gargoylesoftware.htmlunit.UnexpectedPage; import com.gargoylesoftware.htmlunit.html.HtmlAnchor; import com.gargoylesoftware.htmlunit.html.HtmlElement; import com.gargoylesoftware.htmlunit.html.HtmlForm; import com.gargoylesoftware.htmlunit.html.HtmlPage; import com.gargoylesoftware.htmlunit.html.HtmlTable; class HtmlUnitPage implements Page { private static final Logger logger = Logger.getLogger(HtmlUnitPage.class); private static final int SLEEP_TIME = 10; private HtmlPage page; private HtmlPage lastPage; public HtmlUnitPage(HtmlPage page) { setPage(page); lastPage = page; } public Array array(String name) { return new HtmlUnitArray(page.getWebClient(), name); } public Page click(String element) { HtmlElement clickable; if (element.startsWith("link=")) { if (logger.isDebugEnabled()) { logger.debug("Element " + element + " is a link"); } return clickLink(element.replace("link=", "")); } else if (element.startsWith("//")){ if (logger.isDebugEnabled()) { logger.debug("Element " + element + " is a xpath"); } clickable = page.getFirstByXPath(element); } else { if (logger.isDebugEnabled()) { logger.debug("Finding element " + element + " by id or name."); } final List<HtmlElement> elements = page.getElementsByIdAndOrName(element); if (elements.isEmpty()) { throw new ElementNotFoundException("*", "id|name", element); } clickable = elements.get(0); } try { this.page = clickable.click(); } catch (final IOException e) { throw new IllegalStateException(e); } return this; } public Page clickLink(String text) { try { this.page = getFirstAnchorByText(text).click(); } catch (final IOException e) { throw new IllegalArgumentException(e); } return this; } public Page navigate(String element) { if (logger.isDebugEnabled()) { logger.debug("Navigating element " + element); } lastPage = page; click(element); waitForPageToChange(5000); return this; } void waitForPageToChange(long timeout) { if (logger.isDebugEnabled()) { logger.debug("Waiting page to change for: " + timeout + "ms"); } long tries = timeout / SLEEP_TIME; while (lastPage.equals(page)) { final com.gargoylesoftware.htmlunit.Page newPage = page.getWebClient().getCurrentWindow().getEnclosedPage(); if (newPage instanceof UnexpectedPage) { final UnexpectedPage unexpected = (UnexpectedPage) newPage; try { final String error = new Scanner(unexpected.getInputStream()).useDelimiter("$$").next(); throw new IllegalStateException("Unexpected page: \n" + error); } catch (final IOException e) { throw new IllegalStateException(e); } } page = (HtmlPage) newPage; sleep(); tries--; if (tries == 0) { throw new IllegalStateException("Page was not loaded"); } } waitForLoad(); } public HtmlUnitPage mouseDown(String element) { if (logger.isDebugEnabled()) { logger.debug("Mouse down on " + element); } final HtmlElement div = page.getElementsByIdAndOrName(element).get(0); setPage((HtmlPage) div.mouseDown()); return this; } public HtmlUnitPage mouseUp(String element) { if (logger.isDebugEnabled()) { logger.debug("Mouse up on " + element); } final HtmlElement div = page.getElementsByIdAndOrName(element).get(0); setPage((HtmlPage) div.mouseUp()); return this; } public HtmlUnitPage dragAndDrop(String fromElement, String toElement) { mouseDown(fromElement); setPage((HtmlPage) page.getElementsByIdAndOrName(toElement).get(0).mouseMove()); mouseUp(toElement); return this; } public Page navigateLink(String text) { return navigate("link=" + text); } public Page doubleClick(String element) { if (logger.isDebugEnabled()) { logger.debug("Double clicking " + element); } final HtmlElement link = page.getHtmlElementById(element); try { this.page = link.dblClick(); } catch (final IOException e) { throw new IllegalStateException(e); } return this; } public ContentTag div(String id) { return new HtmlUnitContentTag(page, id); } public Form form(String id) { for (final HtmlForm form : page.getForms()) { if (Arrays.asList("", form.getNameAttribute(), form.getId()).contains(id)) { return new HtmlUnitForm(this, new HtmlFormWrapper(form)); } } if (logger.isDebugEnabled()) { final List<String> forms = new ArrayList<String>(); for (final HtmlForm form : page.getForms()) { forms.add(form.toString()); } logger.debug("No forms found with id: " + id + ". Forms found: \n" + forms); } throw new ElementNotFoundException("form", "id|nome", id); } void setPage(HtmlPage page) { if (logger.isDebugEnabled()) { logger.debug("Change page " + this.page + " to " + page); } this.lastPage = this.page; this.page = page; waitForLoad(); } HtmlPage getPage() { return page; } public boolean hasLink(String link) { try { getFirstAnchorByText(link); return true; } catch (final ElementNotFoundException e) { return false; } } public String invoke(String cmd) { if (logger.isDebugEnabled()) { logger.debug("Invoking javascript: " + cmd); } final ScriptResult result = page.executeJavaScript(cmd); if (result.getJavaScriptResult() instanceof NativeArray) { if (logger.isDebugEnabled()) { logger.debug("Result is a native array"); } final NativeArray array = (NativeArray) result.getJavaScriptResult(); return array.get(0, null).toString(); } return result.getJavaScriptResult().toString(); } private HtmlAnchor getFirstAnchorByText(String text) { final List<HtmlAnchor> anchors = (List<HtmlAnchor>) page.getByXPath("//a"); for (final HtmlAnchor anchor : anchors) { if (text.trim().equals(anchor.asXml().replaceAll("<.*?>", "").trim())) { return anchor; } } if (logger.isDebugEnabled()) { final List<String> list = new ArrayList<String>(); for (final HtmlAnchor anchor : anchors) { list.add(anchor.asXml().replaceAll("<.*?>", "").trim()); } logger.debug("No link found with text: " + text + ". Links found: \n" + list); } throw new ElementNotFoundException("a", "text", text); } private void waitForLoad() { while (page.isBeingParsed()) { sleep(); } } private void sleep() { try { Thread.sleep(SLEEP_TIME); } catch (final InterruptedException e) { throw new RuntimeException(e); } } public Page refresh() { try { setPage((HtmlPage) page.refresh()); } catch (final IOException e) { throw new IllegalStateException(e); } return this; } public void screenshot(String filename) { throw new NotImplementedException(); } public ContentTag span(String id) { return div(id); } public Table table(String id) { final List<HtmlElement> elements = page.getElementsByIdAndOrName(id); for (final HtmlElement htmlElement : elements) { if (htmlElement instanceof HtmlTable) { return new HtmlUnitTable((HtmlTable) htmlElement); } } if (logger.isDebugEnabled()) { final List<String> list = new ArrayList<String>(); for (final HtmlElement element : elements) { if (element instanceof HtmlTable) { list.add(element.toString()); } } logger.debug("Table with name or id " + id + " not found. Found tables: \n" + list); } return new InexistantTable(id); } public String title() { return page.getTitleText(); } public Page waitUntil(String condition, long timeout) { if (logger.isDebugEnabled()) { logger.debug("Waiting for condition " + condition); } final long start = System.currentTimeMillis(); while (System.currentTimeMillis() - start < timeout) { ScriptResult result; try { result = page.executeJavaScript(condition); } catch (final ScriptException e) { throw new IllegalStateException(page.toString(), e); } if (!ScriptResult.isFalse(result)) { this.page = (HtmlPage) result.getNewPage(); return this; } sleep(); } throw new IllegalStateException("Condition " + condition + " doesn't hold"); } public String htmlSource() { return page.asXml(); } }