expectedAlerts) throws IOException {
// generate the js code
final InputStream is = getClass().getClassLoader().getResourceAsStream("alertVerifier.js");
final String baseJS = IOUtils.toString(is);
IOUtils.closeQuietly(is);
final StringBuilder sb = new StringBuilder();
sb.append("\n\n");
return sb.toString();
}
/**
* Convenience method to pull the MockWebConnection out of an HtmlPage created with
* the loadPage method.
* @param page HtmlPage to get the connection from
* @return the MockWebConnection that served this page
*/
protected static final MockWebConnection getMockConnection(final HtmlPage page) {
return (MockWebConnection) page.getWebClient().getWebConnection();
}
/**
* Runs the calling JUnit test again and fails only if it already runs.
* This is helpful for tests that don't currently work but should work one day,
* when the tested functionality has been implemented.
* The right way to use it is:
*
* public void testXXX() {
* if (notYetImplemented()) {
* return;
* }
*
* ... the real (now failing) unit test
* }
*
* @return false when not itself already in the call stack
*/
protected boolean notYetImplemented() {
if (notYetImplementedFlag.get() != null) {
return false;
}
notYetImplementedFlag.set(Boolean.TRUE);
final Method testMethod = findRunningJUnitTestMethod();
try {
getLog().info("Running " + testMethod.getName() + " as not yet implemented");
testMethod.invoke(this, (Object[]) new Class[] {});
Assert.fail(testMethod.getName() + " is marked as not implemented but already works");
}
catch (final Exception e) {
getLog().info(testMethod.getName() + " fails which is normal as it is not yet implemented");
// method execution failed, it is really "not yet implemented"
}
finally {
notYetImplementedFlag.set(null);
}
return true;
}
/**
* Finds from the call stack the active running JUnit test case
* @return the test case method
* @throws RuntimeException if no method could be found
*/
private Method findRunningJUnitTestMethod() {
final Class< ? > cl = getClass();
final Class< ? >[] args = new Class[] {};
// search the initial junit test
final Throwable t = new Exception();
for (int i = t.getStackTrace().length - 1; i >= 0; i--) {
final StackTraceElement element = t.getStackTrace()[i];
if (element.getClassName().equals(cl.getName())) {
try {
final Method m = cl.getMethod(element.getMethodName(), args);
if (isPublicTestMethod(m)) {
return m;
}
}
catch (final Exception e) {
// can't access, ignore it
}
}
}
throw new RuntimeException("No JUnit test case method found in call stack");
}
/**
* From Junit. Test if the method is a junit test.
* @param method the method
* @return true if this is a junit test
*/
private boolean isPublicTestMethod(final Method method) {
return method.getParameterTypes().length == 0
&& (method.getName().startsWith("test") || method.getAnnotation(Test.class) != null)
&& method.getReturnType() == Void.TYPE
&& Modifier.isPublic(method.getModifiers());
}
private static final ThreadLocal notYetImplementedFlag = new ThreadLocal();
/**
* Load the specified resource for the supported browsers and tests
* that the generated log corresponds to the expected one for this browser.
*
* @param fileName the resource name which resides in /resources folder and
* belongs to the same package as the test class.
*
* @throws Exception if the test fails
*/
protected void testHTMLFile(final String fileName) throws Exception {
final String resourcePath = getClass().getPackage().getName().replace('.', '/') + '/' + fileName;
final URL url = getClass().getClassLoader().getResource(resourcePath);
final Map testedBrowser = new HashMap();
testedBrowser.put("FIREFOX_2", BrowserVersion.FIREFOX_2);
testedBrowser.put("INTERNET_EXPLORER_6_0", BrowserVersion.INTERNET_EXPLORER_6_0);
for (final Map.Entry entry : testedBrowser.entrySet()) {
final String browserKey = entry.getKey();
final BrowserVersion browserVersion = entry.getValue();
final WebClient client = new WebClient(browserVersion);
final HtmlPage page = (HtmlPage) client.getPage(url);
final HtmlElement want = page.getHtmlElementById(browserKey);
final HtmlElement got = page.getHtmlElementById("log");
final List expected = readChildElementsText(want);
final List actual = readChildElementsText(got);
Assert.assertEquals(expected, actual);
}
}
private List readChildElementsText(final HtmlElement elt) {
final List list = new ArrayList();
for (final HtmlElement child : elt.getChildElements()) {
list.add(child.asText());
}
return list;
}
void setBrowserVersion(final BrowserVersion browserVersion) {
browserVersion_ = browserVersion;
}
/**
* Returns a newly created WebClient with the current {@link BrowserVersion}.
* @return a newly created WebClient with the current {@link BrowserVersion}
*/
protected final WebClient getWebClient() {
return new WebClient(getBrowserVersion());
}
/**
* Returns the current {@link BrowserVersion}.
* @return current {@link BrowserVersion}
*/
protected final BrowserVersion getBrowserVersion() {
if (browserVersion_ == null) {
throw new IllegalStateException("You must annotate the test class with '@RunWith(BrowserRunner.class)'");
}
return browserVersion_;
}
void setExpectedAlerts(final String[] expectedAlerts) {
expectedAlerts_ = expectedAlerts;
}
/**
* Returns the expected alerts.
* @return the expected alerts
*/
protected String[] getExpectedAlerts() {
return expectedAlerts_;
}
/**
* Load a page with the specified HTML using the current browser version, and asserts the alerts
* equal the expected alerts.
* @param html the HTML to use
* @return the new page
* @throws Exception if something goes wrong
*/
protected final HtmlPage loadPageWithAlerts(final String html) throws Exception {
if (expectedAlerts_ == null) {
throw new IllegalStateException("You must annotate the test method with '@Alerts(...)' "
+ "and annotate the test class with '@RunWith(BrowserRunner.class)'");
}
createTestPageForRealBrowserIfNeeded(html, expectedAlerts_);
final WebClient client = getWebClient();
final List collectedAlerts = new ArrayList();
client.setAlertHandler(new CollectingAlertHandler(collectedAlerts));
final MockWebConnection webConnection = new MockWebConnection(client);
webConnection.setResponse(URL_GARGOYLE, html);
client.setWebConnection(webConnection);
final HtmlPage page = (HtmlPage) client.getPage(URL_GARGOYLE);
assertEquals(expectedAlerts_, collectedAlerts);
return page;
}
}