Commits

Vineet Reynolds committed b4fff51

Added Selenium based integration tests that use Arquillian Drone and a remote Glassfish instance for the Index, Login and Signup pages.

Modified the defaultLayout template to rename the wrapper div. Modified the privateLayout template to include element Ids for the form enclosing the AccountPreferences and Logout links. Added Ids in other pages as necessary.

  • Participants
  • Parent commits 419f16d

Comments (0)

Files changed (16)

File galleria-jsf/pom.xml

-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
-  <modelVersion>4.0.0</modelVersion>
-  <parent>
-    <artifactId>info.galleria</artifactId>
-    <groupId>name.reynolds.vineet</groupId>
-    <version>0.0.1-SNAPSHOT</version>
-    <relativePath>..</relativePath>
-  </parent>
-  <artifactId>galleria-jsf</artifactId>
-  <packaging>war</packaging>
-  <description>The web module containing JSF facelets and managed beans.</description>
-  <dependencies>
-  	<dependency>
-  		<groupId>name.reynolds.vineet</groupId>
-  		<artifactId>galleria-ejb</artifactId>
-  		<type>jar</type>
-  		<version>0.0.1-SNAPSHOT</version>
-  		<scope>provided</scope>
-  	</dependency>
-  	<dependency>
-  		<groupId>org.slf4j</groupId>
-  		<artifactId>slf4j-api</artifactId>
-  		<version>1.6.1</version>
-  		<scope>provided</scope>
-  	</dependency>
-  	<dependency>
-  		<groupId>org.primefaces</groupId>
-  		<artifactId>primefaces</artifactId>
-  		<version>3.0.M2</version>
-  	</dependency>
-  	<dependency>
-  		<!-- For use with the PrimeFaces file upload component -->
-  		<groupId>commons-io</groupId>
-  		<artifactId>commons-io</artifactId>
-  		<version>1.4</version>
-  	</dependency>
-  	<dependency>
-  		<!-- For use with the PrimeFaces file upload component -->
-  		<groupId>commons-fileupload</groupId>
-  		<artifactId>commons-fileupload</artifactId>
-  		<version>1.2.1</version>
-  	</dependency>
-  </dependencies>
-  <repositories>
-  	<repository>
-  		<id>prime-repo</id>
-		<name>Prime Technology Maven Repository</name>
-		<url>http://repository.prime.com.tr</url>
-		<layout>default</layout>
-  	</repository>
-  </repositories>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+	<modelVersion>4.0.0</modelVersion>
+	<parent>
+		<artifactId>info.galleria</artifactId>
+		<groupId>name.reynolds.vineet</groupId>
+		<version>0.0.1-SNAPSHOT</version>
+		<relativePath>..</relativePath>
+	</parent>
+	<artifactId>galleria-jsf</artifactId>
+	<packaging>war</packaging>
+	<description>The web module containing JSF facelets and managed beans.</description>
+	<properties>
+		<arquillian.version>1.0.0.CR4</arquillian.version>
+		<arquillian.glassfish-remote.version>1.0.0.CR1</arquillian.glassfish-remote.version>
+		<arquillian.drone.version>1.0.0.CR1</arquillian.drone.version>
+		<arquillian.jacoco.version>1.0.0.Alpha1</arquillian.jacoco.version>
+		<selenium.version>2.5.0</selenium.version>
+		<jacoco.version>0.5.3.201107060350</jacoco.version>
+		<jacoco.file.path>${project.build.directory}/jacoco.exec</jacoco.file.path>
+		<sonar.jacoco.itReportPath>${project.build.directory}/jacoco.exec</sonar.jacoco.itReportPath>
+		<sonar.phase>integration-test</sonar.phase>
+		<sonar.dynamicAnalysis>reuseReports</sonar.dynamicAnalysis>
+	</properties>
+	<dependencies>
+		<dependency>
+			<groupId>name.reynolds.vineet</groupId>
+			<artifactId>galleria-ejb</artifactId>
+			<type>jar</type>
+			<version>0.0.1-SNAPSHOT</version>
+			<scope>provided</scope>
+		</dependency>
+		<dependency>
+			<groupId>org.slf4j</groupId>
+			<artifactId>slf4j-api</artifactId>
+			<version>1.5.6</version>
+			<scope>provided</scope>
+		</dependency>
+		<dependency>
+			<groupId>org.slf4j</groupId>
+			<artifactId>slf4j-jdk14</artifactId>
+			<version>1.5.6</version>
+			<scope>provided</scope>
+		</dependency>
+		<dependency>
+			<groupId>org.primefaces</groupId>
+			<artifactId>primefaces</artifactId>
+			<version>3.0.M2</version>
+		</dependency>
+		<dependency>
+			<!-- For use with the PrimeFaces file upload component -->
+			<groupId>commons-io</groupId>
+			<artifactId>commons-io</artifactId>
+			<version>1.4</version>
+		</dependency>
+		<dependency>
+			<!-- For use with the PrimeFaces file upload component -->
+			<groupId>commons-fileupload</groupId>
+			<artifactId>commons-fileupload</artifactId>
+			<version>1.2.1</version>
+		</dependency>
+		<!-- Dependencies for Arquillian. Version is from Arquillian BOM -->
+		<dependency>
+			<groupId>org.jboss.arquillian.junit</groupId>
+			<artifactId>arquillian-junit-container</artifactId>
+			<scope>test</scope>
+		</dependency>
+		<!-- Add dependencies to shrinkwrap-resolver APIs from the Arquillian BOM. 
+			Version is declared in BOM. -->
+		<dependency>
+			<groupId>org.jboss.shrinkwrap.resolver</groupId>
+			<artifactId>shrinkwrap-resolver-api</artifactId>
+			<scope>test</scope>
+		</dependency>
+		<dependency>
+			<groupId>org.jboss.shrinkwrap.resolver</groupId>
+			<artifactId>shrinkwrap-resolver-api-maven</artifactId>
+			<scope>test</scope>
+		</dependency>
+		<dependency>
+			<groupId>org.jboss.shrinkwrap.resolver</groupId>
+			<artifactId>shrinkwrap-resolver-impl-maven</artifactId>
+			<scope>test</scope>
+		</dependency>
+		<!-- Dependencies for Glassfish support in Arquillian -->
+		<dependency>
+			<groupId>org.jboss.arquillian.container</groupId>
+			<artifactId>arquillian-glassfish-remote-3.1</artifactId>
+			<version>${arquillian.glassfish-remote.version}</version>
+			<scope>test</scope>
+		</dependency>
+		<dependency>
+			<!-- Used to prevent java.lang.ClassFormatError: "Absent Code attribute 
+				in method that is not native or abstract in class file javax/mail/internet/ParseException" 
+				arising out of usage of javaee-6.0-api -->
+			<groupId>javax.mail</groupId>
+			<artifactId>mail</artifactId>
+			<version>1.4.4</version>
+		</dependency>
+		<!-- Arquillian Drone dependency. Starting with 1.0.0.CR1, the drone-impl 
+			and drone-webdriver dependencies are being imported as stated in the migration 
+			notes. -->
+		<dependency>
+		   <groupId>org.jboss.arquillian.extension</groupId>
+		   <artifactId>arquillian-drone-impl</artifactId>
+		   <version>${arquillian.drone.version}</version>
+		   <scope>test</scope>
+		</dependency>
+		<dependency>
+		   <groupId>org.jboss.arquillian.extension</groupId>
+		   <artifactId>arquillian-drone-webdriver</artifactId>
+		   <version>${arquillian.drone.version}</version>
+		   <scope>test</scope>
+		</dependency>
+		<!-- Selenium 2.5.0 to support Firefox 6.0 -->
+		<dependency>
+			<groupId>org.seleniumhq.selenium</groupId>
+			<artifactId>selenium-java</artifactId>
+			<version>${selenium.version}</version>
+			<scope>test</scope>
+		</dependency>
+		<dependency>
+			<groupId>org.seleniumhq.selenium</groupId>
+			<artifactId>selenium-server</artifactId>
+			<version>${selenium.version}</version>
+			<scope>test</scope>
+			<exclusions>
+				<exclusion>
+					<artifactId>servlet-api-2.5</artifactId>
+					<groupId>org.mortbay.jetty</groupId>
+				</exclusion>
+			</exclusions>
+		</dependency>
+		<!-- Dependencies for DbUnit --> 
+		<dependency>
+			<groupId>org.dbunit</groupId>
+			<artifactId>dbunit</artifactId>
+			<version>2.4.8</version>
+			<scope>test</scope>
+		</dependency>
+		<dependency>
+			<groupId>commons-collections</groupId>
+			<artifactId>commons-collections</artifactId>
+			<version>3.2.1</version>
+			<scope>test</scope>
+		</dependency>
+		<dependency>
+			<groupId>org.apache.derby</groupId>
+			<artifactId>derbyclient</artifactId>
+			<version>10.8.1.2</version>
+			<scope>test</scope>
+		</dependency>
+		<!-- Dependencies for code coverage in Arquillian using JaCoCo -->
+		<dependency>
+			<groupId>org.jboss.arquillian.protocol</groupId>
+			<artifactId>arquillian-protocol-servlet</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>org.jboss.arquillian.extension</groupId>
+			<artifactId>arquillian-jacoco</artifactId>
+			<scope>test</scope>
+			<version>${arquillian.jacoco.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>org.jacoco</groupId>
+			<artifactId>org.jacoco.core</artifactId>
+			<version>${jacoco.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>org.slf4j</groupId>
+			<artifactId>slf4j-simple</artifactId>
+			<version>1.5.6</version>
+			<scope>provided</scope>
+		</dependency>
+	</dependencies>
+	<build>		
+		<plugins>
+			<plugin>
+				<artifactId>maven-surefire-plugin</artifactId>
+				<version>2.7.1</version>
+				<configuration>
+					<excludes>
+						<exclude>**/*IntegrationSuite.java</exclude>
+						<exclude>**/*IntegrationTest.java</exclude>
+						<exclude>**/*ITCase.java</exclude>
+					</excludes>
+				</configuration>
+			</plugin>
+			<plugin>
+				<artifactId>maven-failsafe-plugin</artifactId>
+				<version>2.9</version>
+				<configuration>
+					<includes>
+						<!-- Used to include IntegrationSuite and ITCase classes during integration testing; 
+							avoiding the use of the default naming convention. Includes only suite classes 
+							for now as the startup of the embedded Glassfish container is CPU and disk 
+							intensive. -->
+						<include>**/*IntegrationSuite.java</include>
+						<include>**/*IntegrationTest.java</include>
+						<include>**/*ITCase.java</include>
+					</includes>
+				</configuration>
+				<executions>
+					<execution>
+						<goals>
+							<goal>integration-test</goal>
+							<goal>verify</goal>
+						</goals>
+					</execution>
+				</executions>
+			</plugin>
+			<plugin>
+				<groupId>org.glassfish.maven.plugin</groupId>
+				<artifactId>maven-glassfish-plugin</artifactId>
+				<version>2.1</version>
+				<configuration>
+					<user>admin</user>
+					<passwordFile>C:/glassfish3/glassfish/domains/domain1/config/local-password</passwordFile>
+					<glassfishDirectory>C:/glassfish3/glassfish/</glassfishDirectory>
+					<domain>
+						<name>domain1</name>
+						<adminPort>4848</adminPort>
+						<httpPort>8080</httpPort>
+						<httpsPort>8443</httpsPort>
+						<jvmOptions>
+                            <option>-Xdebug</option>
+                        </jvmOptions>
+					</domain>
+					<debug>true</debug>
+					<echo>true</echo>
+					<terse>false</terse>
+					<autoCreate>false</autoCreate>
+				</configuration>
+				<executions>
+					<execution>
+						<id>start-glassfish</id>
+						<phase>pre-integration-test</phase>
+						<goals>
+							<goal>start-domain</goal>
+						</goals>
+					</execution>
+					<execution>
+						<id>stop-glassfish</id>
+						<phase>post-integration-test</phase>
+						<goals>
+							<goal>stop-domain</goal>
+						</goals>
+					</execution>
+				</executions>
+			</plugin>
+		</plugins>
+	</build>
+	<repositories>
+		<repository>
+			<id>prime-repo</id>
+			<name>Prime Technology Maven Repository</name>
+			<url>http://repository.prime.com.tr</url>
+			<layout>default</layout>
+		</repository>
+		<repository>
+			<id>jboss-public-repository-group</id>
+			<name>JBoss Public Maven Repository Group</name>
+			<url>https://repository.jboss.org/nexus/content/groups/public-jboss/</url>
+			<layout>default</layout>
+			<releases>
+				<enabled>true</enabled>
+				<updatePolicy>never</updatePolicy>
+			</releases>
+			<snapshots>
+				<enabled>true</enabled>
+				<updatePolicy>never</updatePolicy>
+			</snapshots>
+		</repository>
+		<repository>
+			<id>jboss-releases-repository-group</id>
+			<name>JBoss Releases Maven Repository Group</name>
+			<url>https://repository.jboss.org/nexus/content/repositories/releases/</url>
+			<layout>default</layout>
+			<releases>
+				<enabled>true</enabled>
+				<updatePolicy>never</updatePolicy>
+			</releases>
+			<snapshots>
+				<enabled>false</enabled>
+				<updatePolicy>never</updatePolicy>
+			</snapshots>
+		</repository>
+	</repositories>
+	<profiles>
+		<profile>
+			<id>arquillian-glassfish-embedded-3.1</id>
+			<dependencies>
+				<dependency>
+					<groupId>org.jboss.arquillian.container</groupId>
+					<artifactId>arquillian-glassfish-embedded-3.1</artifactId>
+					<version>${arquillian.version}</version>
+					<scope>test</scope>
+				</dependency>
+				<dependency>
+					<groupId>org.glassfish.extras</groupId>
+					<artifactId>glassfish-embedded-all</artifactId>
+					<version>3.1</version>
+					<scope>test</scope>
+				</dependency>
+			</dependencies>
+		</profile>
+	</profiles>
+	<dependencyManagement>
+		<dependencies>
+			<!-- Dependencies for Arquillian -->
+			<dependency>
+				<groupId>org.jboss.arquillian</groupId>
+				<artifactId>arquillian-bom</artifactId>
+				<version>${arquillian.version}</version>
+				<type>pom</type>
+				<scope>import</scope>
+			</dependency>
+		</dependencies>
+	</dependencyManagement>
 </project>

File galleria-jsf/src/main/resources/resources/messages_en.properties

 # -- Index --
-Index.title=Galleria
+Index.title=Welcome to Galleria
 Index.heading=Welcome\!
 Index.message=This is Galleria. It is an image gallery where you can upload images.
 Index.login.message=Login

File galleria-jsf/src/main/webapp/Index.xhtml

 			<p>
 				<h:outputText value="#{msg['Index.message']}" />
 			</p>
-			<h:outputLink value="Login.xhtml">
-				<h:outputText id="login" value="#{msg['Index.login.message']}" />
+			<h:outputLink id="login" value="Login.xhtml">
+				<h:outputText value="#{msg['Index.login.message']}" />
 			</h:outputLink>
 			<h:outputText value="#{msg['Index.orMessage']}" />
-			<h:outputLink value="Signup.xhtml">
-				<h:outputText id="signup" value="#{msg['Index.signup.message']}" />
+			<h:outputLink id="signup" value="Signup.xhtml">
+				<h:outputText value="#{msg['Index.signup.message']}" />
 			</h:outputLink>
 			<h:messages id="messages" globalOnly="true" infoClass="infoStyle"
 				warnClass="warnStyle" errorClass="errorStyle" />

File galleria-jsf/src/main/webapp/Signup.xhtml

 		<h3>
 			<h:outputText value="#{msg['Signup.message']}" />
 		</h3>
-		<h:form acceptcharset="UTF-8" styleClass="form">
+		<h:form id="signupForm" acceptcharset="UTF-8" styleClass="form">
 			<ul>
 				<li>
 					<h:outputLabel for="userId" value="#{msg['Signup.emailId.label']}" />
 				</li>
 			</ul>
 		</h:form>
-		<h:messages showSummary="true" showDetail="false" infoClass="infoStyle"
+		<h:messages id="messages" showSummary="true" showDetail="false" infoClass="infoStyle"
 			warnClass="warnStyle" errorClass="errorStyle" />
 	</ui:define>
 </ui:composition>

File galleria-jsf/src/main/webapp/resources/styles/all.css

 	padding-left: 10px;
 }
 
+#publicWrapper {
+	/* The main content should stretch to fit the full window height. Prevents footer from floating upwards.*/
+	height: auto;
+	min-height: 100%;
+}
+
 #wrapper {
 	/* The main content should stretch to fit the full window height. Prevents footer from floating upwards.*/
 	height: auto;

File galleria-jsf/src/main/webapp/templates/defaultLayout.xhtml

 
 <h:body>
 
-	<div id="wrapper">
+	<div id="publicWrapper">
 	
 		<div id="header">
 			<ui:insert name="header">

File galleria-jsf/src/main/webapp/templates/privateLayout.xhtml

 			</ui:insert>
 			
 			<div id="headerLinks">
-				<h:form styleClass="headerLinkContent">
+				<h:form id="commonActions" styleClass="headerLinkContent">
 					<h:commandLink id="logout" action="#{authenticator.logout}">
 						<h:outputText value="#{msg['Application.logout.label']}" />
 					</h:commandLink>

File galleria-jsf/src/test/java/info/galleria/view/user/AllPagesIntegrationTest.java

+package info.galleria.view.user;
+
+import static org.junit.Assert.*;
+
+import java.io.File;
+import java.net.URI;
+import java.sql.*;
+
+import org.dbunit.DatabaseUnitException;
+import org.dbunit.database.*;
+import org.dbunit.dataset.*;
+import org.dbunit.dataset.filter.SequenceTableFilter;
+import org.dbunit.dataset.xml.FlatXmlDataSetBuilder;
+import org.dbunit.operation.DatabaseOperation;
+import org.jboss.arquillian.container.test.api.*;
+import org.jboss.arquillian.core.api.annotation.Observes;
+import org.jboss.arquillian.drone.api.annotation.Drone;
+import org.jboss.arquillian.junit.Arquillian;
+import org.jboss.arquillian.test.api.ArquillianResource;
+import org.jboss.arquillian.test.spi.event.suite.*;
+import org.jboss.shrinkwrap.api.ShrinkWrap;
+import org.jboss.shrinkwrap.api.spec.WebArchive;
+import org.jboss.shrinkwrap.resolver.api.DependencyResolvers;
+import org.jboss.shrinkwrap.resolver.api.maven.MavenDependencyResolver;
+import org.junit.*;
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.rules.TestName;
+import org.junit.runner.RunWith;
+import org.openqa.selenium.*;
+import org.openqa.selenium.support.ui.*;
+import org.slf4j.*;
+
+@RunWith(Arquillian.class)
+@RunAsClient
+public class AllPagesIntegrationTest
+{
+
+	private static final Logger logger = LoggerFactory.getLogger(AllPagesIntegrationTest.class);
+
+	@Rule
+	public TestName testMethod = new TestName();
+	
+	@Drone
+	protected WebDriver driver;
+	
+	@ArquillianResource
+	protected URI contextPath;
+
+	@Deployment
+	public static WebArchive createDeployment()
+	{
+		logger.info("Preparing deployment.");
+		// A separate EJB archive is not being created unless Arquillian can
+		// create EnterpriseArchives that can be deployed on Glassfish.
+		// Also, when this EJB archive is added to the WebArchive, the EJB
+		// classloader will fail to load classes required, but located in
+		// WEB-INF/lib, like the Commons-Codec class.
+		//
+		// JavaArchive ejbArchive = ShrinkWrap.create(JavaArchive.class,
+		// "galleria-ejb.jar")
+		// .addPackage("info.galleria.domain")
+		// .addPackages(true, "info.galleria.service")
+		// .addPackage("info.galleria.utilities")
+		// .addAsManifestResource("ValidationMessages.properties")
+		// .addAsManifestResource("ValidationMessages_de.properties")
+		// .addAsManifestResource("META-INF/ejb-jar.xml","ejb-jar.xml")
+		// .addAsManifestResource("META-INF/glassfish-ejb-jar.xml","glassfish-ejb-jar.xml")
+		// .addAsManifestResource("META-INF/persistence.xml","persistence.xml");
+		// System.out.println("******Contents of EJB Archive******");
+		// System.out.println(ejbArchive.toString(true));
+	
+		WebArchive webArchive = ShrinkWrap
+				.create(WebArchive.class, "galleria-jsf.war")
+				.setWebXML(new File("src/main/webapp/WEB-INF/web.xml"))
+				.addPackage("info.galleria.converters")
+				.addPackage("info.galleria.filters")
+				.addPackage("info.galleria.i18n")
+				.addPackage("info.galleria.listeners")
+				.addPackages(true, "info.galleria.view")
+				.addAsResource("resources/messages_en.properties", "resources/messages_en.properties")
+				.addAsResource("resources/messages_de.properties", "resources/messages_de.properties")
+				.addAsResource("resources/StandardIcon.png", "resources/StandardIcon.png")
+				.addAsWebResource(new File("src/main/webapp/templates", "content.xhtml"), "templates/content.xhtml")
+				.addAsWebResource(new File("src/main/webapp/templates", "defaultLayout.xhtml"), "templates/defaultLayout.xhtml")
+				.addAsWebResource(new File("src/main/webapp/templates", "footer.xhtml"), "templates/footer.xhtml")
+				.addAsWebResource(new File("src/main/webapp/templates", "header.xhtml"), "templates/header.xhtml")
+				.addAsWebResource(new File("src/main/webapp/templates", "privateLayout.xhtml"), "templates/privateLayout.xhtml")
+				.addAsWebResource(new File("src/main/webapp/resources/styles", "all.css"), "resources/styles/all.css")
+				.addAsWebResource(new File("src/main/webapp", "Index.xhtml"))
+				.addAsWebResource(new File("src/main/webapp", "Signup.xhtml"))
+				.addAsWebResource(new File("src/main/webapp", "Login.xhtml"))
+				.addAsWebResource(new File("src/main/webapp/private", "Application.xhtml"), "private/Application.xhtml")
+				.addAsWebResource(new File("src/main/webapp/private/user", "AccountPreferences.xhtml"),	"private/user/AccountPreferences.xhtml")
+				.addAsWebResource(new File("src/main/webapp/private/album", "CreateAlbum.xhtml"), "private/album/CreateAlbum.xhtml")
+				.addAsWebResource(new File("src/main/webapp/private/album", "EditAlbum.xhtml"),	"private/album/EditAlbum.xhtml")
+				.addAsWebResource(new File("src/main/webapp/private/album", "ViewAlbum.xhtml"), "private/album/ViewAlbum.xhtml")
+				.addAsWebResource(new File("src/main/webapp/private/photo", "UploadPhoto.xhtml"), "private/photo/UploadPhoto.xhtml")
+				.addAsWebResource(new File("src/main/webapp/private/photo", "EditPhoto.xhtml"),	"private/photo/EditPhoto.xhtml")
+				.addAsWebResource(new File("src/main/webapp/private/photo", "ViewPhoto.xhtml"),	"private/photo/ViewPhoto.xhtml")
+				.addAsWebInfResource(new File("src/main/webapp/WEB-INF/faces-config.xml"), "faces-config.xml")
+				/* Add glassfish-web.xml as glassfish-ejb-jar.xml will be ignored in the Web archive deployment */
+				.addAsWebInfResource(new File("src/test/resources/glassfish-web.xml"), "glassfish-web.xml")
+				/* .addAsWebInfResource(EmptyAsset.INSTANCE, "beans.xml") */
+				.addAsLibraries(
+						DependencyResolvers.use(MavenDependencyResolver.class)
+								.loadMetadataFromPom("pom.xml")
+								.artifacts("org.primefaces:primefaces:3.0.M2",
+										"commons-io:commons-io:1.4",
+										"commons-fileupload:commons-fileupload:1.2.1",
+										"commons-codec:commons-codec:1.5").resolveAsFiles())
+				.addPackage("info.galleria.domain")
+				.addPackages(true, "info.galleria.service")
+				.addPackage("info.galleria.utilities")
+				.addAsResource("ValidationMessages.properties", "ValidationMessages.properties")
+				.addAsResource("ValidationMessages_de.properties", "ValidationMessages_de.properties")
+				.addAsResource("META-INF/ejb-jar.xml", "ejb-jar.xml")
+				.addAsResource("META-INF/glassfish-ejb-jar.xml", "glassfish-ejb-jar.xml")
+				.addAsResource("META-INF/persistence.xml", "META-INF/persistence.xml");
+		System.out.println("******Contents of WAR Archive******");
+		System.out.println(webArchive.toString(true));
+	
+		//
+		// Don't use Enterprise archives yet, as bug ARQ-527 is not yet resolved
+		// at the time of writing this test.
+		//
+		// EnterpriseArchive enterpriseArchive =
+		// ShrinkWrap.create(EnterpriseArchive.class,"galleria.ear")
+		// .addAsModule(webArchive)
+		// .addAsModule(ejbArchive)
+		// .addAsLibraries(DependencyResolvers.use(MavenDependencyResolver.class).artifact("org.hibernate:hibernate-entitymanager:3.6.5.Final").exclusions("org.hibernate:hibernate-validator","org.hibernate:hibernate-validator","org.hibernate.javax.persistence:hibernate-jpa-2.0-api","javax.transaction:jta","org.slf4j:slf4j-api:1.6.1").resolveAsFiles());
+		// System.out.println("******Contents of EAR Archive******");
+		// System.out.println(enterpriseArchive.toString(true));
+	
+		return webArchive;
+	}
+
+	public void startCoverage(@Observes BeforeSuite event)
+	{
+		logger.info("Before Suite");
+	}
+
+	public void stopCoverage(@Observes AfterSuite event)
+	{
+		logger.info("After Suite");
+	}
+
+	@BeforeClass
+	public static void beforeClass()
+	{
+		logger.info("Before Class");
+	}
+
+	@AfterClass
+	public static void afterClass()
+	{
+		logger.info("After Class");
+	}
+
+	/**
+	 * Resets the contents of the database tables before every test using
+	 * DbUnit.
+	 * 
+	 * @throws Exception
+	 */
+	@Before
+	public void setUp()
+	{
+		logger.info("Performing setup before Test {}", testMethod.getMethodName());
+		IDatabaseConnection connection = null;
+		try
+		{
+			connection = getConnection();
+			IDataSet dataSet = getDataSet();
+			// The FilteredDataSet and the SequenceTableFilter is used to
+			// reorder the DELETE operations to prevent failures due to circular
+			// dependencies
+			// between the ALBUMS and PHOTOS tables. The plain XML file does not
+			// contain this information. The DatabaseSequenceFilter class is
+			// avoided as it cannot handle circular dependencies.
+			String[] orderedTableNames = new String[] { "USERS_GROUPS", "USERS", "GROUPS", "ALBUMS", "PHOTOS" };
+			IDataSet filteredDataSet = new FilteredDataSet(new SequenceTableFilter(orderedTableNames), dataSet);
+			DatabaseOperation.CLEAN_INSERT.execute(connection, filteredDataSet);
+		}
+		catch(Exception ex)
+		{
+			ex.printStackTrace();
+		}
+		finally
+		{
+			try
+			{
+				connection.close();
+			}
+			catch (SQLException sqlEx)
+			{
+				logger.warn(sqlEx.getMessage(), sqlEx);
+			}
+		}
+	}
+
+	/**
+	 * Logout the user after every test. This will prevent subsequent tests from
+	 * failing, as the UserRedirectFilter will redirect the Firefox driver to
+	 * the home page instead of the index, login or signup pages.
+	 */
+	@After
+	public void tearDown()
+	{
+		WebElement logoutLink = null;
+		try
+		{
+			logoutLink = driver.findElement(By.id("commonActions:logout"));
+		}
+		catch (NoSuchElementException noElemEx)
+		{
+			// do nothing if the logout link is not present.
+		}
+		if (logoutLink != null)
+		{
+			logoutLink.click();
+			Wait<WebDriver> wait = new WebDriverWait(driver, 15);
+			wait.until(PageUtilities.visibilityOfElementLocated(By.id("publicWrapper")));
+		}
+	}
+
+	private IDatabaseConnection getConnection() throws ClassNotFoundException, SQLException, DatabaseUnitException
+	{
+		Class.forName("org.apache.derby.jdbc.ClientDriver");
+		Connection jdbcConnection = DriverManager.getConnection("jdbc:derby://localhost:1527/GALLERIATEST", "APP", "GALLERIA");
+		IDatabaseConnection databaseConnection = new DatabaseConnection(jdbcConnection);
+		return databaseConnection;
+	}
+
+	/**
+	 * Returns a dataset containing blanked out tables and table-based sequences
+	 * set to 0.
+	 * 
+	 * @return
+	 * @throws Exception
+	 */
+	private IDataSet getDataSet() throws Exception
+	{
+		ClassLoader classLoader = this.getClass().getClassLoader();
+		return new FlatXmlDataSetBuilder().build(classLoader.getResourceAsStream("database-test-setup.xml"));
+	}
+
+	@Test
+	public void testSignupUser() throws Exception
+	{
+		driver.get(contextPath.toString());
+		IndexPage indexPage = new IndexPage(driver, contextPath);
+		SignupPage signupPage = indexPage.chooseToSignup();
+		indexPage = signupPage.signupAs("User#1", "password");
+		String message = "The new user account has been created. You may now login.";
+		assertEquals(message, indexPage.fetchSignupSuccessMessage());
+	}
+
+	@Test
+	public void testSignupUserWithNoValues() throws Exception
+	{
+		driver.get(contextPath.toString());
+		IndexPage indexPage = new IndexPage(driver, contextPath);
+		SignupPage signupPage = indexPage.chooseToSignup();
+		signupPage = signupPage.signupAsExpectingError("", "", "");
+		String[] errorMessages = { "The user Id should be between 1 and 50 characters in length.",
+				"The password should be between 1 and 500 characters in length.",
+				"The confirmed password should be between 1 and 500 characters in length." };
+		assertArrayEquals(errorMessages, signupPage.fetchSignupErrorMessages());
+	}
+
+	@Test
+	public void testSignupUserWithExcessiveValues() throws Exception
+	{
+		driver.get(contextPath.toString());
+		IndexPage indexPage = new IndexPage(driver, contextPath);
+		SignupPage signupPage = indexPage.chooseToSignup();
+		signupPage = signupPage
+				.signupAsExpectingError(
+						"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
+						"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
+						"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa");
+		String[] errorMessages = { "The user Id should be between 1 and 50 characters in length.",
+				"The password should be between 1 and 500 characters in length.",
+				"The confirmed password should be between 1 and 500 characters in length." };
+		assertArrayEquals(errorMessages, signupPage.fetchSignupErrorMessages());
+	}
+
+	@Test
+	public void testSignupUserWithMismatchedPasswords() throws Exception
+	{
+		driver.get(contextPath.toString());
+		IndexPage indexPage = new IndexPage(driver, contextPath);
+		SignupPage signupPage = indexPage.chooseToSignup();
+		signupPage = signupPage.signupAsExpectingError("User#1", "password", "mismatchpassword");
+		String[] errorMessages = { "The entered passwords do not match. Please correct the passwords before retrying." };
+		assertArrayEquals(errorMessages, signupPage.fetchSignupErrorMessages());
+	}
+
+	@Test
+	public void testSignupDuplicateUsers() throws Exception
+	{
+		driver.get(contextPath.toString());
+		IndexPage indexPage = new IndexPage(driver, contextPath);
+		SignupPage signupPage = indexPage.chooseToSignup();
+		indexPage = signupPage.signupAs("User#1", "password");
+		String message = "The new user account has been created. You may now login.";
+		assertEquals(message, indexPage.fetchSignupSuccessMessage());
+		signupPage = indexPage.chooseToSignup();
+		signupPage = signupPage.signupAsExpectingError("User#1", "password", "password");
+		String[] errorMessages = { "The user account Id is already taken. Please choose another." };
+		assertArrayEquals(errorMessages, signupPage.fetchSignupErrorMessages());
+	}
+	
+	@Test
+	public void testLoginUserInvalidCredentials() throws Exception
+	{
+		driver.get(contextPath.toString());
+		IndexPage indexPage = new IndexPage(driver, contextPath);
+		SignupPage signupPage = indexPage.chooseToSignup();
+		indexPage = signupPage.signupAs("User#1", "password");
+		String message = "The new user account has been created. You may now login.";
+		assertEquals(message, indexPage.fetchSignupSuccessMessage());
+
+		LoginPage loginPage = indexPage.chooseToLogin();
+		loginPage.loginAsExpectingError("User#2", "password");
+		String[] errorMessages = { "The user name or password is incorrect." };
+		assertArrayEquals(errorMessages, loginPage.getLoginErrorMessagesDisplayed());
+	}
+
+	@Test
+	public void testLoginUser() throws Exception
+	{
+		driver.get(contextPath.toString());
+		IndexPage indexPage = new IndexPage(driver, contextPath);
+		SignupPage signupPage = indexPage.chooseToSignup();
+		indexPage = signupPage.signupAs("User#1", "password");
+		String message = "The new user account has been created. You may now login.";
+		assertEquals(message, indexPage.fetchSignupSuccessMessage());
+
+		LoginPage loginPage = indexPage.chooseToLogin();
+		loginPage.loginAs("User#1", "password");
+		assertTrue(driver.findElement(By.id("commonActions:AccountPreferences")) != null);
+		assertTrue(driver.findElement(By.id("commonActions:logout")) != null);
+	}
+
+	@Test
+	public void testLogoutUser() throws Exception
+	{
+		driver.get(contextPath.toString());
+		IndexPage indexPage = new IndexPage(driver, contextPath);
+		SignupPage signupPage = indexPage.chooseToSignup();
+		indexPage = signupPage.signupAs("User#1", "password");
+		String message = "The new user account has been created. You may now login.";
+		assertEquals(message, indexPage.fetchSignupSuccessMessage());
+
+		LoginPage loginPage = indexPage.chooseToLogin();
+		HomePage homePage = loginPage.loginAs("User#1", "password");
+		homePage.logout();
+	}
+
+}

File galleria-jsf/src/test/java/info/galleria/view/user/HomePage.java

+package info.galleria.view.user;
+
+import java.net.URI;
+
+import org.openqa.selenium.*;
+import org.openqa.selenium.support.ui.*;
+
+public class HomePage
+{
+
+	private WebDriver driver;
+	private URI contextPath;
+
+	public HomePage(WebDriver driver, URI contextPath)
+	{
+		this.driver = driver;
+		this.contextPath = contextPath;
+		Wait<WebDriver> wait = new WebDriverWait(driver, 15);
+		wait.until(PageUtilities.visibilityOfElementLocated(By.id("wrapper")));
+		if (!driver.getTitle().equals("Galleria"))
+		{
+			throw new IllegalStateException("This is not the home page.");
+		}
+	}
+
+	public LoginPage logout()
+	{
+		driver.findElement(By.id("commonActions:logout")).click();
+		return new LoginPage(driver, contextPath);
+	}
+
+}

File galleria-jsf/src/test/java/info/galleria/view/user/IndexPage.java

+package info.galleria.view.user;
+
+import java.net.URI;
+
+import org.openqa.selenium.*;
+import org.openqa.selenium.support.ui.*;
+
+public class IndexPage
+{
+	private WebDriver driver;
+
+	private URI contextPath;
+
+	private static final String PAGE_FILE_NAME = "Index.xhtml";
+
+	public IndexPage(WebDriver driver, URI contextPath)
+	{
+		this.driver = driver;
+		this.contextPath = contextPath;
+		Wait<WebDriver> wait = new WebDriverWait(driver, 15);
+		wait.until(PageUtilities.visibilityOfElementLocated(By.id("publicWrapper")));
+		if (!driver.getCurrentUrl().equals(contextPath.toString())
+				&& !driver.getCurrentUrl().equals(contextPath + PAGE_FILE_NAME))
+		{
+			throw new IllegalStateException("This is not the Index page.");
+		}
+		if (!driver.getTitle().equals("Welcome to Galleria"))
+		{
+			throw new IllegalStateException("This is not the Index page.");
+		}
+	}
+
+	public SignupPage chooseToSignup()
+	{
+		driver.findElement(By.id("signup")).click();
+		return new SignupPage(driver, contextPath);
+	}
+
+	public LoginPage chooseToLogin()
+	{
+		driver.findElement(By.id("login")).click();
+		return new LoginPage(driver, contextPath);
+	}
+
+	public String fetchSignupSuccessMessage()
+	{
+		return driver.findElement(By.xpath("//ul[@id='messages']/li[1]")).getText();
+	}
+}

File galleria-jsf/src/test/java/info/galleria/view/user/LoginPage.java

+package info.galleria.view.user;
+
+import java.net.URI;
+import java.util.*;
+
+import org.openqa.selenium.*;
+import org.openqa.selenium.support.ui.*;
+
+public class LoginPage
+{
+
+	private WebDriver driver;
+	private URI contextPath;
+
+	public LoginPage(WebDriver driver, URI contextPath)
+	{
+		this.driver = driver;
+		this.contextPath = contextPath;
+		Wait<WebDriver> wait = new WebDriverWait(driver, 15);
+		wait.until(PageUtilities.visibilityOfElementLocated(By.id("publicWrapper")));
+		if (!driver.getTitle().equals("Log in to Galleria"))
+		{
+			throw new IllegalStateException("This page is not the login page.");
+		}
+	}
+
+	public HomePage loginAs(String userId, String password)
+	{
+		WebElement userIdField = driver.findElement(By.id("LoginForm:userid"));
+		WebElement passwordField = driver.findElement(By.id("LoginForm:password"));
+		WebElement submitButton = driver.findElement(By.id("LoginForm:submit"));
+		userIdField.clear();
+		userIdField.sendKeys(userId);
+		passwordField.clear();
+		passwordField.sendKeys(password);
+		submitButton.click();
+		return new HomePage(driver, contextPath);
+	}
+
+	public LoginPage loginAsExpectingError(String userId, String password)
+	{
+		WebElement userIdField = driver.findElement(By.id("LoginForm:userid"));
+		WebElement passwordField = driver.findElement(By.id("LoginForm:password"));
+		WebElement submitButton = driver.findElement(By.id("LoginForm:submit"));
+		userIdField.clear();
+		userIdField.sendKeys(userId);
+		passwordField.clear();
+		passwordField.sendKeys(password);
+		submitButton.click();
+		return new LoginPage(driver, contextPath);
+	}
+
+	public String[] getLoginErrorMessagesDisplayed()
+	{
+		List<String> errorMessages = new ArrayList<String>();
+		for (WebElement element : driver.findElements(By.xpath("//ul[@id='messages']/li")))
+		{
+			errorMessages.add(element.getText());
+		}
+		return errorMessages.toArray(new String[0]);
+	}
+}

File galleria-jsf/src/test/java/info/galleria/view/user/PageUtilities.java

+package info.galleria.view.user;
+
+import org.openqa.selenium.*;
+import org.openqa.selenium.support.ui.ExpectedCondition;
+
+public class PageUtilities
+{
+	public static ExpectedCondition<WebElement> visibilityOfElementLocated(final By locator)
+	{
+		return new ExpectedCondition<WebElement>()
+			{
+				public WebElement apply(WebDriver driver)
+				{
+					WebElement toReturn = driver.findElement(locator);
+					if (toReturn.isDisplayed())
+					{
+						return toReturn;
+					}
+					return null;
+				}
+			};
+	}
+}

File galleria-jsf/src/test/java/info/galleria/view/user/SignupPage.java

+package info.galleria.view.user;
+
+import java.net.URI;
+import java.util.*;
+
+import org.openqa.selenium.*;
+import org.openqa.selenium.support.ui.*;
+
+public class SignupPage
+{
+
+	private WebDriver driver;
+	private URI contextPath;
+
+	public SignupPage(WebDriver driver, URI contextPath)
+	{
+		this.driver = driver;
+		this.contextPath = contextPath;
+		Wait<WebDriver> wait = new WebDriverWait(driver, 15);
+		wait.until(PageUtilities.visibilityOfElementLocated(By.id("publicWrapper")));
+		if (!driver.getTitle().equals("Sign up with Galleria"))
+		{
+			throw new IllegalStateException("This page is not the sign up page.");
+		}
+	}
+
+	public IndexPage signupAs(String userId, String password)
+	{
+		driver.findElement(By.id("signupForm:userId")).clear();
+		driver.findElement(By.id("signupForm:userId")).sendKeys(userId);
+		driver.findElement(By.id("signupForm:password")).clear();
+		driver.findElement(By.id("signupForm:password")).sendKeys(password);
+		driver.findElement(By.id("signupForm:confirmPassword")).clear();
+		driver.findElement(By.id("signupForm:confirmPassword")).sendKeys(password);
+		driver.findElement(By.id("signupForm:submit")).click();
+		return new IndexPage(driver, contextPath);
+	}
+
+	public SignupPage signupAsExpectingError(String userId, String password, String confirmPassword)
+	{
+		driver.findElement(By.id("signupForm:userId")).clear();
+		driver.findElement(By.id("signupForm:userId")).sendKeys(userId);
+		driver.findElement(By.id("signupForm:password")).clear();
+		driver.findElement(By.id("signupForm:password")).sendKeys(password);
+		driver.findElement(By.id("signupForm:confirmPassword")).clear();
+		driver.findElement(By.id("signupForm:confirmPassword")).sendKeys(confirmPassword);
+		driver.findElement(By.id("signupForm:submit")).click();
+		return new SignupPage(driver, contextPath);
+	}
+
+	public String[] fetchSignupErrorMessages()
+	{
+		List<String> errorMessages = new ArrayList<String>();
+		for (WebElement element : driver.findElements(By.xpath("//ul[@id='messages']/li[@class='errorStyle']")))
+		{
+			errorMessages.add(element.getText());
+		}
+		return errorMessages.toArray(new String[0]);
+	}
+}

File galleria-jsf/src/test/resources/arquillian.xml

+<?xml version="1.0"?>
+<arquillian xmlns="http://jboss.com/arquillian" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+	xsi:schemaLocation="http://jboss.org/schema/arquillian http://jboss.org/schema/arquillian/arquillian_1_0.xsd">
+	<engine>
+		<property name="deploymentExportPath">target/</property>
+	</engine>
+	<!-- Remote Glassfish configuration -->
+	<container qualifier="glassfish" default="true">
+		<configuration>
+		</configuration>
+	</container>
+	<!-- Firefox Driver configuration -->
+	<extension qualifier="webdriver">
+		<property name="implementationClass">org.openqa.selenium.firefox.FirefoxDriver</property>
+	</extension>
+</arquillian>

File galleria-jsf/src/test/resources/database-test-setup.xml

+<?xml version='1.0' encoding='UTF-8'?>
+<dataset>
+  <USERS/>
+  <ALBUMS/>
+  <PHOTOS/>
+  <GROUPS/>
+  <USERS_GROUPS/>
+  <SEQUENCE SEQ_NAME="ALBUMS_SEQ" SEQ_COUNT="0"/>
+  <SEQUENCE SEQ_NAME="PHOTOS_SEQ" SEQ_COUNT="0"/>
+</dataset>

File galleria-jsf/src/test/resources/glassfish-web.xml

+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE glassfish-web-app PUBLIC "-//GlassFish.org//DTD GlassFish Application Server 3.1 Servlet 3.0//EN" "http://glassfish.org/dtds/glassfish-web-app_3_0-1.dtd">
+<!-- This file is used only in the Arquillian managed deployment until ARQ-527 is resolved. The purpose of this file is map the principals to roles. -->
+<glassfish-web-app error-url="">
+	<context-root>galleria-jsf</context-root>
+    <security-role-mapping>
+        <role-name>RegisteredUsers</role-name>
+        <group-name>RegisteredUsers</group-name>
+    </security-role-mapping>
+</glassfish-web-app>