[embjopr-commits] EMBJOPR SVN: r207 - in trunk/jsfunit/src/test/java/org/jboss/jopr/jsfunit: as5 and 1 other directories.

embjopr-commits at lists.jboss.org embjopr-commits at lists.jboss.org
Tue Mar 10 20:46:34 EDT 2009


Author: ozizka at redhat.com
Date: 2009-03-10 20:46:33 -0400 (Tue, 10 Mar 2009)
New Revision: 207

Added:
   trunk/jsfunit/src/test/java/org/jboss/jopr/jsfunit/util/EmbJoprTestToolkit.java
Modified:
   trunk/jsfunit/src/test/java/org/jboss/jopr/jsfunit/ApplicationTestBaseAS5.java
   trunk/jsfunit/src/test/java/org/jboss/jopr/jsfunit/EmbjoprTestCase.java
   trunk/jsfunit/src/test/java/org/jboss/jopr/jsfunit/as5/ApplicationsPageTest.java
Log:
Moved EmbJoprTestToolkit to standalone class and added an instance of it to EmbJoprTestCase.java

Modified: trunk/jsfunit/src/test/java/org/jboss/jopr/jsfunit/ApplicationTestBaseAS5.java
===================================================================
--- trunk/jsfunit/src/test/java/org/jboss/jopr/jsfunit/ApplicationTestBaseAS5.java	2009-03-11 00:35:30 UTC (rev 206)
+++ trunk/jsfunit/src/test/java/org/jboss/jopr/jsfunit/ApplicationTestBaseAS5.java	2009-03-11 00:46:33 UTC (rev 207)
@@ -21,22 +21,14 @@
  */
 package org.jboss.jopr.jsfunit;
 
-import com.gargoylesoftware.htmlunit.ElementNotFoundException;
-import com.gargoylesoftware.htmlunit.Page;
-import java.util.List;
-import java.util.Set;
+import java.io.IOException;
+import java.net.URL;
+import java.util.*;
 import javax.management.*;
+import com.gargoylesoftware.htmlunit.*;
+import com.gargoylesoftware.htmlunit.html.*;
 import org.jboss.mx.util.MBeanServerLocator;
-
 import org.jboss.jopr.jsfunit.exceptions.*;
-import com.gargoylesoftware.htmlunit.html.*;
-import java.io.IOException;
-import java.net.URL;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Properties;
 import org.apache.commons.lang.StringUtils;
 import org.apache.commons.lang.math.NumberUtils;
 import org.w3c.dom.Element;
@@ -1006,40 +998,6 @@
 	}
 
 
-
-	// TODO
-	protected class JMXDeploymentInfo {
-
-		// Etc...
-		public boolean isDeployed(){
-			return false;
-		}
-
-	}
-
-	// TODO
-	protected class FileDeploymentInfo {
-
-		// Etc...
-		public boolean isDeployed(){
-			return false;
-		}
-
-	}
-
-	// TODO
-	protected class EmbJoprDeploymentInfo {
-
-		// Etc...
-		public boolean isDeployed(){
-			return false;
-		}
-
-	}
-
-
-
-
 }// ApplicationTestBaseAS5 
 
 

Modified: trunk/jsfunit/src/test/java/org/jboss/jopr/jsfunit/EmbjoprTestCase.java
===================================================================
--- trunk/jsfunit/src/test/java/org/jboss/jopr/jsfunit/EmbjoprTestCase.java	2009-03-11 00:35:30 UTC (rev 206)
+++ trunk/jsfunit/src/test/java/org/jboss/jopr/jsfunit/EmbjoprTestCase.java	2009-03-11 00:46:33 UTC (rev 207)
@@ -22,7 +22,6 @@
 
 package org.jboss.jopr.jsfunit;
 
-import org.jboss.jopr.jsfunit.exceptions.EmbJoprTestException;
 import com.gargoylesoftware.htmlunit.BrowserVersion;
 import com.gargoylesoftware.htmlunit.html.*;
 import java.io.IOException;
@@ -38,19 +37,15 @@
 import java.util.regex.Pattern;
 import javax.xml.xpath.XPathException;
 import org.jboss.profileservice.spi.ProfileService;
-import org.jboss.profileservice.spi.ProfileKey;
+import org.jboss.profileservice.spi.NoSuchDeploymentException;
 import javax.naming.InitialContext;
-import org.jboss.deployers.spi.management.ManagementView;
 import javax.naming.NamingException;
-import org.jboss.managed.api.ComponentType;
-import org.jboss.managed.api.ManagedProperty;
+import org.jboss.deployers.spi.management.ManagementView;
+import org.jboss.managed.api.*;
 import org.jboss.metatype.api.values.MetaValue;
-import org.jboss.managed.api.ManagedComponent;
-import org.jboss.managed.api.ManagedDeployment;
-import org.jboss.profileservice.spi.NoSuchDeploymentException;
 import org.jboss.metatype.api.values.SimpleValueSupport;
 import org.jboss.jopr.jsfunit.exceptions.EmbJoprTestException;
-import java.io.File;
+import org.jboss.jopr.jsfunit.util.EmbJoprTestToolkit;
 
 
 /**
@@ -67,6 +62,8 @@
 
     protected final Logger log = Logger.getLogger(this.getClass().getName());
 
+		protected EmbJoprTestToolkit ejtt;
+
     protected boolean isJBoss4;
 
     protected JSFClientSession client;
@@ -109,6 +106,7 @@
 								         .getImplementationVersion();
 				log.info("Detected AS version: " + version);
         isJBoss4 = version.startsWith("4");
+				
         
         // Initial JSF request
         WebClientSpec wcSpec = new WebClientSpec("/", BrowserVersion.FIREFOX_3);
@@ -125,6 +123,7 @@
         this.client = jsfSession.getJSFClientSession();
         this.server = jsfSession.getJSFServerSession(); 
 
+				this.ejtt = new EmbJoprTestToolkit(client, server);
     }
 
 		@Override

Modified: trunk/jsfunit/src/test/java/org/jboss/jopr/jsfunit/as5/ApplicationsPageTest.java
===================================================================
--- trunk/jsfunit/src/test/java/org/jboss/jopr/jsfunit/as5/ApplicationsPageTest.java	2009-03-11 00:35:30 UTC (rev 206)
+++ trunk/jsfunit/src/test/java/org/jboss/jopr/jsfunit/as5/ApplicationsPageTest.java	2009-03-11 00:46:33 UTC (rev 207)
@@ -23,21 +23,15 @@
 package org.jboss.jopr.jsfunit.as5;
 
 import java.io.IOException;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
+import java.util.*;
 import org.apache.commons.lang.StringUtils;
 import org.jboss.jopr.jsfunit.AppConstants.DeployableTypes;
 import org.jboss.jopr.jsfunit.ApplicationTestBaseAS5;
-import org.jboss.jopr.jsfunit.DebugUtils;
 import org.jboss.jopr.jsfunit.exceptions.EmbJoprTestException;
 
 
 
 
-
-
 /**
  * This class contains tests for testing the EmbJopr Application
  * Management Functions for Enterprise Application archives with JBoss AS 5.
@@ -56,8 +50,8 @@
 
 	public void testNavigationToApplications() throws EmbJoprTestException, IOException {
 
-		//DebugUtils.writeFile("target/appNav.html", client.getPageAsText());
-
+		// Wait until the tree is loaded via AJAX.
+		// TODO: navTree.waitForNavTreeLoaded();
 		sleep(2000);
 
 		// Navigate to apps summary tab
@@ -78,7 +72,7 @@
 	/**
 	 * Test pagination in Applications page.
 	 * 
-	 * FAILS HERE because of EMBJOPR-61.
+	 * FAILS because of EMBJOPR-61.
 	 * Bad number of listed rows at page 2. expected:<5> but was:<0>
 	 */
 	public void testAppsPagination() throws EmbJoprTestException, IOException {

Added: trunk/jsfunit/src/test/java/org/jboss/jopr/jsfunit/util/EmbJoprTestToolkit.java
===================================================================
--- trunk/jsfunit/src/test/java/org/jboss/jopr/jsfunit/util/EmbJoprTestToolkit.java	                        (rev 0)
+++ trunk/jsfunit/src/test/java/org/jboss/jopr/jsfunit/util/EmbJoprTestToolkit.java	2009-03-11 00:46:33 UTC (rev 207)
@@ -0,0 +1,949 @@
+
+package org.jboss.jopr.jsfunit.util;
+
+
+import com.gargoylesoftware.htmlunit.ElementNotFoundException;
+import com.gargoylesoftware.htmlunit.Page;
+import java.util.*;
+
+import org.jboss.jopr.jsfunit.exceptions.*;
+import com.gargoylesoftware.htmlunit.html.*;
+import java.io.IOException;
+import java.net.URL;
+import org.apache.commons.lang.StringUtils;
+import org.apache.commons.lang.math.NumberUtils;
+import org.jboss.jsfunit.jsfsession.*;
+import org.w3c.dom.Element;
+
+
+
+/**
+ * Embedded Jopr Test Toolkit - helps with testing EmbJopr's web UI
+ * by creating object model of the UI.
+ *
+ * @author Ondrej Zizka
+ */
+public class EmbJoprTestToolkit {
+
+	protected JSFClientSession client;
+	protected JSFServerSession server;
+
+	public JSFClientSession getClient() { return client; }
+	public JSFServerSession getServer() { return server; }
+
+	
+	public EmbJoprTestToolkit(JSFClientSession client, JSFServerSession server) {
+		this.client = client;
+		this.server = server;
+	}
+
+
+
+
+
+	/**
+	 * Single nav tree instance.
+	 *
+	 * Using instance instead of static methods is necessary
+	 * if we want to subclass this tools for AS 4 tests in the future.
+	 */
+	protected final NavTree navTree = new NavTree();
+
+	/**
+	 * Single tab menu instance.
+	 *
+	 * Using instance instead of static methods is necessary
+	 * if we want to subclass this tools for AS 4 tests in the future.
+	 */
+	protected final TabMenu tabMenu = new TabMenu();
+
+
+
+
+
+
+	/**
+	 * Inner class to encapsulate navigation tree operations.
+	 *
+	 * Note that this class does not hold any element reference,
+	 * because the reference becomes invalid with every navigation action.
+	 *
+	 * Instead, all these methods call client methods.
+	 */
+	protected class NavTree {
+
+		public static final String ID_NAV_TREE_FORM = "navTreeForm";
+
+		private void clickRootNode() throws IOException, EmbJoprTestException {
+			DomElement element = (DomElement)client.getElement(ID_NAV_TREE_FORM);
+			if( null == element )
+				throw new HtmlElementNotFoundException("Can't find #"+ID_NAV_TREE_FORM+".");
+
+			// ID changes upon core build?
+			//HtmlAnchor rootNodeLink = element.getFirstByXPath(".//a[@id='navTreeForm:navTree:2::homeLink']");
+
+			// No XSLT 2.0 support :(
+			// javax.xml.transform.TransformerException: Can't find function: matches
+			//HtmlAnchor rootNodeLink = element.getFirstByXPath(
+			//				".//a[matches(@id,'^navTreeForm:navTree:.+::homeLink$')]");
+
+			String xPath = ".//a[ starts-with( @id, '"+ID_NAV_TREE_FORM+":navTree:' ) " +
+							           " and contains( @id, '::homeLink' ) ]"; // XPath 2.0: ends-with( @id, '::homeLink' )
+			HtmlAnchor rootNodeLink = element.getFirstByXPath(xPath);
+
+			if( null == rootNodeLink )
+				throw new HtmlElementNotFoundException("Root node not found using XPath: "+xPath);
+
+			rootNodeLink.click();
+		}
+
+
+		public void waitForNavTreeLoaded(){
+			sleep(1500);///
+			// TODO: Use ActiveConditionChecker
+
+		}
+
+
+		/**
+		 * Finds the nav tree node by it's link label.
+		 *
+		 * @param label
+		 * @return
+		 * @throws org.jboss.jopr.jsfunit.exceptions.HtmlElementNotFoundException
+		 *         when there's no node with given label.
+		 */
+		public NavTreeNode getNodeByLabel( String label ) throws HtmlElementNotFoundException{
+
+			DomElement navTreeForm = (DomElement)client.getElement(ID_NAV_TREE_FORM);
+
+			// A table which has an anchor containing given text.
+			// Note: Different node types have different id endings - typeSummaryLink, categorySummaryLink, ...
+			//String xPath = ".//table[.//td[contains(@id,':text')]//a[contains(@id,'SummaryLink') and normalize-space() = '"+label+"']]";
+			// Node has @id = navTreeForm:navTree:136:137:138:139:143:195::instanceNodeOrLeafLink
+			// Let's rely on the ":text" id part only and suppose there will be just one link in that TD.
+			String xPath = ".//table[.//td[contains(@id,':text')]//a[normalize-space() = '"+label+"']]";
+
+			HtmlTable nodeTable = navTreeForm.getFirstByXPath( xPath );
+
+			if( null == nodeTable )
+				throw new HtmlElementNotFoundException("Node '"+label+"' not found using XPath: "+xPath);
+
+			return new NavTreeNode(nodeTable);
+
+		}
+
+
+    /**
+     * Need a standard JSFUnit API to replace this code.
+		 * @see getNodeByLabel().getLabelLink()
+     */
+    public HtmlAnchor getNodeLink(String linkLabel) throws HtmlElementNotFoundException
+    {
+				return getLinkInsideForm(ID_NAV_TREE_FORM, linkLabel);
+    }
+
+
+    /**
+     * Finds the arrow icon in the nav tree that corresponds to the resource
+     * given by resourceName. This method is used to expand tree nodes
+     * (eg. "Web Applications (WAR)", "Datasources", etc.) in the
+     * navigation tree.
+		 * @see getNodeByLabel().getArrowLink()
+     */
+    public ClickableElement getNodeArrow(String resourceName) throws HtmlElementNotFoundException
+		{
+        HtmlAnchor link = getNodeLink(resourceName);
+        String id = link.getIdAttribute();
+
+        // An example id is: "navTreeForm:navTree:81:82:83:84::typeSummaryLink"
+        // The icon's id would be: "81:82:83:84::typeSummary:handle:img:collapsed"
+        int index = id.lastIndexOf("Link");
+        id = id.substring(0, index) + ":handle";
+        return (ClickableElement)client.getElement(id);
+    }
+
+	}// class NavTree
+
+
+
+	/**
+	 * Represents nav tree node.
+	 * Contains convenience methods to work with the node.
+	 */
+	protected class NavTreeNode {
+
+		/** Keeps the table element of this node. */
+		private HtmlElement elem;
+		public HtmlElement getElement() {			return this.elem;		}
+
+		private NavTreeNode( HtmlTable nodeTable ){
+			this.elem = nodeTable;
+		}
+
+		/** Returns true if this node is expanded. */
+		public boolean isExpanded() throws EmbJoprTestException
+		{
+			HtmlAnchor arrowLink = this.getArrowLink();
+			HtmlElement img = (HtmlElement)arrowLink.getFirstByXPath("img[@style='display: none;']");
+
+			if( img.getId().endsWith("expanded") )
+				return false;
+			else if( img.getId().endsWith("collapsed") )
+				return true;
+			else
+				throw new EmbJoprTestException("Can't determine whether nav tree node is expanded.");
+
+		}
+
+		/** Returns the text link of this node. */
+		public HtmlAnchor getLabelLink(){
+			// Until I come up with something smarter, let it be so:
+			String xPath = ".//td[contains(@id,':text')]/a";
+			return (HtmlAnchor) this.elem.getFirstByXPath( xPath );
+		}
+
+		/** Returns the arrow's link. */
+		public HtmlAnchor getArrowLink(){
+			String xPath = ".//td[contains(@id,':handles')]//a[contains(@id,':handle')]";
+			return (HtmlAnchor) this.elem.getFirstByXPath( xPath );
+		}
+
+		/** Clicks the link of the this node. */
+		public void click() throws IOException {
+			this.getLabelLink().click();
+		}
+
+
+	}// class NavTreeNode()
+
+
+
+
+
+
+
+
+	/**
+	 * Inner class to encapsulate tab menu operations.
+	 */
+	protected class TabMenu {
+
+		public TabContentBox getTabContentBox() throws HtmlElementNotFoundException {
+
+			HtmlElement contentElement = (HtmlElement) client.getElement("content");
+			HtmlElement tabContentBox = (HtmlElement) contentElement.getFirstByXPath("div[@class='tabmenubox']");
+			if( null == tabContentBox )
+				throw new HtmlElementNotFoundException("Tab content box not found using div[@class='tabmenubox'] XPath");
+
+			return new TabContentBox(tabContentBox);
+		}
+
+		public ClickableElement getTab( String label ) throws HtmlElementNotFoundException {
+
+			DomElement element = (DomElement)client.getElement("tabmenu");
+			String xPath = "ul/li/span[normalize-space(string())='"+label+"'] | ul/li/a[normalize-space(string())='"+label+"']";
+			ClickableElement tabContent = element.getFirstByXPath(xPath);
+
+
+			if( null == tabContent )
+				throw new HtmlElementNotFoundException("Tab '"+label+"' not found using XPath '"+xPath+"'");
+
+			return tabContent;
+
+		}
+
+		/** Returns true if the tab with given label is active (it's content is shown). */
+		public boolean isTabActive( String label ) throws IOException, HtmlElementNotFoundException {
+
+			StyledElement tabContent = getTab(label);
+			return "span".equals( tabContent.getTagName() )
+							&& tabContent.getClassAttribute().contains("active");
+		}
+
+		/** Returns true if the tab with given label is disabled (grayed and can't be activated). */
+		public boolean isTabDisabled( String label ) throws HtmlElementNotFoundException {
+			StyledElement tabContent = getTab(label);
+			return "span".equals( tabContent.getTagName() )
+							&& tabContent.getClassAttribute().contains("disabled");
+		}
+
+		/** Shotcut -  getTab(label).click(); Not necesarilly clicks an anchor. */
+		public void clickTab( String label ) throws IOException, ActionNotAvailableException, HtmlElementNotFoundException {
+
+			StyledElement tabContent = getTab(label);
+			if( !( tabContent instanceof ClickableElement ) )
+				throw new ActionNotAvailableException("Tab element <"+tabContent.getTagName()+"> is not clickable: "+label);
+
+			((ClickableElement)tabContent).click();
+
+		}
+
+	}
+
+
+	/**
+	 * Base class for parts that will check their validity before performing actions.
+	 * Not necessary if we pay attention to validity, but why not check it -
+	 * performance is not a question for us.
+	 *
+	 * TODO: Instead, use the Page returned from click() etc.
+	 */
+	protected class PageContextAwareElement {
+		private URL validForURL;
+		private String validForDate;
+
+		/** Store the context identifiing attributes upon creation. */
+		public PageContextAwareElement() {
+			// TODO: Shouldn't we store the elements's page instead of current?
+			this.validForDate = client.getContentPage().getWebResponse().getResponseHeaderValue("Date");
+			this.validForURL = client.getContentPage().getWebResponse().getUrl();
+		}
+
+		/**
+		 * Checks whether this element is still valid in the current web page context,
+		 * i.e. whether we are still on the page in which this element existed.
+		 * @throws org.jboss.jopr.jsfunit.exceptions.ActionOutOfSyncException
+		 */
+		public void checkIfStillValid() throws ActionOutOfSyncException {
+
+			String validForDate_ = client.getContentPage().getWebResponse().getResponseHeaderValue("Date");
+			URL    validForURL_  = client.getContentPage().getWebResponse().getUrl();
+
+			if( !validForDate_.equals(this.validForDate) ||
+			    !validForURL_.equals(this.validForURL) )
+			{
+				throw new ActionOutOfSyncException(
+								"This element wrapper was created from another page and is not valid now.");
+			}
+		}
+
+	}
+
+	/**
+	 * Base class for parts that will check their validity before performing actions.
+	 * Not necessary if we pay attention to validity, but why not check it.
+	 */
+	protected class PageAware {
+
+		private Page  validForPage;
+		
+		public PageAware(){
+			this.validForPage = client.getContentPage();
+		}
+		
+		/**
+		 * Checks whether this element is still valid in the current web page context,
+		 * i.e. whether we are still on the page in which this element existed.
+		 * @throws org.jboss.jopr.jsfunit.exceptions.ActionOutOfSyncException
+		 */
+		public void checkIfStillValid() throws PageHasChangedException  {
+			if( !client.getContentPage().equals( this.validForPage ) ){
+				throw new PageHasChangedException(
+						"This element wrapper was created from another page and is not valid now.",
+						client.getContentPage(), this.validForPage );
+			}
+		}
+	}
+
+
+	/**
+	 * Inner class for manipulation with tab content box.
+	 */
+	protected class TabContentBox extends PageAware {
+
+		private HtmlElement element;
+		public HtmlElement getElement() {			return element;		}
+
+		public TabContentBox(HtmlElement element) {
+			super();
+			this.element = element;
+		}
+
+		/**
+		 * Returns first table under given header.
+		 *
+		 * Unfortunately, headers are not H2 or similar, but DIV class="instructionalText".
+		 * To be revised later.
+		 *
+		 * @return
+		 */
+		public ContentTable getTableUnderHeader( String headerText ) throws ActionOutOfSyncException
+		{
+			checkIfStillValid();
+
+			String xPath = "*[contains(normalize-space(), '"+headerText+"')]/following::table";
+			// [@id='resourceSummaryForm:dataTable'] - is this reliable?
+			HtmlTable tableElement = (HtmlTable) element.getFirstByXPath(xPath);
+			return new ContentTable(tableElement);
+		}
+
+		/**
+		 * Returns first table in the content box.
+		 * @param headerText
+		 * @return
+		 * @throws org.jboss.jopr.jsfunit.exceptions.ActionOutOfSyncException
+		 */
+		public ContentTable getFirstTable() throws ActionOutOfSyncException
+		{
+			checkIfStillValid();
+
+			String xPath = ".//form//table[contains( normalize-space(@id), ':dataTable')]";
+			// [@id='resourceSummaryForm:dataTable'] - is this reliable?
+			HtmlTable tableElement = (HtmlTable) element.getFirstByXPath(xPath);
+			return new ContentTable(tableElement);
+		}
+
+
+		/**
+		 * Finds first button with given label inside this box.
+		 * @param label
+		 * @return
+		 * @throws org.jboss.jopr.jsfunit.exceptions.HtmlElementNotFoundException
+		 * @throws org.jboss.jopr.jsfunit.exceptions.ActionOutOfSyncException
+		 */
+		public HtmlButtonInput getButtonByLabel( String label )
+						throws HtmlElementNotFoundException, ActionOutOfSyncException {
+
+			checkIfStillValid();
+
+			HtmlButtonInput button = this.element.getFirstByXPath(".//input[normalize-space(@value)='"+label+"']");
+			if( null == button )
+				throw new HtmlElementNotFoundException("Button labelled '"+label+"' not found.");
+
+			return button;
+
+		}
+
+	}// inner class TabContentBox
+
+
+
+	/**
+	 * Contains convenience methods for accessing content tables in EmbJopr.
+	 */
+	protected class ContentTable {
+
+		public static final String ID_CATEGORY_DATA_TABLE = "categorySummaryForm:dataTable";
+		public static final String ID_RESOURCE_DATA_TABLE = "resourceSummaryForm:dataTable";
+
+		private HtmlTable element;
+		public HtmlTable getElement() {			return element;		}
+
+		/**
+		 * Creates a data table wrapper for the given table element.
+		 */
+		public ContentTable( HtmlTable element ) {
+			this.element = element;
+		}
+
+		/**
+		 * Creates a data table wrapper for first found element with one of these IDs:
+		 * ID_CATEGORY_DATA_TABLE, ID_RESOURCE_DATA_TABLE
+		 *
+		 * This method assumes there's only one "data table" per page.
+		 * If not, it simply returns the first in the order of IDs searched.
+		 * You can always specify the element using ContentTable( HtmlTable element ).
+		 */
+		public ContentTable() throws HtmlElementNotFoundException {
+
+			// Find the data table - try some known IDs.
+			String[] elemIDs = { ID_CATEGORY_DATA_TABLE, ID_RESOURCE_DATA_TABLE };
+			Element elem = null;
+			for( String elemID : elemIDs ){
+				elem = client.getElement(elemID);
+				if( null != elem )
+					break;
+			}
+			if( null == elem )
+				throw new HtmlElementNotFoundException(
+								"Can't find the content table element, searched IDs: "
+								+ elemIDs.toString() );
+
+			this.element = (HtmlTable)elem;
+		}
+
+
+		// Columns maps
+		private List<String> colLabels = null;
+		private Map<String, Integer> colIndexes = null;
+		//private boolean analyzedButNotFound = false;
+
+
+
+		/**
+		 * Returns the first row that contains given text, or throws HtmlElementNotFoundException.
+		 * @param text
+		 * @return
+		 * @throws org.jboss.jopr.jsfunit.exceptions.HtmlElementNotFoundException
+		 *	       when no row contains specified text.
+		 */
+		public ContentTableRow getFirstRowContainingText( String text )
+						throws HtmlElementNotFoundException
+		{
+			if( 0 == element.getRowCount() )
+				throw new HtmlElementNotFoundException("Table has no rows.");
+
+			// TODO: Escape the single quotes. By doubling?
+			// http://books.google.com/books?id=jzqFMlM0gb0C&pg=PA308&lpg=PA308&dq=xquery+escape+quote&source=bl&ots=DIKQ92AhHh&sig=A7adGlif6jfYKtJXGc4eZbXYeCQ&hl=cs&ei=LYCcSYKLO5ir-gazwfjtBA&sa=X&oi=book_result&resnum=8&ct=result
+			String xPath = ".//tr[contains(string(), '"+text+"')]";
+			HtmlTableRow elm = (HtmlTableRow) element.getFirstByXPath(xPath);
+			if( null == elm )
+				throw new HtmlElementNotFoundException(xPath);
+			return new ContentTableRow(elm, this);
+		}
+
+
+
+		/**
+		 * Returns wrapper of the table row which contains a link with given label,
+		 * or null when the no such row is found.
+		 */
+		public ContentTableRow findFirstRowContainingLink( String linkLabel )
+		{
+			if( 0 == element.getRowCount() )
+				return null;
+
+			String xPath = ".//tr[.//a[normalize-space()='"+linkLabel+"']]";
+			HtmlTableRow elm = (HtmlTableRow) element.getFirstByXPath(xPath);
+			if( null == elm )
+				return null;
+			return new ContentTableRow(elm, this);
+		}
+
+		public List<ContentTableRow> getRows(){
+			if( 0 == element.getRowCount() )
+				return Collections.EMPTY_LIST;
+
+			String xPath = "./tbody/tr";
+			List<HtmlTableRow> trList = (List) element.getByXPath(xPath);
+			List<ContentTableRow> rows = new ArrayList(trList.size());
+			for (HtmlTableRow tr : trList) {
+				rows.add( new ContentTableRow(tr, this));
+			}
+			return rows;
+		}
+
+		/**
+		 * Returns wrapper of the table row which contains a link with given label.
+		 * @throws org.jboss.jopr.jsfunit.exceptions.HtmlElementNotFoundException
+		 *				 when no row with such label found.
+		 */
+		public ContentTableRow getFirstRowContainingLink( String linkLabel )
+						throws HtmlElementNotFoundException
+		{
+			if( 0 == element.getRowCount() )
+				throw new HtmlElementNotFoundException("Table has no rows.");
+
+			String xPath = ".//tr[.//a[normalize-space()='"+linkLabel+"']]";
+			HtmlTableRow elm = (HtmlTableRow) element.getFirstByXPath(xPath);
+			if( null == elm )
+				throw new HtmlElementNotFoundException(
+								"Can't find row containing link '"+linkLabel+"' using XPath: "+xPath);
+			return new ContentTableRow(elm, this);
+		}
+
+		/**
+		 * Creates a list of columns headers
+		 * and a label => col index map.
+		 */
+		public void analyzeColumns() throws HtmlElementNotFoundException
+		{
+
+			/*if( this.analyzedButNotFound )
+				throw new HtmlElementNotFoundException("Table has no column headers.");
+			/**/ // Give it another chance, JavaScript could change it.
+
+			// Get all TH from the first THEAD row that contains TH.
+			String xPath = "./thead/tr[th and position()=1]/th";
+			List<HtmlTableHeaderCell> colHeaders = (List<HtmlTableHeaderCell>) this.element.getByXPath(xPath);
+
+			if( 0 == colHeaders.size() ){
+				//this.analyzedButNotFound = true;
+				throw new HtmlElementNotFoundException("Table has no column headers.");
+			}
+
+			List<String> colLabels_ = new ArrayList(colHeaders.size());
+			Map<String, Integer> colIndexes_ = new HashMap(colHeaders.size());
+
+			//for( HtmlTableHeaderCell th : colHeaders ){
+			for( int i = 0; i < colHeaders.size(); i++ ) {
+				HtmlTableHeaderCell th = colHeaders.get(i);
+				String sHeader = th.getTextContent();
+				colLabels_.add( sHeader );
+				colIndexes_.put( sHeader, i );
+			}
+
+			this.colLabels = colLabels_;
+			this.colIndexes = colIndexes_;
+
+		}
+
+		/**
+		 * Returns an index of the column with given name in the TH header,
+		 * or throws HtmlElementNotFound in two cases:
+		 *  1) Table does not have column headers
+		 *  2) No header with such name was found.
+		 *
+		 * @param colName
+		 * @return
+		 * @throws org.jboss.jopr.jsfunit.exceptions.HtmlElementNotFoundException
+		 */
+		public int getColumnIndexByName( String colName ) throws HtmlElementNotFoundException {
+			if( null == this.colIndexes )
+				this.analyzeColumns();
+
+			Integer index = this.colIndexes.get(colName);
+			if( null == index )
+				throw new HtmlElementNotFoundException("No column named '"+colName+"'.");
+
+			return index;
+		}
+
+	}// inner class ContentTable
+
+
+
+
+
+	/**
+	 * Provides extra method that parses text-like info table (with one column).
+	 */
+	protected class ContentInfoTable extends ContentTable {
+
+		public ContentInfoTable(HtmlTable element) {
+			super(element);
+		}
+
+		/**
+		 * Parses the content of the table for properties.
+		 * @return
+		 */
+		public Properties getProperties()
+		{
+			Properties props = new Properties();
+
+			// The template has label in span/strong and the value as text in td.
+			String xPath = ".//tr/td[span/strong]";
+			List<HtmlTableCell> cells  = (List<HtmlTableCell>) this.getElement().getByXPath(xPath);
+			for( HtmlTableCell cell : cells ){
+				String[] parts = cell.getTextContent().split(":");
+				props.put(parts[0], parts[1]);
+			}
+			return props;
+		}
+
+	}// inner class ContentInfoTable
+
+
+
+	/**
+	 * Row of a content table.
+	 * Contains convenience methods for accessing content table rows in EmbJopr.
+	 */
+	protected class ContentTableRow {
+
+		private HtmlTableRow element;
+		public HtmlTableRow getElement() {			return element;		}
+
+		private ContentTable containingTable;
+
+		private ContentTableRow(HtmlTableRow elm, ContentTable containingTable) {
+			this.element = elm;
+			this.containingTable = containingTable;
+		}
+
+		/**
+		 * Finds a <button> or <input type="button"> with the given label.
+		 * @param label The label of the button. Compared to a trimmed label text of the button.
+		 * @returns the button element - either HtmlButtonInput or HtmlButton.
+		 * @throws org.jboss.jopr.jsfunit.exceptions.HtmlElementNotFoundException
+		 */
+		public ClickableElement getButtonByLabel( String label ) throws HtmlElementNotFoundException
+		{
+			String xPath = ".//input[@type='button' and normalize-space(@value) = '"+label+"']" +
+							" || .//button[normalize-space() = '"+label+"']";
+			HtmlElement elm = this.element.getFirstByXPath(xPath);
+			if( null == elm )
+				throw new HtmlElementNotFoundException("Can't find the button using xPath: "+xPath);
+			if( !(elm instanceof HtmlButton ) && !(elm instanceof HtmlButtonInput) )
+				throw new HtmlElementNotFoundException("Element is not a button, but: "+elm.getClass().getName());
+
+			return (ClickableElement)elm;
+		}
+
+
+		/**
+		 * Gives the (first) link from the cell of this row, from the column with given header.
+		 * @param colName
+		 * @return
+		 * @throws org.jboss.jopr.jsfunit.exceptions.HtmlElementNotFoundException
+		 *				 in two cases:
+		 *             1) Table has no column of given name
+		 *             2) There's no link in that cell.
+		 */
+		public HtmlAnchor getFirstLinkFromColumn(String colName) throws HtmlElementNotFoundException
+		{
+			HtmlTableCell cell = this.getCellByColumnName(colName);
+
+			String xPath = ".//a";
+			HtmlAnchor link = (HtmlAnchor) this.element.getFirstByXPath(xPath);
+
+			if( null == link )
+				throw new HtmlElementNotFoundException("No link found in column '"+colName+"'");
+
+			return link;
+		}
+
+
+		/**
+		 * Returns the link with given label.
+		 * @param string
+		 */
+		public HtmlAnchor getLinkByLabel(String linkLabel) throws HtmlElementNotFoundException {
+			String xPath = ".//a[normalize-space()='"+linkLabel+"']";
+			HtmlAnchor link = (HtmlAnchor) this.element.getFirstByXPath(xPath);
+			if( null == link ){
+				StringBuilder sb = new StringBuilder();
+				List<HtmlElement> linksFound = this.element.getHtmlElementsByTagName("a");
+				for( HtmlElement linkFound : linksFound ){
+					sb.append("'").append( ((HtmlAnchor)linkFound).getTextContent() ).append("', ");
+				}
+				String availLinks = StringUtils.removeEnd( sb.toString(), ", " );
+
+				throw new HtmlElementNotFoundException("Can't find link using '"+xPath+"', available: "+availLinks);
+			}
+			return link;
+		}
+
+		/**
+		 * Returns the cell of this table from given index,
+		 * or throws IndexOutOfBoundsException.
+		 *
+		 * TODO: May throw IndexOutOfBoundsException - leave unchecked?
+		 *
+		 * @param index
+		 * @return
+		 */
+		public HtmlTableCell getCell( int index ){
+			HtmlTableCell cell = this.element.getCell(index);
+			return cell;
+		}
+
+		/**
+		 * Returns  this row's cell element from the column with specified name.
+		 * @param colName  Name of the column.
+		 * @throws org.jboss.jopr.jsfunit.exceptions.HtmlElementNotFoundException
+		 *         if no such column exists in the table.
+		 */
+		public HtmlTableCell getCellByColumnName( String colName ) throws HtmlElementNotFoundException
+		{
+			int index = this.containingTable.getColumnIndexByName(colName);
+			return getCell(index);
+		}
+
+		/**  Shortcut - returns text content of this row's cell from given column. */
+		public String getCellTextByColumnName( String colName ) throws HtmlElementNotFoundException {
+			return this.getCellByColumnName(colName).getTextContent();
+		}
+
+
+	}// inner class ContentTableRow
+
+
+
+
+	/**
+	 *  Pagination control
+	 *  Not tested yet.
+	 */
+	public class ContentBoxPagination {
+
+		//private HtmlElement element;
+		//public HtmlElement getElement() {			return element;		}
+
+		private static final String ID_PAGE_CONTROLS = "categorySummaryForm:dataTableScroller";
+		private static final String ID_PAGE_SIZE_SELECT = "categorySummaryForm:currentPageSize";
+		private static final String ID_PAGINATION_TOTAL_ITEMS = "paginationTotalItems";
+
+		protected HtmlDivision getPageContols(){
+			return (HtmlDivision) client.getElement(ID_PAGE_CONTROLS);
+		}
+
+		protected HtmlSelect getPageSizeSelect(){
+			return (HtmlSelect) client.getElement(ID_PAGE_SIZE_SELECT);
+		}
+
+		public int getTotalItemsCount() throws ActionNotAvailableException
+		{
+			Element e = client.getElement(ID_PAGINATION_TOTAL_ITEMS);
+			String textContent = e.getTextContent();
+			String[] parts = textContent.split(":");
+			if( parts.length < 2 )
+				throw new ActionNotAvailableException("Total pagination items count expected after 'Total:' or similar.");
+
+			String countStr = parts[1].trim();
+			try{
+				return NumberUtils.createInteger( countStr );
+			}catch(NumberFormatException ex ){
+				throw new ActionNotAvailableException("Can't parse pagination items count from '"+countStr+"'", ex);
+			}
+		}
+
+		protected HtmlSpan getGoSpan( String spanLabel ) throws HtmlElementNotFoundException {
+			String xPath = ".//span[normalize-space() = '"+spanLabel+"']";
+			HtmlSpan span = getPageContols().getFirstByXPath(xPath);
+			if( null == span )
+				throw new HtmlElementNotFoundException(
+								"Span with '"+spanLabel+"' not found using XPath '"+xPath+"'");
+			return span;
+		}
+
+		protected HtmlTableCell getGoPage( int pageNumber ) throws HtmlElementNotFoundException{
+			String xPath = ".//td[normalize-space() = '"+pageNumber+"']";
+			HtmlElement td = getPageContols().getFirstByXPath(xPath);
+			if( null == td )
+				throw new HtmlElementNotFoundException(
+								"Page number "+pageNumber+" not found. Try 1 - "+this.getPageCount());
+			return (HtmlTableCell) td;
+		}
+
+
+		public boolean goFirst() throws IOException, HtmlElementNotFoundException{
+			// TODO: Compare old and new page object reference?
+			HtmlSpan goSpan = getGoSpan("First");
+			goSpan.click();
+			return !"".equals( ((HtmlTableCell)goSpan.getParentNode()).getOnClickAttribute() );
+		}
+
+		public boolean goPrev() throws IOException, HtmlElementNotFoundException{
+			HtmlSpan goSpan = getGoSpan("Prev");
+			goSpan.click();
+			return !"".equals( ((HtmlTableCell)goSpan.getParentNode()).getOnClickAttribute() );
+		}
+
+		public boolean goNext() throws IOException, HtmlElementNotFoundException{
+			HtmlSpan goSpan = getGoSpan("Next");
+			goSpan.click();
+			return !"".equals( ((HtmlTableCell)goSpan.getParentNode()).getOnClickAttribute() );
+		}
+
+		public boolean goLast() throws IOException, HtmlElementNotFoundException{
+			HtmlSpan goSpan = getGoSpan("Last");
+			goSpan.click();
+			return !"".equals( ((HtmlTableCell)goSpan.getParentNode()).getOnClickAttribute() );
+		}
+
+		public void goPage( int pageNumber ) throws IOException, HtmlElementNotFoundException{
+			getGoPage(pageNumber).click();
+		}
+
+
+		public boolean isPageActive( int pageNumber ) throws IOException, HtmlElementNotFoundException{
+			return !getGoPage(pageNumber).getStyleAttribute().contains("-inact");
+		}
+
+		public int getPageCount(){
+			String xPath = ".//td[ count(./*) = 0  and  normalize-space() > 0 ]";
+			List<?> elements = getPageContols().getByXPath(xPath);
+			return elements.size();
+		}
+
+		public int getItemsPerPage() throws HtmlElementNotFoundException, ActionNotAvailableException {
+			String selectText = getPageSizeSelect().asText();
+			try {
+				return NumberUtils.createInteger( selectText );
+			}
+			catch( NumberFormatException ex ){
+				throw new ActionNotAvailableException("Can't parse 'items per page' from '"+selectText+"'.", ex);
+			}
+		}
+		public void setItemsPerPage( int num ) throws HtmlElementNotFoundException {
+			String optionValue = ""+num;
+			HtmlSelect pageSizeSelect = getPageSizeSelect();
+
+			try{
+				pageSizeSelect.getOptionByValue(optionValue);
+			}
+			catch( ElementNotFoundException ex ){
+				// Print out available options.
+				StringBuilder sb = new StringBuilder(pageSizeSelect.getOptionSize());
+				for( HtmlOption opt : pageSizeSelect.getOptions() ){
+					sb.append(" ").append(opt.getValueAttribute());
+				}
+				String possibleOptions = sb.toString().trim().replace(" ", ", ");
+
+				throw new HtmlElementNotFoundException("Specified option is invalid. Try one of: "+possibleOptions, ex);
+			}
+
+			pageSizeSelect.setSelectedAttribute(optionValue, true);
+		}
+
+	}// ContentBoxPagination
+
+
+
+	/**
+	 * Finds a <a> tag inside a form that has a particular label.
+	 *
+	 * TODO: Create Form wrapper.
+	 */
+	public HtmlAnchor getLinkInsideForm(String formId, String linkLabel) throws HtmlElementNotFoundException
+	{
+		HtmlForm form = (HtmlForm)client.getElement(formId);
+		String xPath = ".//a[contains(normalize-space(), '"+linkLabel+"']";
+		HtmlAnchor link = form.getFirstByXPath(xPath);
+		if( null == link )
+			throw new HtmlElementNotFoundException("Can't find row containing link '"+linkLabel+"' using XPath: "+xPath);
+		return link;
+	}
+
+
+
+
+	/** Calls Thread.sleep(ms), ignores the InterruptedException. */
+	@SuppressWarnings("empty-statement")
+	public static void sleep( int ms ) {
+		try {
+			Thread.sleep(3000);
+		} catch (InterruptedException ex) {
+			; // We don't care, that's the purpose of this method.
+		}
+	}
+
+
+
+	// TODO
+	protected class JMXDeploymentInfo {
+
+		// Etc...
+		public boolean isDeployed(){
+			return false;
+		}
+
+	}
+
+	// TODO
+	protected class FileDeploymentInfo {
+
+		// Etc...
+		public boolean isDeployed(){
+			return false;
+		}
+
+	}
+
+	// TODO
+	protected class EmbJoprDeploymentInfo {
+
+		// Etc...
+		public boolean isDeployed(){
+			return false;
+		}
+
+	}
+
+
+
+
+}// class EmbJoprTestToolkit




More information about the embjopr-commits mailing list