Skip to content

Commit

Permalink
Working Hybrid test case
Browse files Browse the repository at this point in the history
  • Loading branch information
marcpalmer committed Aug 3, 2012
1 parent afd9481 commit a60a1a3
Show file tree
Hide file tree
Showing 8 changed files with 135 additions and 57 deletions.
2 changes: 1 addition & 1 deletion FunctionalTestGrailsPlugin.groovy
Expand Up @@ -17,7 +17,7 @@
*/

class FunctionalTestGrailsPlugin {
def version = "2.0.M3-SNAPSHOT"
def version = "2.0.M4-SNAPSHOT"

def grailsVersion = "1.3 > *"

Expand Down
7 changes: 0 additions & 7 deletions src/groovy/com/grailsrocks/functionaltest/APITestCase.groovy
Expand Up @@ -13,11 +13,4 @@ class APITestCase extends TestCaseBase {
def head(url, Closure paramSetup = null) {
doRequest(url, 'HEAD', paramSetup)
}

/**
* Get whatever the server gave us back and RESTClient parsed out
*/
def getData() {
client.response.data
}
}
26 changes: 16 additions & 10 deletions src/groovy/com/grailsrocks/functionaltest/BrowserTestCase.groovy
Expand Up @@ -9,18 +9,24 @@ import com.grailsrocks.functionaltest.client.htmlunit.*
import com.grailsrocks.functionaltest.client.Client

class BrowserTestCase extends TestCaseBase {
void setBrowser(String browser) {
client.clientState.browser = browser
Class getDefaultClientType() {
BrowserClient
}

@Override
Client getClient() {
def c = super.getClient()
if (c instanceof BrowserClient) {
return c
} else {
throw new IllegalArgumentException("Cannot change browser, current client is not a browser")

boolean __isDSLMethod(String name) {
if (super.__isDSLMethod(name)) {
return true
}

name.startsWith('by') ||
name == 'back' ||
name == 'form' ||
name == 'click' ||
name == 'clearCache'
}

void setBrowser(String browser) {
client.clientState.browser = browser
}

def getBrowser() {
Expand Down
Expand Up @@ -24,19 +24,22 @@ import grails.util.GrailsUtil
class FunctionalTestException extends junit.framework.AssertionFailedError {
def urlStack
def hackedCause
def baseURL

FunctionalTestException(TestCaseBase test, Throwable cause) {
super(cause.message ?: cause.toString())
this.hackedCause = GrailsUtil.sanitize(cause)
this.urlStack = test.urlStack
this.baseURL = test.baseURL
}

void dumpURLStack(PrintWriter pw = null) {
if (!pw) pw = System.out
pw.println "URL Stack that resulted in ${hackedCause ?: 'failure'}"
pw.println "---------------"
urlStack?.reverseEach {
pw.println "${it.method} ${it.url} ${it.eventSource}"
def url = it.url.startsWith(baseURL) ? it.url[baseURL.size()..-1] : it.url
pw.println "${it.method} ${url} ${it.eventSource}"
}
pw.println "---------------"

Expand Down
58 changes: 57 additions & 1 deletion src/groovy/com/grailsrocks/functionaltest/HybridTestCase.groovy
@@ -1,5 +1,61 @@
package com.grailsrocks.functionaltest

class HybridTestCase extends TestCaseBase {
import com.grailsrocks.functionaltest.client.*

class HybridTestCase extends BrowserTestCase {
private Class _defaultClientType = BrowserClient

Class getDefaultClientType() {
_defaultClientType
}

void defaultClientType(String clientType) {
switch (clientType) {
case 'API': _defaultClientType = APIClient;
break;
case 'Browser': _defaultClientType = BrowserClient;
break;
default:
throw new IllegalArgumentException("Only 'API' and 'Browser' are supported values for client type")
}
}


boolean __isDSLMethod(String name) {
if (super.__isDSLMethod(name)) {
return true
}

name == 'withAPI' ||
name == 'withBrowser'
}


def withAPI(Closure c) {
def oldclient = currentClientId
client('API', APIClient)
c()
client(oldclient)
}

def withAPI(String id, Closure c) {
def oldclient = currentClientId
client(id, APIClient)
c()
client(oldclient)
}

def withBrowser(String id, Closure c) {
def oldclient = currentClientId
client(id, BrowserClient)
c()
client(oldclient)
}

def withBrowser(Closure c) {
def oldclient = currentClientId
client('Browser', BrowserClient)
c()
client(oldclient)
}
}
75 changes: 43 additions & 32 deletions src/groovy/com/grailsrocks/functionaltest/TestCaseBase.groovy
Expand Up @@ -37,16 +37,16 @@ import junit.framework.AssertionFailedError

import com.grailsrocks.functionaltest.client.*

class TestCaseBase extends GroovyTestCase implements GroovyInterceptable, ClientAdapter {
abstract class TestCaseBase extends GroovyTestCase implements GroovyInterceptable, ClientAdapter {

static MONKEYING_DONE

static BORING_STACK_ITEMS = [
'FunctionalTests',
'functionaltestplugin.',
'gant.',
'com.grailsrocks',
'com.gargoylesoftware', 'org.apache']
'com.gargoylesoftware',
'org.apache']

static {
StackTraceUtils.addClassTest { className ->
Expand All @@ -65,7 +65,7 @@ class TestCaseBase extends GroovyTestCase implements GroovyInterceptable, Client
boolean autoFollowRedirects = true
def consoleOutput
protected stashedClients = [:]
String currentClientId
private String currentClientId
Client currentClient
def redirectUrl

Expand All @@ -81,19 +81,17 @@ class TestCaseBase extends GroovyTestCase implements GroovyInterceptable, Client
BrowserClient.initVirtualMethods()
MONKEYING_DONE = true
}

if (!consoleOutput) {
consoleOutput = System.out
}
}

Class getDefaultClientType() {
BrowserClient
}

void switchClient(Class<Client> type = getDefaultClientType()) {
currentClient = type.newInstance(this)
}
abstract Class getDefaultClientType()

Client getClient() {
if (!currentClient) {
switchClient()
client('default')
clientChanged()
}
return currentClient
Expand All @@ -111,23 +109,41 @@ class TestCaseBase extends GroovyTestCase implements GroovyInterceptable, Client
autoFollowRedirects = enabled
}

boolean __isDSLMethod(String name) {
name.startsWith('assert') ||
name.startsWith('shouldFail') ||
name.startsWith('fail') ||
name == 'client' ||
name == 'defaultClientType'
}

/**
* Call to switch between multiple client browsers, simulating different users
* Call to switch between multiple clients, simulating different users or access scenarios (REST API + browser)
*/
void client(String id) {
//System.out.println "Stashed clients: ${stashedClients.dump()}"
void client(String id, Class<Client> type = getDefaultClientType()) {
testReportOut.println "Switching to browser client [$id]"
if (id != currentClientId) {
// If we were currently unnamed but have some state, save our state with name ""
stashClient(currentClientId ?: '')
currentClient = null
// restore client if it is known, else
unstashClient(id)
currentClientId = id
if (!currentClient) {
// Creat new
testReportOut.println "Creating to new client [$id] of type [$type]"
currentClient = (type ?: BrowserClient).newInstance(this)
stashClient(id)
}
}
currentClientId = id
}

String getCurrentClientId() {
this.@currentClientId
}

protected void stashClient(id) {
stashedClients[id] = client
currentClient = null
stashedClients[id] = currentClient
}

protected void unstashClient(id) {
Expand All @@ -146,14 +162,14 @@ class TestCaseBase extends GroovyTestCase implements GroovyInterceptable, Client
super.tearDown()
}

PrintStream getTestReportOut() {
System.out
}

def invokeMethod(String name, args) {
def t = this
// Let's not mess with internal calls, or it is a nightmare to debug
if (name.startsWith('__')) {
return InvokerHelper.getMetaClass(this).invokeMethod(this,name,args)
} else if ((name.startsWith('assert') ||
name.startsWith('shouldFail') ||
name.startsWith('fail')) ) {
if (!name.startsWith('__') && __isDSLMethod(name)) {
try {
return InvokerHelper.getMetaClass(this).invokeMethod(this,name,args)
} catch (Throwable e) {
Expand All @@ -164,15 +180,7 @@ class TestCaseBase extends GroovyTestCase implements GroovyInterceptable, Client
} else throw e
}
} else {
try {
//System.out.println "Invoking: ${name} - $args"
return InvokerHelper.getMetaClass(this).invokeMethod(this,name,args)
} catch (Throwable e) {
if (!(e instanceof FunctionalTestException)) {
__reportFailure(__sanitize(e))
throw __sanitize(new FunctionalTestException(this, e))
} else throw e
}
return InvokerHelper.getMetaClass(this).invokeMethod(this,name,args)
}
}

Expand Down Expand Up @@ -526,6 +534,9 @@ class TestCaseBase extends GroovyTestCase implements GroovyInterceptable, Client
handleRedirects()
}

/**
* Called by client implementations when new response received
*/
void contentChanged(ContentChangedEvent event) {
newResponseReceived(event.client)

Expand Down
Expand Up @@ -138,7 +138,6 @@ class BrowserClient implements Client, WebWindowListener, HtmlAttributeChangeLis
}

void clientChanged() {
mainWindow = _client?.currentWindow
}

int getResponseStatus() {
Expand Down Expand Up @@ -188,8 +187,8 @@ class BrowserClient implements Client, WebWindowListener, HtmlAttributeChangeLis
}

void webWindowContentChanged(WebWindowEvent event) {
System.out.println "Content of web window [${event.webWindow}] changed"
if (event.webWindow == mainWindow) {
System.out.println "Content of web window [${event?.webWindow}] changed"
if (event?.webWindow == mainWindow) {
_page = event.newPage
response = _page.webResponse

Expand All @@ -198,10 +197,10 @@ class BrowserClient implements Client, WebWindowListener, HtmlAttributeChangeLis
client: this,
url: response.requestSettings.url,
method: response.requestSettings.httpMethod,
eventSource: 'webWindowContentChange event',
eventSource: 'Browser content change',
statusCode: response.statusCode) )
} else {
System.out.println "New content of web window [${event.webWindow}] was not for main window, ignoring"
System.out.println "New content of web window [${event?.webWindow}] was not for main window, ignoring"
}
}

Expand All @@ -223,18 +222,21 @@ class BrowserClient implements Client, WebWindowListener, HtmlAttributeChangeLis
response = null
currentURL = url

System.out.println "Making request: $url A"
if (currentAuthInfo) {
// @todo We could use htmlunit auth stuff here?
def encoded = Base64Codec.encode("${currentAuthInfo.user}:${currentAuthInfo.credentials}".getBytes('utf-8'))
settings.addAdditionalHeader('Authorization', "Basic "+encoded)
}

System.out.println "Making request: $url B"
def wrapper
if (paramSetupClosure) {
def builder = new RequestBuilder(settings)
wrapper = builder.build(paramSetupClosure)
}

System.out.println "Making request: $url C"
def headerLists = [stickyHeaders]
if (wrapper) {
headerLists << wrapper.headers
Expand All @@ -245,6 +247,7 @@ class BrowserClient implements Client, WebWindowListener, HtmlAttributeChangeLis
}
}

System.out.println "Making request: $url D"
if (wrapper?.reqParameters) {
def params = []
wrapper.reqParameters.each { pair ->
Expand All @@ -256,10 +259,15 @@ class BrowserClient implements Client, WebWindowListener, HtmlAttributeChangeLis
if (wrapper?.body) {
settings.requestBody = wrapper.body
}
System.out.println "Making request: $url D"
TestUtils.dumpRequestInfo(this)

System.out.println "Making request: $url E"
response = _client.loadWebResponse(settings)
mainWindow = _client?.currentWindow
System.out.println "Making request: $url F"
_page = _client.loadWebResponseInto(response, mainWindow)
System.out.println "Making request: $url G"

// By this time the events will have been triggered
}
Expand Down
Expand Up @@ -14,6 +14,7 @@ class InterceptingPageCreator extends DefaultPageCreator {
}

Page createPage(WebResponse webResponse, WebWindow webWindow) {
println "Interceptor createPage: ${webWindow}"
def p = super.createPage(webResponse,webWindow)
if (p instanceof HtmlPage) {
p.addDomChangeListener(client)
Expand Down

0 comments on commit a60a1a3

Please sign in to comment.