/**
* Copyright (c) 2008-2011 Sonatype, Inc.
* All rights reserved. Includes the third-party code listed at http://www.sonatype.com/products/nexus/attributions.
*
* This program is free software: you can redistribute it and/or modify it only under the terms of the GNU Affero General
* Public License Version 3 as published by the Free Software Foundation.
*
* This program 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 Affero General Public License Version 3
* for more details.
*
* You should have received a copy of the GNU Affero General Public License Version 3 along with this program. If not, see
* http://www.gnu.org/licenses.
*
* Sonatype Nexus (TM) Open Source Version is available from Sonatype, Inc. Sonatype and Sonatype Nexus are trademarks of
* Sonatype, Inc. Apache Maven is a trademark of the Apache Foundation. M2Eclipse is a trademark of the Eclipse Foundation.
* All other trademarks are the property of their respective owners.
*/
package org.sonatype.nexus.mock;
import static com.thoughtworks.selenium.grid.tools.ThreadSafeSeleniumSessionStorage.startSeleniumSession;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.TimeUnit;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.io.FileUtils;
import org.codehaus.plexus.component.repository.exception.ComponentLookupException;
import org.sonatype.nexus.mock.models.User;
import org.sonatype.nexus.mock.pages.MainPage;
import org.sonatype.nexus.mock.rest.MockHelper;
import org.sonatype.nexus.mock.util.PropUtil;
import org.sonatype.nexus.mock.util.ThreadUtils;
import org.sonatype.nexus.test.utils.TestProperties;
import org.sonatype.nexus.testng.PlexusObjectFactory;
import org.sonatype.spice.jscoverage.JsonReportHandler;
import org.testng.Assert;
import org.testng.annotations.AfterClass;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.BeforeSuite;
import org.testng.annotations.Test;
import com.thoughtworks.selenium.Selenium;
import com.thoughtworks.selenium.SeleniumException;
import com.thoughtworks.selenium.grid.tools.ThreadSafeSeleniumSessionStorage;
@Test( sequential = true )
public abstract class SeleniumTest
extends NexusMockTestCase
{
protected Selenium selenium;
protected MainPage main;
private static JsonReportHandler handler;
@BeforeClass
public void createSelenium()
throws Exception
{
if ( ( selenium = ThreadSafeSeleniumSessionStorage.session() ) == null )
{
final String seleniumServer = PropUtil.get( "seleniumServer", "localhost" );
final int seleniumPort = PropUtil.get( "seleniumPort", 4444 );
final String seleniumBrowser = PropUtil.get( "seleniumBrowser", "*firefox" );
startSeleniumSession( seleniumServer, seleniumPort, seleniumBrowser,
TestProperties.getString( "nexus.base.url" ) );
selenium = ThreadSafeSeleniumSessionStorage.session();
if ( !seleniumBrowser.contains( "iexplore" ) )
{
selenium.setSpeed( "500" );
}
selenium.getEval( "window.moveTo(1,1); window.resizeTo(1021,737);" );
}
main = new MainPage( selenium );
}
@BeforeMethod
public void loadUrl()
throws Exception
{
selenium.open( "/nexus" );
// sometimes the browser window just froze between tasks
try
{
selenium.getEval( "window.isRunning()" );
}
catch ( Exception e )
{
selenium.open( "/nexus" );
selenium.getEval( "window.isRunning()" );
}
MockHelper.clearMocks();
}
@AfterMethod( alwaysRun = true )
public void logout()
throws Exception
{
if ( selenium != null && main != null )
{
main.clickLogout();
getCoverage();
}
MockHelper.clearMocks();
}
/**
* Takes a screenshot of the browser and saves it to the target/screenshots directory. The exact name of the file is
* based on the currently executing test class and method name plus the line number of the source code that called
* this method.
*
* @throws java.io.IOException If the screenshot could not be taken.
*/
protected void takeScreenshot()
throws IOException
{
StackTraceElement ste = new Exception().getStackTrace()[1];
takeScreenshot( "line-" + ste.getLineNumber() );
}
/**
* Takes a screenshot of the browser and saves it to the target/screenshots directory. The name is a combination of
* the currently executing test class and method name, plus the name parameterized supplied when calling this
* method.
*
* @param name A specific name to append to the screenshot file name.
* @throws IOException If the screenshot could not be taken.
*/
@Test( enabled = false )
public void takeScreenshot( String name )
{
File parent = new File( "target/screenshots/" );
// noinspection ResultOfMethodCallIgnored
parent.mkdirs();
String screen = selenium.captureScreenshotToString();
FileOutputStream fos;
try
{
fos = new FileOutputStream( new File( parent, testId + "-" + name + ".png" ) );
fos.write( Base64.decodeBase64( screen.getBytes() ) );
fos.close();
}
catch ( IOException e )
{
log.error( e.getMessage(), e );
}
}
@Test( enabled = false )
public void captureNetworkTraffic()
{
try
{
File parent = new File( "target/network-traffic/" );
// noinspection ResultOfMethodCallIgnored
parent.mkdirs();
FileOutputStream fos = new FileOutputStream( new File( parent, testId + ".txt" ) );
fos.write( selenium.captureNetworkTraffic( "TODO" ).getBytes( "UTF-8" ) );
}
catch ( Exception e )
{
log.error( e.getMessage(), e );
}
}
@BeforeSuite
public void coverageInit()
throws ComponentLookupException
{
handler = lookup( JsonReportHandler.class );
}
protected void getCoverage()
throws IOException
{
try
{
handler.appendResults( selenium.getEval( "window.jscoverage_serializeCoverageToJSON()" ) );
handler.persist();
}
catch ( SeleniumException e )
{
// ignore probably coverage was turned off
}
}
protected void doLogin()
{
doLogin( User.ADMIN.getUsername(), User.ADMIN.getPassword() );
}
protected void doLogin( String username, String password )
{
selenium.runScript( "window.Sonatype.utils.doLogin( null, '" + username + "', '" + password + "');" );
// wait for the login-link to change
ThreadUtils.waitFor( new ThreadUtils.WaitCondition()
{
public boolean checkCondition( long elapsedTimeInMs )
{
return !main.loginLinkAvailable();
}
}, TimeUnit.SECONDS, 15 );
}
@AfterClass
public void cleanInstance()
throws Exception
{
saveLogs();
PlexusObjectFactory.getContainer().release( this );
cleanFields();
}
private void saveLogs()
{
File logsDir = new File( "./target/logs" );
if ( logsDir.exists() )
{
File testLogs = new File( logsDir, testId );
testLogs.mkdirs();
File[] logs = logsDir.listFiles();
for ( File log : logs )
{
try
{
FileUtils.copyFile( log, new File( testLogs, log.getName() + testName ) );
FileUtils.writeStringToFile( log, "" );
}
catch ( IOException e )
{
NexusMockTestCase.log.error( e.getMessage(), e );
}
}
}
}
private void cleanFields()
throws IllegalArgumentException, IllegalAccessException
{
List<Field> fields = getFields( getClass() );
for ( Field field : fields )
{
if ( Modifier.isStatic( field.getModifiers() ) || Modifier.isFinal( field.getModifiers() ) )
{
continue;
}
field.setAccessible( true );
if ( field.getDeclaringClass().isPrimitive() )
{
field.set( this, 0 );
}
else
{
field.set( this, null );
}
}
}
private List<Field> getFields( Class<?> clazz )
{
if ( clazz == null )
{
return Collections.emptyList();
}
Field[] f = clazz.getDeclaredFields();
List<Field> fields = new ArrayList<Field>();
fields.addAll( Arrays.asList( f ) );
fields.addAll( getFields( clazz.getSuperclass() ) );
return fields;
}
// profiling with yourkit, activate using -P youtkit-profile
private static Object profiler;
@BeforeSuite
public void startProfiler()
{
Class<?> controllerClazz;
try
{
controllerClazz = Class.forName( "com.yourkit.api.Controller" );
}
catch ( Exception e )
{
log.info( "Profiler not present" );
return;
}
try
{
profiler = controllerClazz.newInstance();
controllerClazz.getMethod( "captureMemorySnapshot" ).invoke( profiler );
}
catch ( Exception e )
{
Assert.fail( "Profiler was active, but failed due: " + e.getMessage(), e );
}
}
@AfterMethod
public void takeSnapshot()
{
if ( profiler != null )
{
try
{
profiler.getClass().getMethod( "forceGC" ).invoke( profiler );
profiler.getClass().getMethod( "captureMemorySnapshot" ).invoke( profiler );
}
catch ( Exception e )
{
Assert.fail( "Profiler was active, but failed due: " + e.getMessage(), e );
}
}
}
}