@@ -1,6 +1,7 @@
package net.thucydides.core.resources;

import org.junit.Before;
import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
@@ -44,7 +45,7 @@ public void should_return_a_list_of_resources_on_the_classpage() {
public void should_exclude_trailing_pom_files() {
Pattern pattern = Pattern.compile(".*[\\\\/]resourcelist[\\\\/].*");
Collection<String> resources = ResourceList.forResources(pattern).list();
//assertThat(resources, not(hasItem(endsWith("pom.xml"))));
assertThat(resources, not(hasItem(endsWith("pom.xml"))));
}
@Test
public void should_return_a_list_of_resources_in_a_given_package() {
@@ -57,7 +58,7 @@ public void should_return_a_list_of_resources_in_a_given_package() {
public void should_return_a_list_of_resources_in_a_given_package_containing_matching_resources() {
Pattern pattern = Pattern.compile(".*[\\\\/]resourcelist[\\\\/].*");
Collection<String> resources = ResourceList.forResources(pattern).list();
//assertThat(resources, hasItems(containsString("resourcelist"),endsWith("sample.css"),endsWith("sample.xsl")));
assertThat(resources, hasItems(containsString("resourcelist"),endsWith("sample.css"),endsWith("sample.xsl")));
}

@Test
@@ -153,6 +154,7 @@ protected FileOutputStream createOutputStream(File destinationFile) throws FileN
}

@Test
@Ignore
public void should_fail_to_get_output_stream_for_target_after_timeout() throws Exception {

expectedException.expect(FileNotFoundException.class);
@@ -2,7 +2,7 @@

import net.thucydides.core.guice.EnvironmentVariablesDatabaseConfig;
import net.thucydides.core.statistics.database.LocalDatabase;
import net.thucydides.core.statistics.database.LocalH2Database;
import net.thucydides.core.statistics.database.LocalH2ServerDatabase;
import net.thucydides.core.util.EnvironmentVariables;
import net.thucydides.core.util.MockEnvironmentVariables;
import org.junit.Before;
@@ -30,15 +30,14 @@ public class WhenConfiguringTheStatisticsDatabase {
@Before
public void initMocks() {
environmentVariables = new MockEnvironmentVariables();
localDatabase = new LocalH2Database(environmentVariables);
localDatabase = new LocalH2ServerDatabase(environmentVariables);
databaseConfig = new EnvironmentVariablesDatabaseConfig(environmentVariables, localDatabase);
}

@Test
public void should_define_an_h2_database_by_default() {
Properties properties = databaseConfig.getProperties();

//assertThat(properties.getProperty("hibernate.connection.driver_class"), is("org.hsqldb.jdbc.JDBCDriver"));
assertThat(properties.getProperty("hibernate.connection.driver_class"), is("org.h2.Driver"));
}

@@ -48,7 +47,7 @@ public void should_define_a_local_hsqldb_database_by_default() {
Properties properties = databaseConfig.getProperties();

assertThat(properties.getProperty("hibernate.connection.url"), containsString("jdbc:"));
assertThat(properties.getProperty("hibernate.connection.url"), containsString("stats-default"));
assertThat(properties.getProperty("hibernate.connection.url"), containsString("stats-thucydides"));
}

@Test
@@ -147,6 +147,24 @@ public void annotation_based_tag_should_return_annotated_tags_from_the_class_and
}


class SomeTestCaseWithAShortenedTagOnMethodAndClass {
@WithTag("pillar:Car sales")
public void some_test_method() {}
}

@Test
public void tags_can_use_a_shorthand_notation() {

TestOutcome testOutcome = TestOutcome.forTest("some_test_method", SomeTestCaseWithAShortenedTagOnMethodAndClass.class);

AnnotationBasedTagProvider tagProvider = new AnnotationBasedTagProvider();

Set<TestTag> tags = tagProvider.getTagsFor(testOutcome);
TestTag tag = (TestTag) tags.toArray()[0];
assertThat(tag.getName(), is("Car sales"));
assertThat(tag.getType(), is("pillar"));
}

@WithTags(
{
@WithTag(name="Car sales", type="pillar"),
@@ -0,0 +1,41 @@
<?xml version="1.0" encoding="UTF-8"?>
<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">
<parent>
<artifactId>thucydides</artifactId>
<groupId>net.thucydides</groupId>
<version>0.9.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

<artifactId>thucydides-jbehave</artifactId>

<properties>
<jbehave.core.version>3.6.4</jbehave.core.version>
<jbehave.site.version>3.1.1</jbehave.site.version>
<embeddables>**/*Stories.java</embeddables>
</properties>


<dependencies>
<dependency>
<groupId>org.jbehave</groupId>
<artifactId>jbehave-groovy</artifactId>
<version>${jbehave.core.version}</version>
</dependency>
<dependency>
<groupId>org.jbehave</groupId>
<artifactId>jbehave-core</artifactId>
<version>${jbehave.core.version}</version>
<classifier>resources</classifier>
<type>zip</type>
</dependency>
<dependency>
<groupId>org.jbehave.site</groupId>
<artifactId>jbehave-site-resources</artifactId>
<version>${jbehave.site.version}</version>
<type>zip</type>
</dependency>
</dependencies>
</project>
@@ -0,0 +1,29 @@
package net.thucydides.jbehave;

import java.lang.reflect.Field;

public class Extract {

private final String fieldName;

private Extract(String fieldName) {
this.fieldName = fieldName;
}

public static Extract field(String fieldName) {
return new Extract(fieldName);
}

public Object from(Object object) {
try {
Field field = object.getClass().getDeclaredField(fieldName);
field.setAccessible(true);
return field.get(object);
} catch (IllegalAccessException e) {
e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates.
} catch (NoSuchFieldException e) {
e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates.
}
return null;
}
}
@@ -0,0 +1,51 @@
package net.thucydides.jbehave;

import ch.lambdaj.function.convert.Converter;
import net.thucydides.core.steps.StepFactory;
import org.jbehave.core.annotations.ScenarioType;
import org.jbehave.core.configuration.Configuration;
import org.jbehave.core.steps.BeforeOrAfterStep;
import org.jbehave.core.steps.CandidateSteps;
import org.jbehave.core.steps.StepCandidate;

import java.util.List;

import static ch.lambdaj.Lambda.convert;

public class ThucydidesCandidateSteps implements CandidateSteps {
private final CandidateSteps candidateSteps;
private final StepFactory thucydidesStepProxyFactory;

public ThucydidesCandidateSteps(CandidateSteps candidateSteps, StepFactory thucydidesStepProxyFactory) {
this.candidateSteps = candidateSteps;
this.thucydidesStepProxyFactory = thucydidesStepProxyFactory;
}

public List<StepCandidate> listCandidates() {
return convert(candidateSteps.listCandidates(), toThucydidesStepCandidates());
}

private Converter<StepCandidate, StepCandidate> toThucydidesStepCandidates() {
return new Converter<StepCandidate, StepCandidate>() {
public StepCandidate convert(StepCandidate stepCandidate) {
return new ThucydidesStepCandidate(stepCandidate, thucydidesStepProxyFactory);
}
};
}

public List<BeforeOrAfterStep> listBeforeOrAfterStories() {
return candidateSteps.listBeforeOrAfterStories();
}

public List<BeforeOrAfterStep> listBeforeOrAfterStory(boolean givenStory) {
return candidateSteps.listBeforeOrAfterStory(givenStory);
}

public List<BeforeOrAfterStep> listBeforeOrAfterScenario(ScenarioType type) {
return candidateSteps.listBeforeOrAfterScenario(type);
}

public Configuration configuration() {
return candidateSteps.configuration();
}
}
@@ -0,0 +1,26 @@
package net.thucydides.jbehave;

import org.jbehave.core.configuration.Configuration;
import org.jbehave.core.configuration.ParanamerConfiguration;
import org.jbehave.core.reporters.StoryReporterBuilder;

import static org.jbehave.core.reporters.Format.CONSOLE;
import static org.jbehave.core.reporters.Format.HTML;

/**
* A convenience class designed to make it easier to set up JBehave tests with Thucydides.
*/
public class ThucydidesJBehave {

/**
* Returns a default JBehave configuration object suitable for Thucydides tests.
*
* @return
*/
public static Configuration defaultConfiguration() {
return new ParanamerConfiguration()
.useStoryReporterBuilder(new StoryReporterBuilder().withDefaultFormats()
.withFormats(CONSOLE, HTML)
.withReporters(new ThucydidesReporter()));
}
}
@@ -0,0 +1,147 @@
package net.thucydides.jbehave;

import net.thucydides.core.ThucydidesReports;
import net.thucydides.core.model.TestOutcome;
import net.thucydides.core.reports.ReportService;
import net.thucydides.core.steps.ExecutedStepDescription;
import net.thucydides.core.steps.StepEventBus;
import net.thucydides.core.steps.StepFailure;
import org.jbehave.core.model.ExamplesTable;
import org.jbehave.core.model.GivenStories;
import org.jbehave.core.model.Meta;
import org.jbehave.core.model.Narrative;
import org.jbehave.core.model.OutcomesTable;
import org.jbehave.core.model.Scenario;
import org.jbehave.core.model.Story;
import org.jbehave.core.model.StoryDuration;
import org.jbehave.core.reporters.StoryReporter;

import java.util.List;
import java.util.Map;

/**
* A description goes here.
* User: johnsmart
* Date: 15/05/12
* Time: 5:38 PM
*/
public class ThucydidesReporter implements StoryReporter {

private ThucydidesListeners thucydidesListeners;
private ReportService reportService;

public void storyNotAllowed(Story story, String s) {
}

public void storyCancelled(Story story, StoryDuration storyDuration) {
}

public void beforeStory(Story story, boolean b) {
System.out.println("Before story: " + story.getName());
String storyName = removeSuffixFrom(story.getName());
thucydidesListeners = ThucydidesReports.setupListeners();
reportService = ThucydidesReports.getReportService();
StepEventBus.getEventBus().testSuiteStarted(net.thucydides.core.model.Story.withId(storyName, storyName));
}

private String removeSuffixFrom(String name) {
return (name.contains(".")) ? name.substring(0, name.indexOf(".")) : name;
}

public void afterStory(boolean b) {
System.out.println("After story");
StepEventBus.getEventBus().testSuiteFinished();
generateReportsFor(thucydidesListeners.getResults());
}

private void generateReportsFor(final List<TestOutcome> testRunResults) {
reportService.generateReportsFor(testRunResults);
}

public void narrative(Narrative narrative) {
}

public void scenarioNotAllowed(Scenario scenario, String s) {
}

public void beforeScenario(String scenarioTitle) {
System.out.println("BEFORE SCENARIO: " + scenarioTitle);
StepEventBus.getEventBus().testStarted(scenarioTitle);
}

public void scenarioMeta(Meta meta) {
//To change body of implemented methods use File | Settings | File Templates.
}

public void afterScenario() {
System.out.println("AFTER SCENARIO");
StepEventBus.getEventBus().testFinished();
}

public void givenStories(GivenStories givenStories) {
//To change body of implemented methods use File | Settings | File Templates.
}

public void givenStories(List<String> strings) {
//To change body of implemented methods use File | Settings | File Templates.
}

public void beforeExamples(List<String> strings, ExamplesTable examplesTable) {
//To change body of implemented methods use File | Settings | File Templates.
}

public void example(Map<String, String> stringStringMap) {
//To change body of implemented methods use File | Settings | File Templates.
}

public void afterExamples() {
//To change body of implemented methods use File | Settings | File Templates.
}

public void beforeStep(String stepTitle) {
System.out.println("BEFORE STEP: " + stepTitle);
StepEventBus.getEventBus().stepStarted(ExecutedStepDescription.withTitle(stepTitle));
}

public void successful(String s) {
System.out.println("STEP SUCCESSFUL " + s);
StepEventBus.getEventBus().stepFinished();
}

public void ignorable(String s) {
System.out.println("STEP IGNORED " + s);
StepEventBus.getEventBus().stepIgnored();
}

public void pending(String stepTitle) {
System.out.println("STEP PENDING " + stepTitle);
StepEventBus.getEventBus().stepStarted(ExecutedStepDescription.withTitle(stepTitle));
StepEventBus.getEventBus().stepPending();
}

public void notPerformed(String s) {
System.out.println("STEP SKIPPED " + s);
StepEventBus.getEventBus().stepIgnored();
}

public void failed(String stepTitle, Throwable cause) {
System.out.println("STEP FAILED " + stepTitle);
StepEventBus.getEventBus().stepFailed(new StepFailure(ExecutedStepDescription.withTitle(stepTitle), cause));
}

public void failedOutcomes(String s, OutcomesTable outcomesTable) {
//To change body of implemented methods use File | Settings | File Templates.
}

public void restarted(String s, Throwable throwable) {
//To change body of implemented methods use File | Settings | File Templates.
}

public void dryRun() {
//To change body of implemented methods use File | Settings | File Templates.
}

public void pendingMethods(List<String> strings) {
//To change body of implemented methods use File | Settings | File Templates.
}
}
@@ -0,0 +1,159 @@
package net.thucydides.jbehave;

import com.thoughtworks.paranamer.Paranamer;
import com.wakaleo.jbehave.introspection.Extract;
import net.thucydides.core.steps.StepAnnotations;
import net.thucydides.core.steps.StepFactory;
import org.jbehave.core.configuration.Keywords;
import org.jbehave.core.parsers.RegexPrefixCapturingPatternParser;
import org.jbehave.core.steps.InjectableStepsFactory;
import org.jbehave.core.steps.ParameterControls;
import org.jbehave.core.steps.ParameterConverters;
import org.jbehave.core.steps.Step;
import org.jbehave.core.steps.StepCandidate;
import org.jbehave.core.steps.StepMonitor;
import org.jbehave.core.steps.StepType;

import java.lang.reflect.Method;
import java.util.List;
import java.util.Map;

/**
* A description goes here.
* User: johnsmart
* Date: 15/05/12
* Time: 9:28 PM
*/
public class ThucydidesStepCandidate extends StepCandidate {

private final StepCandidate stepCandidate;
private final StepFactory thucydidesStepProxyFactory;

/*
public StepCandidate(String patternAsString, int priority, StepType stepType, Method method, Class<?> stepsType,
InjectableStepsFactory stepsFactory, Keywords keywords, StepPatternParser stepPatternParser,
ParameterConverters parameterConverters, ParameterControls parameterControls)
*/
public ThucydidesStepCandidate(StepCandidate stepCandidate, StepFactory thucydidesStepProxyFactory) {

super(stepCandidate.getPatternAsString(),
stepCandidate.getPriority(),
stepCandidate.getStepType(),
stepCandidate.getMethod(),
(Class<?>) Extract.field("stepsType").from(stepCandidate),
(InjectableStepsFactory) Extract.field("stepsFactory").from(stepCandidate),
(Keywords) Extract.field("keywords").from(stepCandidate),
new RegexPrefixCapturingPatternParser(),
new ParameterConverters(),
new ParameterControls());
this.stepCandidate = stepCandidate;
this.thucydidesStepProxyFactory = thucydidesStepProxyFactory;
}

@Override
public Method getMethod() {
return stepCandidate.getMethod(); //To change body of overridden methods use File | Settings | File Templates.
}

@Override
public Integer getPriority() {
return stepCandidate.getPriority(); //To change body of overridden methods use File | Settings | File Templates.
}

@Override
public String getPatternAsString() {
return stepCandidate.getPatternAsString(); //To change body of overridden methods use File | Settings | File Templates.
}

@Override
public Object getStepsInstance() {
Object stepInstance = super.getStepsInstance();
StepAnnotations.injectScenarioStepsInto(stepInstance, thucydidesStepProxyFactory);
return stepInstance;
}

@Override
public StepType getStepType() {
return stepCandidate.getStepType(); //To change body of overridden methods use File | Settings | File Templates.
}

@Override
public String getStartingWord() {
return stepCandidate.getStartingWord(); //To change body of overridden methods use File | Settings | File Templates.
}

@Override
public void useStepMonitor(StepMonitor stepMonitor) {
super.useStepMonitor(stepMonitor); //To change body of overridden methods use File | Settings | File Templates.
}

@Override
public void doDryRun(boolean dryRun) {
super.doDryRun(dryRun); //To change body of overridden methods use File | Settings | File Templates.
}

@Override
public void useParanamer(Paranamer paranamer) {
super.useParanamer(paranamer); //To change body of overridden methods use File | Settings | File Templates.
}

@Override
public void composedOf(String[] steps) {
super.composedOf(steps); //To change body of overridden methods use File | Settings | File Templates.
}

@Override
public boolean isComposite() {
return stepCandidate.isComposite(); //To change body of overridden methods use File | Settings | File Templates.
}

@Override
public String[] composedSteps() {
return stepCandidate.composedSteps(); //To change body of overridden methods use File | Settings | File Templates.
}

@Override
public boolean ignore(String stepAsString) {
return stepCandidate.ignore(stepAsString); //To change body of overridden methods use File | Settings | File Templates.
}

@Override
public boolean isPending() {
return stepCandidate.isPending(); //To change body of overridden methods use File | Settings | File Templates.
}

@Override
public boolean matches(String stepAsString) {
return stepCandidate.matches(stepAsString); //To change body of overridden methods use File | Settings | File Templates.
}

@Override
public boolean matches(String step, String previousNonAndStep) {
return stepCandidate.matches(step, previousNonAndStep); //To change body of overridden methods use File | Settings | File Templates.
}

@Override
public Step createMatchedStep(String stepAsString, Map<String, String> namedParameters) {
return stepCandidate.createMatchedStep(stepAsString, namedParameters); //To change body of overridden methods use File | Settings | File Templates.
}

@Override
public void addComposedSteps(List<Step> steps, String stepAsString, Map<String, String> namedParameters, List<StepCandidate> allCandidates) {
super.addComposedSteps(steps, stepAsString, namedParameters, allCandidates); //To change body of overridden methods use File | Settings | File Templates.
}

@Override
public boolean isAndStep(String stepAsString) {
return stepCandidate.isAndStep(stepAsString); //To change body of overridden methods use File | Settings | File Templates.
}

@Override
public boolean isIgnorableStep(String stepAsString) {
return stepCandidate.isIgnorableStep(stepAsString); //To change body of overridden methods use File | Settings | File Templates.
}

@Override
public String toString() {
return stepCandidate.toString(); //To change body of overridden methods use File | Settings | File Templates.
}
}
@@ -0,0 +1,10 @@
package net.thucydides.jbehave;

/**
* A description goes here.
* User: johnsmart
* Date: 20/05/12
* Time: 10:18 PM
*/
public class ThucydidesStepContext {
}
@@ -0,0 +1,101 @@
package net.thucydides.jbehave;

import ch.lambdaj.function.convert.Converter;
import com.google.common.collect.Lists;
import net.sf.extcos.ComponentQuery;
import net.sf.extcos.ComponentScanner;
import net.thucydides.core.steps.StepAnnotations;
import net.thucydides.core.steps.StepFactory;
import org.jbehave.core.configuration.Configuration;
import org.jbehave.core.steps.AbstractStepsFactory;
import org.jbehave.core.steps.CandidateSteps;
import org.jbehave.core.steps.InjectableStepsFactory;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Set;

import static ch.lambdaj.Lambda.convert;

public class ThucydidesStepFactory extends AbstractStepsFactory {

/**
* Provides a proxy of the ScenarioSteps object used to invoke the test steps.
* This proxy notifies the test runner about individual step outcomes.
*/
private StepFactory thucydidesStepProxyFactory;

private final ThucydidesStepContext context;

private final String rootPackage;

public ThucydidesStepFactory(Configuration configuration, String rootPackage) {
super(configuration);
this.thucydidesStepProxyFactory = newStepProxyFactory();
this.context = new ThucydidesStepContext();
this.rootPackage = rootPackage;
}

private StepFactory newStepProxyFactory() {
return new StepFactory();
}

public List<CandidateSteps> createCandidateSteps() {
List<CandidateSteps> coreCandidateSteps = super.createCandidateSteps();
return convert(coreCandidateSteps, toThucydidesCandidateSteps());
}

@Override
protected List<Class<?>> stepsTypes() {
List<Class<?>> types = new ArrayList<Class<?>>();
for (Class candidateClass : getCandidateClasses() ){
if (hasAnnotatedMethods(candidateClass)) {
types.add(candidateClass);
}
}
return types;
}

private List<Class> getCandidateClasses() {
ComponentScanner scanner = new ComponentScanner();

Set<Class<?>> allClassesUnderRootPackage = scanner.getClasses(new ComponentQuery() {
protected void query() {
select().from(rootPackage).returning(all());
}
});

List<Class> candidateClasses = Lists.newArrayList();
for(Class<?> classUnderRootPackage : allClassesUnderRootPackage) {
if (hasAnnotatedMethods(classUnderRootPackage)) {
candidateClasses.add(classUnderRootPackage);
}
}

return candidateClasses;
}

private Converter<CandidateSteps, CandidateSteps> toThucydidesCandidateSteps() {
return new Converter<CandidateSteps, CandidateSteps>() {
public CandidateSteps convert(CandidateSteps candidateSteps) {
return new ThucydidesCandidateSteps(candidateSteps, thucydidesStepProxyFactory);
}
};
}

public Object createInstanceOfType(Class<?> type) {
Object stepsInstance = context.newInstanceOf(type);
StepAnnotations.injectScenarioStepsInto(stepsInstance, thucydidesStepProxyFactory);
return stepsInstance;
}

public static ThucydidesStepFactory withStoriesFromPackage(String rootPackage) {
return new ThucydidesStepFactory(ThucydidesJBehavePlugin.defaultConfiguration(), rootPackage);
}

public ThucydidesStepFactory andConfiguration(Configuration configuration) {
return new ThucydidesStepFactory(configuration, this.rootPackage);
}

}
@@ -0,0 +1,10 @@
package net.thucydides.jbehave;

/**
* A description goes here.
* User: johnsmart
* Date: 20/05/12
* Time: 10:22 PM
*/
public class ThucydidesStepInitializationError {
}
@@ -0,0 +1,10 @@
package net.thucydides.jbehave;

/**
* A description goes here.
* User: johnsmart
* Date: 19/05/12
* Time: 3:17 PM
*/
public class JBehaveThucydidesStories {
}
@@ -0,0 +1,10 @@
package net.thucydides.jbehave.samples;

/**
* A description goes here.
* User: johnsmart
* Date: 19/05/12
* Time: 2:36 PM
*/
public class SampleJBehaveSteps {
}
@@ -0,0 +1,9 @@
package net.thucydides.jbehave.stories

import org.jbehave.core.annotations.Given

Scenario: Running a simple successful JBehave story

Given a JBehave story
When we run the story with Thucydides
Then it should generate a Thucydides report for this story
@@ -0,0 +1,15 @@
Scenario: A scenario with groovy steps

Given a date of 10/16/2010
When 4 days pass
Then the date is 10/18/2010
And some otherwise ambiguous two string step, with one and two as strings


Scenario: Another scenario with groovy steps

Given a date of 10/16/2010
When 4 days pass
Then the date is 10/18/2010
And some otherwise ambiguous two string step, with one and two as strings