Author: fjuma
Date: 2008-12-16 15:13:35 -0500 (Tue, 16 Dec 2008)
New Revision: 74
Added:
trunk/jsfunit/src/test/java/org/jboss/jopr/jsfunit/DatasourceTest.java
trunk/jsfunit/src/test/java/org/jboss/jopr/jsfunit/EmbjoprTestCase.java
Modified:
trunk/core/src/main/webapp/include/tabMenu.xhtml
trunk/core/src/main/webapp/secure/categorySummary.xhtml
trunk/core/src/main/webapp/secure/resourceCreatePage1.xhtml
trunk/core/src/main/webapp/secure/resourceInstanceConfig.xhtml
trunk/core/src/main/webapp/secure/resourceInstanceMetrics.xhtml
trunk/jsfunit/pom.xml
Log:
Added a base test class for Embedded Jopr JSFUnit tests and added a test class that
currently contains some preliminary datasource creation tests.
Added ID attributes to tags in some xhtml files.
Modified: trunk/core/src/main/webapp/include/tabMenu.xhtml
===================================================================
--- trunk/core/src/main/webapp/include/tabMenu.xhtml 2008-12-16 20:10:13 UTC (rev 73)
+++ trunk/core/src/main/webapp/include/tabMenu.xhtml 2008-12-16 20:13:35 UTC (rev 74)
@@ -72,7 +72,7 @@
<h:panelGroup layout="block"
rendered="#{activeTab ne 'configuration' and
navigationAction.enabledTabs.contains('configuration')}">
<li>
- <s:link styleClass=""
action="#{resourceConfigurationUIBean.resourceConfiguration()}"
propagation="end">
+ <s:link id="configurationTab" styleClass=""
action="#{resourceConfigurationUIBean.resourceConfiguration()}"
propagation="end">
#{messages['tab.menu.configuration']}
</s:link>
</li>
@@ -97,7 +97,7 @@
<h:panelGroup layout="block"
rendered="#{activeTab ne 'metric' and
navigationAction.enabledTabs.contains('metric')}">
<li>
- <s:link styleClass=""
view="/secure/resourceInstanceMetrics.xhtml" propagation="end">
+ <s:link id="metricsTab" styleClass=""
view="/secure/resourceInstanceMetrics.xhtml" propagation="end">
#{messages['tab.menu.metrics']}
</s:link>
</li>
Modified: trunk/core/src/main/webapp/secure/categorySummary.xhtml
===================================================================
--- trunk/core/src/main/webapp/secure/categorySummary.xhtml 2008-12-16 20:10:13 UTC (rev
73)
+++ trunk/core/src/main/webapp/secure/categorySummary.xhtml 2008-12-16 20:13:35 UTC (rev
74)
@@ -71,7 +71,7 @@
</h:selectOneMenu>
</h:form>
</div>
- <h:form>
+ <h:form id="categorySummaryForm">
<rich:dataTable
id="dataTable"
rows="#{tableManager.numRows}"
@@ -117,7 +117,8 @@
<h:outputText value="#{resourceItem.availability}"/>
</rich:column>
<rich:column>
- <s:button
action="#{resourceCRUDAction.removeResourceAction()}" value="Delete"
+ <s:button id="removeButton"
+
action="#{resourceCRUDAction.removeResourceAction()}" value="Delete"
styleClass="buttonsmall"
rendered="#{resourceItem.resource.resourceType.deletable}">
<f:param name="resourceId"
value="#{resourceItem.resource.id}"/>
Modified: trunk/core/src/main/webapp/secure/resourceCreatePage1.xhtml
===================================================================
--- trunk/core/src/main/webapp/secure/resourceCreatePage1.xhtml 2008-12-16 20:10:13 UTC
(rev 73)
+++ trunk/core/src/main/webapp/secure/resourceCreatePage1.xhtml 2008-12-16 20:13:35 UTC
(rev 74)
@@ -36,7 +36,7 @@
<ui:include
src="../include/displayGlobalMessages.xhtml"/>
<hr/>
- <h:form>
+ <h:form id="resourceCreateForm">
<h:panelGrid styleClass="formstyle">
<ui:include
src="/include/formDropDownComponent.xhtml">
@@ -50,9 +50,10 @@
</ui:include>
</h:panelGrid>
<h:panelGrid columns="2"
styleClass="buttons-table" columnClasses="button-cell">
- <h:commandButton
value="#{messages['resource.add.button.continue']}"
-
action="#{templateDropDownPopulator.populateFromTemplate()}"
- styleClass="buttonmed"/>
+ <h:commandButton id="addButton"
+
value="#{messages['resource.add.button.continue']}"
+
action="#{templateDropDownPopulator.populateFromTemplate()}"
+ styleClass="buttonmed"/>
<s:button
value="#{messages['resource.add.button.cancel']}"
view="/secure/summary.xhtml"
styleClass="buttonmed"
Modified: trunk/core/src/main/webapp/secure/resourceInstanceConfig.xhtml
===================================================================
--- trunk/core/src/main/webapp/secure/resourceInstanceConfig.xhtml 2008-12-16 20:10:13 UTC
(rev 73)
+++ trunk/core/src/main/webapp/secure/resourceInstanceConfig.xhtml 2008-12-16 20:13:35 UTC
(rev 74)
@@ -71,12 +71,14 @@
nullConfigurationStyle="infoBlock"/>
<h:panelGrid columns="2"
styleClass="buttons-table" columnClasses="button-cell">
- <h:commandButton type="submit"
+ <h:commandButton id="saveButton"
+ type="submit"
action="#{resourceConfigurationUIBean.saveConfiguration()}"
value="Save"
alt="Click to Save Changes"
styleClass="buttonmed"/>
- <s:button
value="#{messages['resource.add.button.cancel']}"
+ <s:button id="cancelButton"
+
value="#{messages['resource.add.button.cancel']}"
view="/secure/summary.xhtml"
styleClass="buttonmed"
propagation="end"/>
Modified: trunk/core/src/main/webapp/secure/resourceInstanceMetrics.xhtml
===================================================================
--- trunk/core/src/main/webapp/secure/resourceInstanceMetrics.xhtml 2008-12-16 20:10:13
UTC (rev 73)
+++ trunk/core/src/main/webapp/secure/resourceInstanceMetrics.xhtml 2008-12-16 20:13:35
UTC (rev 74)
@@ -72,6 +72,7 @@
<h2>#{messages['metrics.resourceInstance.numericMetricValues']}</h2>
<rich:dataTable
+ id="dataTable"
value="#{measurementCategoryList}"
var="category"
rendered="#{measurementCategoryList ne null}"
styleClass="controlTable"
@@ -91,7 +92,7 @@
rowClasses="oddRow,evenRow"
columnClasses="rich-table-cell, rich-table-cell,
rich-table-cell-action">
<rich:column>#{measurementDefinitionMap[md.measurementData.name].displayName}</rich:column>
-
<rich:column>#{md.measurementValueAndUnits}</rich:column>
+ <rich:column
id="measurementValue">#{md.measurementValueAndUnits}</rich:column>
<rich:column>#{measurementDefinitionMap[md.measurementData.name].description}</rich:column>
</rich:subTable>
<f:facet name="footer"><h:graphicImage
value="images/spacer.gif"/>
Modified: trunk/jsfunit/pom.xml
===================================================================
--- trunk/jsfunit/pom.xml 2008-12-16 20:10:13 UTC (rev 73)
+++ trunk/jsfunit/pom.xml 2008-12-16 20:13:35 UTC (rev 74)
@@ -72,6 +72,11 @@
<scope>provided</scope>
<version>2.8.1</version>
</dependency>
+ <dependency>
+ <groupId>jdom</groupId>
+ <artifactId>jdom</artifactId>
+ <version>1.0</version>
+ </dependency>
</dependencies>
<profiles>
<profile>
@@ -102,6 +107,7 @@
result is in server.log
<jsfunit.htmlunitsnooper>enabled</jsfunit.htmlunitsnooper> -->
<jsfunit.testdata>${basedir}/testdata</jsfunit.testdata>
+
<jsfunit.deploy.dir>${basedir}/target/jboss42x/deploy</jsfunit.deploy.dir>
</systemProperties>
</container>
</configuration>
@@ -185,6 +191,7 @@
<!-- Snoops request and response from client side. Result
is in server.log
<jsfunit.htmlunitsnooper>enabled</jsfunit.htmlunitsnooper> -->
<jsfunit.testdata>${basedir}/testdata</jsfunit.testdata>
+
<jsfunit.deploy.dir>${basedir}/target/jboss5x/deploy</jsfunit.deploy.dir>
</systemProperties>
</container>
</configuration>
Added: trunk/jsfunit/src/test/java/org/jboss/jopr/jsfunit/DatasourceTest.java
===================================================================
--- trunk/jsfunit/src/test/java/org/jboss/jopr/jsfunit/DatasourceTest.java
(rev 0)
+++ trunk/jsfunit/src/test/java/org/jboss/jopr/jsfunit/DatasourceTest.java 2008-12-16
20:13:35 UTC (rev 74)
@@ -0,0 +1,394 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2008, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This 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 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site:
http://www.fsf.org.
+ */
+
+package org.jboss.jopr.jsfunit;
+
+import com.gargoylesoftware.htmlunit.html.HtmlAnchor;
+import com.gargoylesoftware.htmlunit.html.HtmlButtonInput;
+import com.gargoylesoftware.htmlunit.html.HtmlForm;
+import com.gargoylesoftware.htmlunit.html.HtmlImage;
+import com.gargoylesoftware.htmlunit.html.HtmlSelect;
+import com.gargoylesoftware.htmlunit.html.HtmlInput;
+import java.io.IOException;
+import junit.framework.Test;
+import junit.framework.TestSuite;
+import org.jboss.mx.util.MBeanServerLocator;
+import javax.management.MBeanServer;
+import javax.management.ObjectName;
+import java.util.Set;
+import java.util.Iterator;
+import org.jdom.Document;
+import org.jdom.Element;
+import org.jdom.input.SAXBuilder;
+import org.jdom.JDOMException;
+import java.io.File;
+import java.util.Map;
+import java.util.HashMap;
+import javax.naming.InitialContext;
+import java.sql.Connection;
+import javax.sql.DataSource;
+import javax.naming.Context;
+import java.sql.SQLException;
+import org.jboss.jopr.jsfunit.EmbjoprTestCase;
+
+/**
+ * When complete, this class will contain tests for creating,
+ * configuring, and deleting various types of datasources. This
+ * test class should be run against JBAS 5.x.
+ *
+ * @author Farah Juma
+ *
+ */
+public class DatasourceTest extends EmbjoprTestCase {
+
+ // Datasource types, as they appear in the left nav
+ private final String LOCAL_TX_DATASOURCE="Local TX Datasources";
+ private final String NO_TX_DATASOURCE="No TX Datasources";
+ private final String XA_DATASOURCE="XA Datasources";
+
+ /**
+ * Create a new datasource using the given type, template, and properties.
+ *
+ * @param datasourceType must be the name of a datasource type, as it
+ * appears in the left nav (eg. "Local TX Datasources")
+ */
+ private void createDatasource(String datasourceType,
+ String datasourceTemplate,
+ Map<String, String> propertiesMap) throws
IOException {
+
+ // Expand the "Datasources" tree node
+ HtmlImage resourceNodeImage = getNavTreeArrow("Datasources");
+ resourceNodeImage.click();
+
+ HtmlAnchor datasourceLink = getNavTreeLink(datasourceType);
+ datasourceLink.click();
+
+ // Add a new datasource
+ client.click("actionHeaderForm:addNewNotContent");
+ HtmlSelect menu = (HtmlSelect)client.getElement("selectedTemplate");
+ menu.setSelectedAttribute(datasourceTemplate, Boolean.TRUE);
+ client.click("resourceCreateForm:addButton");
+
+ // Configure the properties associated with this datasource
+ fillOutForm(propertiesMap);
+ }
+
+ /**
+ * Delete the datasource given by datasourceName.
+ */
+ private void deleteDatasource(String datasourceName) throws IOException {
+ HtmlAnchor datasourceLink = getNavTreeLink("Datasources");
+ datasourceLink.click();
+ HtmlButtonInput deleteButton = getDeleteButton("categorySummaryForm",
+ datasourceName);
+ deleteButton.click();
+ }
+
+ /**
+ * Use JMX to check if the datasource given by datasourceName is
+ * deployed.
+ */
+ private boolean isDatasourceDeployed(String jndiName,
+ String datasourceType) {
+ try {
+
+ ObjectName deploymentMBean;
+ ObjectName objName;
+ Object state;
+ Set dsMBeans;
+ String service;
+
+ // One of the MBeans we will need to inspect depends on
+ // the type of datasource
+ if(datasourceType.equals(LOCAL_TX_DATASOURCE)) {
+ service="LocalTxCM";
+ } else if(datasourceType.equals(NO_TX_DATASOURCE)) {
+ service="NoTxCM";
+ } else {
+ service="XATxCM";
+ }
+
+ String[] dsMBeanServices = {"DataSourceBinding",
+ "ManagedConnectionPool",
+ "ManagedConnectionFactory",
+ service};
+
+ // Query the MBean server to check if the datasource is deployed
+ MBeanServer jmxServer = MBeanServerLocator.locateJBoss();
+
+ // Inspect the following MBeans and make sure that the "State"
attribute
+ // is "DEPLOYED":
+ // 1)
"jboss.jca:name=TestDS,service=DataSourceBinding",type=Component
+ // 2)
"jboss.jca:name=TestDS,service=ManagedConnectionPool",type=Component
+ // 3)
"jboss.jca:name=TestDS,service=ManagedConnectionFactory",type=Component
+ // 4) The fourth MBean inspected depends on the type of datasource
+ for(int i = 0; i < dsMBeanServices.length; i++) {
+
+ objName = new
ObjectName("jboss.deployment:id=\"jboss.jca:name="
+ + jndiName + ",service="
+ + dsMBeanServices[i] + "\",type=Component");
+
+ dsMBeans = jmxServer.queryNames(objName, null);
+ if (dsMBeans.size() != 1) return false;
+
+ deploymentMBean = (ObjectName)dsMBeans.iterator().next();
+ state = jmxServer.getAttribute(deploymentMBean, "State");
+ if(!("DEPLOYED".equals(state.toString()))) return false;
+ }
+
+ return true;
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+
+ /**
+ * checkProperties reads the *-ds.xml file corresponding to the datasource
+ * given by jndiName and compares the property values in this file to the
+ * given expected values. checkProperties returns true if all the given
+ * properties are correctly set in the *-ds.xml file and false otherwise.
+ */
+ private boolean checkProperties(String jndiName,
+ String datasourceType,
+ Map<String, String> expectedValuesMap) {
+
+ Map<String, String> actualValuesMap = new HashMap<String, String>();
+ String datasourceElementName;
+
+ try {
+
+ // Parse the *-ds.xml file
+ File file = new File(System.getProperty("jsfunit.deploy.dir")
+ + "/" + jndiName + "-ds.xml");
+
+ SAXBuilder builder = new SAXBuilder();
+ Document doc = builder.build(file);
+
+ Element root = doc.getRootElement();
+ assertTrue(root.getName().equals("datasources"));
+
+ // Get the datasource element
+ if(datasourceType.equals(LOCAL_TX_DATASOURCE)) {
+ datasourceElementName="local-tx-datasource";
+ } else if(datasourceType.equals(NO_TX_DATASOURCE)) {
+ datasourceElementName="no-tx-datasource";
+ } else {
+ datasourceElementName="xa-datasource";
+ }
+ Element datasource = root.getChild(datasourceElementName);
+
+ // Create actualValuesMap by mapping property names to
+ // property values
+ Iterator itr = (datasource.getChildren()).iterator();
+ while(itr.hasNext()) {
+ Element property = (Element)itr.next();
+ actualValuesMap.put(property.getName(), property.getValue());
+ }
+
+ // Compare the actual values to the expected ones
+ itr = expectedValuesMap.keySet().iterator();
+ while(itr.hasNext()) {
+ String key = (String)itr.next();
+ if(actualValuesMap.containsKey(key)) {
+ if(!expectedValuesMap.get(key).equals(actualValuesMap.get(key))) {
+ return false; // incorrect value
+ }
+ } else {
+ return false; // value was not set
+ }
+ }
+
+ return true;
+
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * Connect to the database identified by the given JNDI name,
+ * using the given username and password. Return the Connection
+ * object.
+ */
+ public Connection connectDB(String jndiName, String username,
+ String password) throws Exception{
+ Context ctx = new InitialContext();
+ DataSource ds = (DataSource)ctx.lookup(jndiName);
+ return ds.getConnection(username, password);
+ }
+
+ /**
+ * Disconnect from the database.
+ */
+ public void disconnectDB(Connection con) throws SQLException {
+ if(con != null) {
+ con.close();
+ }
+ }
+
+ /**
+ * @return the suite of tests being tested
+ */
+ public static Test suite()
+ {
+ return new TestSuite(DatasourceTest.class);
+ }
+
+ /*
+ * Some preliminary creation tests
+ */
+
+ /**
+ * Create a new datasource. Leave some property values that aren't
+ * required unset.
+ */
+ public void testCreateDatasource() throws IOException {
+ Map<String, String> propertiesMap = new HashMap<String, String>();
+
+ // The properties we want to configure
+ propertiesMap.put("jndi-name", "TestDS");
+ propertiesMap.put("user-name", "testUser");
+ propertiesMap.put("password", "password");
+ propertiesMap.put("min-pool-size", "5");
+ propertiesMap.put("driver-class", "org.hsqldb.jdbcDriver");
+ propertiesMap.put("connection-url", "jdbc:hsqldb:.");
+ propertiesMap.put("idle-timeout-minutes", "20");
+ propertiesMap.put("query-timeout", "180");
+ propertiesMap.put("prepared-statement-cache-size", "2");
+ propertiesMap.put("valid-connection-checker-class-name",
+
"org.jboss.resource.adapter.jdbc.CheckValidConnectionSQL");
+ propertiesMap.put("stale-connection-checker-class-name",
+
"org.jboss.resource.adapter.jdbc.StaleConnectionChecker");
+ propertiesMap.put("exception-sorter-class-name",
+ "org.jboss.resource.adapter.jdbc.ExceptionSorter");
+ propertiesMap.put("allocation-retry", "10000");
+ propertiesMap.put("allocation-retry-wait-millis", "10000");
+ propertiesMap.put("background-validation-millis", "15000");
+ propertiesMap.put("use-try-lock", "61000");
+ propertiesMap.put("prefill", "true");
+ propertiesMap.put("share-prepared-statements", "false");
+
+ createDatasource(LOCAL_TX_DATASOURCE,
+ "default__Local TX Datasource",
+ propertiesMap);
+ client.click("resourceConfigurationForm:saveButton");
+
+ // Check for the appropriate success messages
+ String expectedMessage = "Successfully added new Local TX Datasource";
+ checkClientAndServerMessages(expectedMessage, expectedMessage, false);
+
+ assertTrue(isDatasourceDeployed(propertiesMap.get("jndi-name"),
+ LOCAL_TX_DATASOURCE));
+ assertTrue(checkProperties(propertiesMap.get("jndi-name"),
+ LOCAL_TX_DATASOURCE,
+ propertiesMap));
+
+ // TODO: need to verify that appropriate default values were
+ // set for properties that were not specified above
+
+ // Clean up
+ deleteDatasource(propertiesMap.get("jndi-name"));
+ expectedMessage = "Successfully deleted Local TX Datasource '"
+ + propertiesMap.get("jndi-name") +
"'";
+ checkClientAndServerMessages(expectedMessage, expectedMessage, false);
+ }
+
+ /**
+ * Attempt to create a new datasource but leave at least one required
+ * value unset. An error should occur.
+ */
+ public void testCreateDatasourceMissingRequiredValues() throws IOException {
+
+ // Leave jndi-name and connection-url unset
+ Map<String, String> propertiesMap = new HashMap<String, String>();
+ propertiesMap.put("user-name", "testUser");
+ propertiesMap.put("max-pool-size", "10");
+ propertiesMap.put("prefill", "true");
+ propertiesMap.put("idle-timeout-minutes", "20");
+ propertiesMap.put("set-tx-query-timeout", "true");
+ propertiesMap.put("query-timeout", "1800");
+
+ createDatasource(NO_TX_DATASOURCE, "default__No TX Datasource",
+ propertiesMap);
+ client.click("resourceConfigurationForm:saveButton");
+
+ // Check for the appropriate error messages
+ checkClientAndServerMessages("An invalid value was specified for one "
+ + "or more properties",
+ "Value is required",
+ true);
+ }
+
+ /**
+ * Attempt to create a new datasource but set a property value
+ * beyond its expected range of values. An error should occur.
+ */
+ public void testCreateDatasourcePropertyOutOfRange() throws IOException {
+ Map<String, String> propertiesMap = new HashMap<String, String>();
+ propertiesMap.put("jndi-name", "InvalidDS");
+ propertiesMap.put("user-name", "testUser");
+ propertiesMap.put("password", "password");
+ propertiesMap.put("xa-datasource-class",
"org.postgresql.xa.PGXADataSource");
+ propertiesMap.put("xa-resource-timeout", "36000");
+
+ // This number is too big
+ propertiesMap.put("max-pool-size", "100000000000000");
+
+ createDatasource(XA_DATASOURCE, "default__XA Datasource",
+ propertiesMap);
+ client.click("resourceConfigurationForm:saveButton");
+
+ // Check for the appropriate error messages
+ checkClientAndServerMessages("An invalid value was specified for one or more
properties",
+ "Specified attribute is not between the
expected values",
+ true);
+ }
+
+ /**
+ * Attempt to create a new datasource but set a property value
+ * to an invalid type. An error should occur.
+ */
+ public void testCreateDatasourceInvalidPropertyType() throws IOException {
+ Map<String, String> propertiesMap = new HashMap<String, String>();
+ propertiesMap.put("jndi-name", "InvalidDS");
+ propertiesMap.put("min-pool-size", "10");
+ propertiesMap.put("max-pool-size", "20");
+ propertiesMap.put("driver-class", "org.hsqldb.jdbcDriver");
+ propertiesMap.put("connection-url", "jdbc:hsqldb:.");
+ propertiesMap.put("share-prepared-statements", "false");
+
+ // This property value is supposed to be an integer
+ propertiesMap.put("background-validation-millis", "abcde");
+
+ createDatasource(LOCAL_TX_DATASOURCE,
+ "default__Local TX Datasource",
+ propertiesMap);
+ client.click("resourceConfigurationForm:saveButton");
+
+ // Check for the appropriate error messages
+ checkClientAndServerMessages("An invalid value was specified for one or more
properties",
+ "Value is not a valid integer",
+ true);
+ }
+}
+
Added: trunk/jsfunit/src/test/java/org/jboss/jopr/jsfunit/EmbjoprTestCase.java
===================================================================
--- trunk/jsfunit/src/test/java/org/jboss/jopr/jsfunit/EmbjoprTestCase.java
(rev 0)
+++ trunk/jsfunit/src/test/java/org/jboss/jopr/jsfunit/EmbjoprTestCase.java 2008-12-16
20:13:35 UTC (rev 74)
@@ -0,0 +1,307 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2008, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This 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 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site:
http://www.fsf.org.
+ */
+
+package org.jboss.jopr.jsfunit;
+
+import com.gargoylesoftware.htmlunit.WebClient;
+import com.gargoylesoftware.htmlunit.html.HtmlAnchor;
+import com.gargoylesoftware.htmlunit.html.HtmlButtonInput;
+import com.gargoylesoftware.htmlunit.html.HtmlForm;
+import com.gargoylesoftware.htmlunit.html.HtmlImage;
+import com.gargoylesoftware.htmlunit.html.HtmlTable;
+import com.gargoylesoftware.htmlunit.html.HtmlTableRow;
+import com.gargoylesoftware.htmlunit.html.HtmlTableCell;
+import com.gargoylesoftware.htmlunit.html.HtmlInput;
+import java.io.IOException;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import junit.framework.Test;
+import junit.framework.TestSuite;
+import org.apache.cactus.ServletTestCase;
+import org.jboss.jsfunit.framework.WebClientSpec;
+import org.jboss.jsfunit.jsfsession.JSFClientSession;
+import org.jboss.jsfunit.jsfsession.JSFServerSession;
+import org.jboss.jsfunit.jsfsession.JSFSession;
+import javax.faces.application.FacesMessage;
+
+/**
+ * This is the base test class for Embedded Jopr test cases.
+ * It supplies access to a JSFClientSession object and a JSFServerSession
+ * object. It also provides methods that make it easier to access some
+ * commonly used UI components.
+ *
+ * @author Farah Juma
+ * @author Stan Silvert
+ */
+public class EmbjoprTestCase extends ServletTestCase {
+
+ protected boolean isJBoss4;
+
+ protected JSFClientSession client;
+ protected JSFServerSession server;
+
+ /**
+ * Start a JSFUnit session by logging in to the main page. Note that
+ * because setUp() is called before each test, a new HttpSession will be
+ * created each time a test is run.
+ */
+ public void setUp() throws IOException
+ {
+ isJBoss4 = Package.getPackage("org.jboss.system.server")
+ .getImplementationVersion()
+ .startsWith("4");
+
+ // Initial JSF request
+ WebClientSpec wcSpec = new WebClientSpec("/");
+
+ // This is temporary because embedded Jopr can't find /js/rhq.js
+ wcSpec.getWebClient().setThrowExceptionOnFailingStatusCode(false);
+
+ // Always press OK for confirm dialogs
+ wcSpec.getWebClient().setConfirmHandler(new SimpleConfirmHandler(true));
+
+ wcSpec.setInitialRequestStrategy(new JoprLoginStrategy()); // logs in
+
+ JSFSession jsfSession = new JSFSession(wcSpec);
+ this.client = jsfSession.getJSFClientSession();
+ this.server = jsfSession.getJSFServerSession();
+
+ }
+
+ /**
+ * Need a standard JSFUnit API to replace this code
+ */
+ public HtmlAnchor getNavTreeLink(String linkLabel)
+ {
+ return getLinkInsideForm("navTreeForm", linkLabel);
+ }
+
+ /**
+ * Get the "Delete" button in the resources data table that corresponds
+ * to the resource given by resourceName.
+ */
+ public HtmlButtonInput getDeleteButton(String formId, String resourceName)
+ {
+ HtmlAnchor link = getLinkInsideForm(formId, resourceName);
+ // The id will look like
"resourceSummaryForm:dataTable:2:resourceName"
+ // I need the row number. (2 in the above example)
+ String id = link.getIdAttribute();
+ String[] idElements = id.split(":");
+ String row = idElements[idElements.length - 2];
+ return (HtmlButtonInput)client.getElement(row + ":removeButton");
+ }
+
+ /**
+ * Finds a <a> tag inside a form that has a particular label.
+ */
+ public HtmlAnchor getLinkInsideForm(String formId, String linkLabel)
+ {
+ HtmlForm form = (HtmlForm)client.getElement(formId);
+ List links = form.getByXPath(".//a"); // get all <a> tags inside
form
+
+ for (Iterator i = links.iterator(); i.hasNext();)
+ {
+ HtmlAnchor link = (HtmlAnchor)i.next();
+ String linkText = link.getTextContent();
+ if (linkText.contains(linkLabel)) return link;
+ }
+
+ throw new IllegalStateException("Nav Tree link for '"
+ + linkLabel
+ + "' not found.");
+ }
+
+ /**
+ * 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.
+ */
+ public HtmlImage getNavTreeArrow(String resourceName) {
+ HtmlAnchor link = getNavTreeLink(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("navTreeForm:navTree:".length(), index)
+ + ":handle:img:collapsed";
+ return (HtmlImage)client.getElement(id);
+ }
+
+ /**
+ * Finds the value of the given metric in the given metrics
+ * data table.
+ */
+ public String getMetricValueFromTable(String metricName,
+ String tableId) {
+
+ // Get the metrics data table
+ HtmlTable metricsTable = (HtmlTable)client.getElement(tableId);
+ List<HtmlTableRow> rows = metricsTable.getRows();
+
+ // Find the appropriate metric and return its value
+ for(Iterator i = rows.iterator(); i.hasNext();) {
+
+ HtmlTableRow row = (HtmlTableRow)i.next();
+ HtmlTableCell metric = row.getCell(0);
+
+ if(metric.asText().equals(metricName)) {
+ String id = metric.getIdAttribute();
+
+ // An example id is: dataTable:0:j_id118:2:j_id119
+ String[] idElements = id.split(":");
+ String rowNum = idElements[idElements.length - 2];
+ return ((HtmlTableCell)client.getElement(rowNum
+ +
":measurementValue")).asText();
+
+ }
+ }
+
+ throw new IllegalStateException("Value of '" + metricName
+ + "' not found.");
+ }
+
+ /**
+ * fillOutForm sets the values of input boxes and "Yes/No" radio
+ * buttons on a resource configuration page using the given values.
+ *
+ * @param properties maps property names (eg. "jndi-name",
+ * "min-pool-size", etc.) to property values
+ */
+ public void fillOutForm(Map<String, String> properties) {
+ Iterator itr = properties.keySet().iterator();
+
+ // Set each property
+ while(itr.hasNext()) {
+ String propertyName = (String)itr.next();
+
+ // Enable the input if it is currently disabled
+ HtmlInput input = enableFormInput(propertyName);
+ assertFalse(input.isDisabled());
+
+ setFormInput(input, properties.get(propertyName));
+ }
+ }
+
+ /**
+ * Enable the given input box or "Yes/No" radio button that corresponds
+ * to the given property name on a resource configuration page. If the
+ * input box/radio button is already enabled, do nothing.
+ *
+ * @return the enabled input element
+ */
+ public HtmlInput enableFormInput(String propertyName) {
+ HtmlForm form =
(HtmlForm)client.getElement("resourceConfigurationForm");
+ HtmlInput input =
(HtmlInput)form.getFirstByXPath(".//input[@ondblclick='//"
+ + propertyName + "']");
+
+ boolean isRadioButton = input.getTypeAttribute().equals("radio");
+ String id = input.getId();
+ String xpath;
+
+ if(input.isDisabled()) {
+
+ // Find the enable/disable checkbox corresponding to this
+ // input element.
+ if(isRadioButton) {
+
+ xpath =
".//input[(a)onchange=\"setInputUnset(document.getElementById('"
+ + id
+ + "'),
this.checked);setInputUnset(document.getElementById('"
+ + id.substring(0, id.lastIndexOf(":")) +
":1'), this.checked);\"]";
+ } else {
+ xpath =
".//input[(a)onchange=\"setInputUnset(document.getElementById('"
+ + id + "'), this.checked);\"]";
+ }
+
+ HtmlInput checkBox = (HtmlInput)form.getFirstByXPath(xpath);
+ checkBox.setChecked(Boolean.FALSE);
+ input =
(HtmlInput)form.getFirstByXPath(".//input[@ondblclick='//"
+ + propertyName + "']");
+ }
+
+ return input;
+ }
+
+ /**
+ * Set the given input box or "Yes/No" radio button to the given value.
+ */
+ public void setFormInput(HtmlInput input, String propertyValue) {
+ boolean isRadioButton = input.getTypeAttribute().equals("radio");
+ String id = input.getId();
+
+ if(isRadioButton) {
+
+ // Check the appropriate button
+ if(propertyValue.equals("false")) {
+
+ // Get the "No" radio button
+ input = (HtmlInput)client.getElement(id.substring(0,
+ id.lastIndexOf(":"))
+ + ":1");
+ }
+
+ input.setChecked(Boolean.TRUE);
+ } else {
+ input.setValueAttribute(propertyValue);
+ }
+ }
+
+
+ /**
+ * Check that the given messages occur on the client side
+ * and server side.
+ */
+ public void checkClientAndServerMessages(String expectedClientMsg,
+ String expectedServerMsg,
+ boolean isErrorMsg) {
+ assertTrue(client.getPageAsText().contains(expectedClientMsg));
+
+ assertTrue(server.getFacesMessages().hasNext());
+ FacesMessage message = server.getFacesMessages().next();
+
+ if(isErrorMsg) {
+ assertTrue(FacesMessage.SEVERITY_ERROR.equals(message.getSeverity()));
+ } else {
+ assertTrue(FacesMessage.SEVERITY_INFO.equals(message.getSeverity()));
+ }
+
+ assertTrue(message.getDetail().contains(expectedServerMsg));
+ }
+
+ /**
+ * This just checks that the JSFServerSession object was created.
+ */
+ public void testServerSessionCreated() {
+ assertTrue(server != null);
+ }
+
+ /**
+ * This just checks that the JSFClientSession object was created.
+ */
+ public void testClientSessionCreated() {
+ assertTrue(client != null);
+ }
+}
+