Skip to content

Commit

Permalink
Merge pull request #618 from FluentLenium/feature/#612
Browse files Browse the repository at this point in the history
Feature/#612 Added FluentCucumber
  • Loading branch information
koryl committed Aug 22, 2018
2 parents 0045787 + 22523f0 commit c9b6327
Show file tree
Hide file tree
Showing 50 changed files with 955 additions and 351 deletions.

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package org.fluentlenium.adapter.cucumber.custom;

import cucumber.api.CucumberOptions;
import org.fluentlenium.adapter.cucumber.FluentCucumber;
import org.fluentlenium.configuration.FluentConfiguration;
import org.junit.runner.RunWith;

@RunWith(FluentCucumber.class)
@FluentConfiguration(webDriver = "chrome")
@CucumberOptions(features = "classpath:org/fluentlenium/examples/quickstart",
plugin = {"pretty", "html:target/cucumber", "json:target/cucumber.json"})
public class BasicRunner {

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package org.fluentlenium.adapter.cucumber.custom.steps;

import cucumber.api.java.en.Given;
import cucumber.api.java.en.Then;
import cucumber.api.java.en.When;
import org.fluentlenium.adapter.cucumber.custom.page.HomePage;
import org.fluentlenium.adapter.cucumber.FluentCucumberTest;
import org.fluentlenium.core.annotation.Page;

import static org.assertj.core.api.Assertions.assertThat;

public class BasicStep extends FluentCucumberTest{

@Page
HomePage page;

@Given(value = "Visit duckduckgo")
public void step1() {
goTo(page);
}

@When(value = "I search FluentLenium")
public void step2() {
el("#search_form_input_homepage").fill().with("FluentLenium");
el("#search_button_homepage").submit();
}

@Then(value = "Title contains FluentLenium")
public void step3() {
assertThat(page.window().title()).contains("FluentLenium");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ public class FluentAdapter implements FluentControl {

private final FluentControlContainer controlContainer;

private final Configuration configuration = ConfigurationFactoryProvider.newConfiguration(getClass());
private final Configuration configuration;

/**
* Creates a new fluent adapter.
Expand All @@ -37,6 +37,18 @@ public FluentAdapter() {
*/
public FluentAdapter(FluentControlContainer controlContainer) {
this.controlContainer = controlContainer;
configuration = ConfigurationFactoryProvider.newConfiguration(getClass());
}

/**
* Creates a new fluent adapter, using given control interface container.
*
* @param controlContainer control interface container
* @param clazz class from which annotation configuration will be looked up
*/
public FluentAdapter(FluentControlContainer controlContainer, Class clazz) {
this.controlContainer = controlContainer;
configuration = ConfigurationFactoryProvider.newConfiguration(clazz);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,20 @@ public FluentTestRunnerAdapter(FluentControlContainer driverContainer, SharedMut
this.sharedMutator = sharedMutator;
}

/**
* Creates a test runner adapter, with a customer driver container and a customer shared mutator.
* It is possible to pass class from which the FluentConfiguration annotation will be loaded.
*
* @param driverContainer driver container
* @param clazz class from which FluentConfiguration annotation will be loaded
* @param sharedMutator shared mutator
*/
public FluentTestRunnerAdapter(FluentControlContainer driverContainer, Class clazz, SharedMutator sharedMutator) {
super(driverContainer, clazz);
this.sharedMutator = sharedMutator;
}


/**
* Invoked when a test class has finished (whatever the success of failing status)
*
Expand Down
5 changes: 5 additions & 0 deletions fluentlenium-cucumber/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,11 @@
<artifactId>jcip-annotations</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.seleniumhq.selenium</groupId>
<artifactId>selenium-chrome-driver</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

<build>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
package org.fluentlenium.adapter.cucumber;

import cucumber.api.TypeRegistryConfigurer;
import cucumber.api.event.TestRunFinished;
import cucumber.api.formatter.Formatter;
import cucumber.runtime.ClassFinder;
import cucumber.runtime.Runtime;
import cucumber.runtime.RuntimeOptions;
import cucumber.runtime.RuntimeOptionsFactory;
import cucumber.runtime.Reflections;
import cucumber.runtime.DefaultTypeRegistryConfiguration;
import cucumber.runtime.io.MultiLoader;
import cucumber.runtime.io.ResourceLoader;
import cucumber.runtime.io.ResourceLoaderClassFinder;
import cucumber.runtime.java.JavaBackend;
import cucumber.runtime.junit.Assertions;
import cucumber.runtime.junit.FeatureRunner;
import cucumber.runtime.junit.JUnitOptions;
import cucumber.runtime.junit.JUnitReporter;
import cucumber.runtime.model.CucumberFeature;
import io.cucumber.stepexpression.TypeRegistry;
import org.fluentlenium.configuration.FluentConfiguration;
import org.junit.runner.Description;
import org.junit.runner.notification.RunNotifier;
import org.junit.runners.ParentRunner;
import org.junit.runners.model.InitializationError;
import org.junit.runners.model.Statement;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import static java.util.Collections.singletonList;
import static java.util.Optional.ofNullable;
import static org.fluentlenium.adapter.cucumber.FluentCucumberTestContainer.FLUENT_TEST;

/**
* Main point for integrating FluentLenium with Cucumber. Pass this class to JUnit @RunWith() annotation to enable
* FluentLenium dependency injection for Cucumber steps.
* To use annotation configuration annotate class with @RunWith(FluentCucumber.class) and not on BaseTest or any other
* class containing Cucumber steps.
*/
@SuppressWarnings("unused")
public class FluentCucumber extends ParentRunner<FeatureRunner> {
private final JUnitReporter jUnitReporter;
private final List<FeatureRunner> children = new ArrayList<>();
final Runtime runtime;
private final Formatter formatter;
private final ClassFinder classFinder;
private final RuntimeOptions runtimeOptions;

/**
* Constructor for FluentCucumber.
*
* @param clazz runner class
* @throws InitializationError problem with initialization
* @throws IOException problem with reading feature files
*/
public FluentCucumber(Class clazz) throws InitializationError, IOException {
super(clazz);
ClassLoader classLoader = clazz.getClassLoader();
Assertions.assertNoCucumberAnnotatedMethods(clazz);
RuntimeOptionsFactory runtimeOptionsFactory = new RuntimeOptionsFactory(clazz);
runtimeOptions = runtimeOptionsFactory.create();
ResourceLoader resourceLoader = new MultiLoader(classLoader);
classFinder = new ResourceLoaderClassFinder(resourceLoader, classLoader);

getFluentConfiguration(clazz);
this.runtime = new Runtime(resourceLoader, classLoader, singletonList(getBackend()), runtimeOptions);
this.formatter = runtimeOptions.formatter(classLoader);

JUnitOptions junitOptions = new JUnitOptions(runtimeOptions.getJunitOptions());
List<CucumberFeature> cucumberFeatures =
runtimeOptions.cucumberFeatures(resourceLoader, this.runtime.getEventBus());

this.jUnitReporter = new JUnitReporter(this.runtime.getEventBus(), runtimeOptions.isStrict(), junitOptions);
this.addChildren(cucumberFeatures);
}

/**
* Initialization of JavaBackend with {@link FluentObjectFactory} for FluentCucumber tests.
*
* @return backend with {@link FluentObjectFactory}
*/
private JavaBackend getBackend() {
Reflections reflections = new Reflections(classFinder);
TypeRegistryConfigurer typeRegistryConfigurer =
reflections.instantiateExactlyOneSubclass(TypeRegistryConfigurer.class,
MultiLoader.packageName(runtimeOptions.getGlue()),
new Class[0],
new Object[0],
new DefaultTypeRegistryConfiguration());

FLUENT_TEST.instance();

return new JavaBackend(new FluentObjectFactory(),
classFinder, new TypeRegistry(typeRegistryConfigurer.locale()));
}

/**
* Check for {@link FluentConfiguration} and pass runner class to {@link FluentCucumberTest}
*
* @param clazz runner class
*/
private void getFluentConfiguration(Class clazz) {
ofNullable(clazz.getAnnotation(FluentConfiguration.class))
.ifPresent(annotation -> FLUENT_TEST.setRunnerClass(clazz));
}

/**
* @return list of children
*/
public List<FeatureRunner> getChildren() {
return this.children;
}

/**
* @param child FeatureRunner child
* @return description of a FeatureRunner child
*/
protected Description describeChild(FeatureRunner child) {
return child.getDescription();
}

/**
* Run feature.
*
* @param child FeatureRunner child
* @param notifier notifier used by Cucumber
*/
protected void runChild(FeatureRunner child, RunNotifier notifier) {
child.run(notifier);
}

/**
* Invoke feature
*
* @param notifier notifier
* @return evaluated Statement
*/
protected Statement childrenInvoker(RunNotifier notifier) {
final Statement features = super.childrenInvoker(notifier); //NOPMD
return new Statement() {
public void evaluate() {
try {
features.evaluate();
} catch (Throwable throwable) {
throw new RuntimeException("Cannot evaluate statement", throwable);
}
FluentCucumber.this.runtime.getEventBus()
.send(new TestRunFinished(
FluentCucumber.this.runtime.getEventBus()
.getTime()));
}
};
}

/**
* Add features.
*
* @param cucumberFeatures list of Cucumber features
* @throws InitializationError problem with initializing features.
*/
private void addChildren(List<CucumberFeature> cucumberFeatures) throws InitializationError {
Iterator var2 = cucumberFeatures.iterator();

while (var2.hasNext()) {
CucumberFeature cucumberFeature = (CucumberFeature) var2.next();
FeatureRunner featureRunner = new FeatureRunner(cucumberFeature, this.runtime, this.jUnitReporter);
if (!featureRunner.isEmpty()) {
this.children.add(featureRunner);
}
}
}
}
Loading

0 comments on commit c9b6327

Please sign in to comment.