diff --git a/.gitignore b/.gitignore index ee029dcb29..ff548d5f08 100644 --- a/.gitignore +++ b/.gitignore @@ -31,3 +31,4 @@ libpeerconnection.log ehthumbs.db Icon? Thumbs.db +test-json-report.json diff --git a/Gemfile b/Gemfile deleted file mode 100644 index 4b2817a9dd..0000000000 --- a/Gemfile +++ /dev/null @@ -1,4 +0,0 @@ -source 'http://rubygems.org' -gem 'rake', '0.9.2.2' -gem 'cucumber', '1.1.4' -gem 'aruba', '0.4.11' \ No newline at end of file diff --git a/History.md b/History.md index 1ae7e4121f..9a9f11ba50 100644 --- a/History.md +++ b/History.md @@ -1,5 +1,10 @@ -## [Git master](https://github.com/cucumber/cucumber-jvm/compare/v1.1.4...master) +## [1-1-5-SNAPSHOT (Git master)](https://github.com/cucumber/cucumber-jvm/compare/v1.1.4...master) +* [TestNG] Java Calculator TestNG example project ([#579](https://github.com/cucumber/cucumber-jvm/pull/579) Dmytro Chyzhykov) +* [Jython] Access to scenario in Before and After hooks ([#582](https://github.com/cucumber/cucumber-jvm/issues/582) Aslak Hellesøy) +* [Core] Replace placeholders in the Scenario Outline title ([#580](https://github.com/cucumber/cucumber-jvm/pull/580), [#510](https://github.com/cucumber/cucumber-jvm/issues/510) Jamie W. Astin) +* [JUnit/Core] `@cucumber.junit.api.Cucumber.Options` is deprecated in favour of `@cucumber.api.Options` ([#549](https://github.com/cucumber/cucumber-jvm/issues/549) Aslak Hellesøy) +* [JUnit] Inherit Information of @Cucumber.Options ([#568](https://github.com/cucumber/cucumber-jvm/issues/568) Klaus Bayrhammer) * [JUnit] JUnitFormatter does not put required name attribute in testsuite root element ([#480](https://github.com/cucumber/cucumber-jvm/pull/480), [#477](https://github.com/cucumber/cucumber-jvm/issues/477) ericmaxwell2003) * [Core] Output embedded text in HTML report ([#501](https://github.com/cucumber/cucumber-jvm/pull/501) Tom Dunstan) * [Core] Fix for Lexing Error message not useful ([#519](https://github.com/cucumber/cucumber-jvm/issues/519), [#523](https://github.com/cucumber/cucumber-jvm/pull/523) Alpar Gal) @@ -9,6 +14,7 @@ * [Core] Bugfix: StringIndexOutOfBoundsException when optional argument not present. ([#394](https://github.com/cucumber/cucumber-jvm/issues/394), [#558](https://github.com/cucumber/cucumber-jvm/pull/558) Guy Burton) * [Java, Jython] New `--snippet [underscore|camelcase]` option for more control over snippet style. ([#561](https://github.com/cucumber/cucumber-jvm/pull/561), [302](https://github.com/cucumber/cucumber-jvm/pull/302) Márton Mészáros, Aslak Hellesøy) * [Windows] Use uri instead of path in CucumberFeature ([#562](https://github.com/cucumber/cucumber-jvm/pull/562) Björn Rasmusson) +* [Android] Better example for Cucumber-Android. ([#547](https://github.com/cucumber/cucumber-jvm/issues/547), [#574](https://github.com/cucumber/cucumber-jvm/issues/574)) ## [1.1.4](https://github.com/cucumber/cucumber-jvm/compare/v1.1.3...v1.1.4) (2013-08-11) diff --git a/Rakefile b/Rakefile deleted file mode 100644 index f7d3eb1e96..0000000000 --- a/Rakefile +++ /dev/null @@ -1,7 +0,0 @@ -require 'cucumber/rake/task' - -Cucumber::Rake::Task.new(:tck_tests) do |t| - t.cucumber_opts = '-r java/src/test/resources/cucumber-tck -r cucumber-tck cucumber-tck' -end - -task :default => :tck_tests \ No newline at end of file diff --git a/core/src/main/java/cucumber/api/CucumberOptions.java b/core/src/main/java/cucumber/api/CucumberOptions.java new file mode 100644 index 0000000000..7e25fe62da --- /dev/null +++ b/core/src/main/java/cucumber/api/CucumberOptions.java @@ -0,0 +1,62 @@ +package cucumber.api; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * This annotation provides the same options as the cucumber command line, {@link cucumber.api.cli.Main}. + */ +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.TYPE}) +public @interface CucumberOptions { + /** + * @return true if this is a dry run + */ + boolean dryRun() default false; + + /** + * @return true if strict mode is enabled (fail if there are undefined or pending steps) + */ + boolean strict() default false; + + /** + * @return the paths to the feature(s) + */ + String[] features() default {}; + + /** + * @return where to look for glue code (stepdefs and hooks) + */ + String[] glue() default {}; + + /** + * @return what tags in the features should be executed + */ + String[] tags() default {}; + + /** + * @return what formatter(s) to use + */ + String[] format() default {}; + + /** + * @return whether or not to use monochrome output + */ + boolean monochrome() default false; + + /** + * Specify a patternfilter for features or scenarios + * + * @return a list of patterns + */ + String[] name() default {}; + + String dotcucumber() default ""; + + /** + * @return what format should the snippets use. underscore, camelcase + */ + SnippetType snippets() default SnippetType.UNDERSCORE; +} diff --git a/core/src/main/java/cucumber/api/Scenario.java b/core/src/main/java/cucumber/api/Scenario.java index dd827e6617..04bc7324d6 100644 --- a/core/src/main/java/cucumber/api/Scenario.java +++ b/core/src/main/java/cucumber/api/Scenario.java @@ -7,6 +7,9 @@ * It allows writing text and embedding media into reports, as well as inspecting results (in an After block). */ public interface Scenario { + /** + * @return source_tag_names. Needed for compatibility with Capybara. + */ Collection getSourceTagNames(); /** diff --git a/core/src/main/java/cucumber/runtime/RuntimeOptionsFactory.java b/core/src/main/java/cucumber/runtime/RuntimeOptionsFactory.java new file mode 100644 index 0000000000..aa017f0fb7 --- /dev/null +++ b/core/src/main/java/cucumber/runtime/RuntimeOptionsFactory.java @@ -0,0 +1,182 @@ +package cucumber.runtime; + +import cucumber.api.SnippetType; +import cucumber.runtime.io.MultiLoader; + +import java.lang.annotation.Annotation; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +public class RuntimeOptionsFactory { + private final Class clazz; + private final Class[] annotationClasses; + + public RuntimeOptionsFactory(Class clazz, Class[] annotationClasses) { + this.clazz = clazz; + this.annotationClasses = annotationClasses; + } + + public RuntimeOptions create() { + List args = buildArgsFromOptions(); + + return new RuntimeOptions(System.getProperties(), args.toArray(new String[args.size()])); + } + + private List buildArgsFromOptions() { + List args = new ArrayList(); + + for (Class classWithOptions = clazz; hasSuperClass(classWithOptions); classWithOptions = classWithOptions.getSuperclass()) { + Annotation[] optionsArray = getOptions(classWithOptions); + for (Annotation options : optionsArray) { + if (options != null) { + addDryRun(options, args); + addMonochrome(options, args); + addTags(options, args); + addFormats(options, args); + addStrict(options, args); + addName(options, args); + addDotCucumber(options, args); + addSnippets(options, args); + } + } + addGlue(optionsArray, args, classWithOptions); + addFeatures(optionsArray, args, classWithOptions); + } + return args; + } + + private void addDotCucumber(Annotation options, List args) { + String dotcucumber = this.invoke(options, "dotcucumber"); + if (!dotcucumber.isEmpty()) { + args.add("--dotcucumber"); + args.add(dotcucumber); + } + } + + private void addName(Annotation options, List args) { + for (String name : this.invoke(options, "name")) { + args.add("--name"); + args.add(name); + } + } + + private void addSnippets(Annotation options, List args) { + args.add("--snippets"); + args.add(this.invoke(options, "snippets").toString()); + } + + private void addDryRun(Annotation options, List args) { + if (this.invoke(options, "dryRun")) { + args.add("--dry-run"); + } + } + + private void addMonochrome(Annotation options, List args) { + if (this.invoke(options, "monochrome") || runningInEnvironmentWithoutAnsiSupport()) { + args.add("--monochrome"); + } + } + + private void addTags(Annotation options, List args) { + for (String tags : this.invoke(options, "tags")) { + args.add("--tags"); + args.add(tags); + } + } + + private void addFormats(Annotation options, List args) { + if (this.invoke(options, "format").length != 0) { + for (String format : this.invoke(options, "format")) { + args.add("--format"); + args.add(format); + } + } else { + args.add("--format"); + args.add("null"); + } + } + + private void addFeatures(Annotation[] optionsArray, List args, Class clazz) { + boolean specified = false; + for (Annotation options : optionsArray) { + if (options != null && this.invoke(options, "features").length != 0) { + Collections.addAll(args, this.invoke(options, "features")); + specified = true; + } + } + if (!specified) { + args.add(MultiLoader.CLASSPATH_SCHEME + packagePath(clazz)); + } + } + + private void addGlue(Annotation[] optionsArray, List args, Class clazz) { + boolean specified = false; + for (Annotation options : optionsArray) { + if (options != null && this.invoke(options, "glue").length != 0) { + for (String glue : this.invoke(options, "glue")) { + args.add("--glue"); + args.add(glue); + } + specified = true; + } + } + if (!specified) { + args.add("--glue"); + args.add(MultiLoader.CLASSPATH_SCHEME + packagePath(clazz)); + } + } + + + private void addStrict(Annotation options, List args) { + if (this.invoke(options, "strict")) { + args.add("--strict"); + } + } + + static String packagePath(Class clazz) { + return packagePath(packageName(clazz.getName())); + } + + static String packagePath(String packageName) { + return packageName.replace('.', '/'); + } + + static String packageName(String className) { + return className.substring(0, Math.max(0, className.lastIndexOf("."))); + } + + private boolean runningInEnvironmentWithoutAnsiSupport() { + boolean intelliJidea = System.getProperty("idea.launcher.bin.path") != null; + // TODO: What does Eclipse use? + return intelliJidea; + } + + private boolean hasSuperClass(Class classWithOptions) { + return classWithOptions != Object.class; + } + + private Annotation[] getOptions(Class clazz) { + Annotation[] annotations = new Annotation[annotationClasses.length]; + for (int i = 0; i < annotations.length; i++) { + annotations[i] = clazz.getAnnotation(annotationClasses[i]); + } + return annotations; + } + + private T invoke(Annotation options, String name) { + try { + Method method = options.annotationType().getMethod(name); + return (T) method.invoke(options); + } catch (NoSuchMethodException e) { + throw new CucumberException(e); + } catch (InvocationTargetException e) { + throw new CucumberException(e); + } catch (IllegalAccessException e) { + throw new CucumberException(e); + } + } + +} diff --git a/core/src/main/java/cucumber/runtime/model/CucumberScenarioOutline.java b/core/src/main/java/cucumber/runtime/model/CucumberScenarioOutline.java index ab81237646..f576c5bbc1 100644 --- a/core/src/main/java/cucumber/runtime/model/CucumberScenarioOutline.java +++ b/core/src/main/java/cucumber/runtime/model/CucumberScenarioOutline.java @@ -48,7 +48,10 @@ public void run(Formatter formatter, Reporter reporter, Runtime runtime) { } CucumberScenario createExampleScenario(ExamplesTableRow header, ExamplesTableRow example, List examplesTags) { - Scenario exampleScenario = new Scenario(example.getComments(), examplesTags, getGherkinModel().getKeyword(), getGherkinModel().getName(), "", example.getLine(), example.getId()); + // Make sure we replace the tokens in the name of the scenario + String exampleScenarioName = replaceTokens(new HashSet(), header.getCells(), example.getCells(), getGherkinModel().getName()); + + Scenario exampleScenario = new Scenario(example.getComments(), examplesTags, getGherkinModel().getKeyword(), exampleScenarioName, "", example.getLine(), example.getId()); CucumberScenario cucumberScenario = new CucumberScenario(cucumberFeature, cucumberBackground, exampleScenario, example); for (Step step : getSteps()) { cucumberScenario.step(createExampleStep(step, header, example)); diff --git a/junit/src/test/java/cucumber/runtime/junit/RuntimeOptionsFactoryTest.java b/core/src/test/java/cucumber/runtime/RuntimeOptionsFactoryTest.java similarity index 56% rename from junit/src/test/java/cucumber/runtime/junit/RuntimeOptionsFactoryTest.java rename to core/src/test/java/cucumber/runtime/RuntimeOptionsFactoryTest.java index 057f6dcdac..fec3fe276f 100644 --- a/junit/src/test/java/cucumber/runtime/junit/RuntimeOptionsFactoryTest.java +++ b/core/src/test/java/cucumber/runtime/RuntimeOptionsFactoryTest.java @@ -1,8 +1,10 @@ -package cucumber.runtime.junit; +package cucumber.runtime; -import cucumber.api.junit.Cucumber; -import cucumber.runtime.RuntimeOptions; +import cucumber.api.CucumberOptions; import cucumber.api.SnippetType; +import gherkin.formatter.Formatter; +import gherkin.formatter.JSONFormatter; +import gherkin.formatter.PrettyFormatter; import org.junit.Test; import java.net.MalformedURLException; @@ -11,8 +13,9 @@ import java.util.List; import java.util.regex.Pattern; -import static cucumber.runtime.junit.RuntimeOptionsFactory.packageName; -import static cucumber.runtime.junit.RuntimeOptionsFactory.packagePath; +import static cucumber.runtime.RuntimeOptionsFactory.packageName; +import static cucumber.runtime.RuntimeOptionsFactory.packagePath; +import static java.util.Arrays.asList; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; @@ -20,35 +23,37 @@ public class RuntimeOptionsFactoryTest { @Test public void create_strict() throws Exception { - RuntimeOptionsFactory runtimeOptionsFactory = new RuntimeOptionsFactory(Strict.class); + RuntimeOptionsFactory runtimeOptionsFactory = new RuntimeOptionsFactory(Strict.class, new Class[]{CucumberOptions.class}); RuntimeOptions runtimeOptions = runtimeOptionsFactory.create(); assertTrue(runtimeOptions.isStrict()); } @Test public void create_non_strict() throws Exception { - RuntimeOptionsFactory runtimeOptionsFactory = new RuntimeOptionsFactory(NotStrict.class); + RuntimeOptionsFactory runtimeOptionsFactory = new RuntimeOptionsFactory(NotStrict.class, new Class[]{CucumberOptions.class}); RuntimeOptions runtimeOptions = runtimeOptionsFactory.create(); assertFalse(runtimeOptions.isStrict()); } @Test public void create_without_options() throws Exception { - RuntimeOptionsFactory runtimeOptionsFactory = new RuntimeOptionsFactory(WithoutOptions.class); + RuntimeOptionsFactory runtimeOptionsFactory = new RuntimeOptionsFactory(WithoutOptions.class, new Class[]{CucumberOptions.class}); RuntimeOptions runtimeOptions = runtimeOptionsFactory.create(); assertFalse(runtimeOptions.isStrict()); + assertEquals(asList("classpath:cucumber/runtime"), runtimeOptions.getFeaturePaths()); + assertEquals(asList("classpath:cucumber/runtime"), runtimeOptions.getGlue()); } @Test public void create_with_no_name() throws Exception { - RuntimeOptionsFactory runtimeOptionsFactory = new RuntimeOptionsFactory(NoName.class); + RuntimeOptionsFactory runtimeOptionsFactory = new RuntimeOptionsFactory(NoName.class, new Class[]{CucumberOptions.class}); RuntimeOptions runtimeOptions = runtimeOptionsFactory.create(); assertTrue(runtimeOptions.getFilters().isEmpty()); } @Test public void create_with_multiple_names() throws Exception { - RuntimeOptionsFactory runtimeOptionsFactory = new RuntimeOptionsFactory(MultipleNames.class); + RuntimeOptionsFactory runtimeOptionsFactory = new RuntimeOptionsFactory(MultipleNames.class, new Class[]{CucumberOptions.class}); RuntimeOptions runtimeOptions = runtimeOptionsFactory.create(); @@ -61,21 +66,21 @@ public void create_with_multiple_names() throws Exception { @Test public void create_with_dotcucumber_dir() throws MalformedURLException { - RuntimeOptionsFactory runtimeOptionsFactory = new RuntimeOptionsFactory(DotCucumberFile.class); + RuntimeOptionsFactory runtimeOptionsFactory = new RuntimeOptionsFactory(DotCucumberFile.class, new Class[]{CucumberOptions.class}); RuntimeOptions runtimeOptions = runtimeOptionsFactory.create(); assertEquals(new URL("file:somewhere/.cucumber/"), runtimeOptions.getDotCucumber()); } @Test public void create_with_dotcucumber_url() throws MalformedURLException { - RuntimeOptionsFactory runtimeOptionsFactory = new RuntimeOptionsFactory(DotCucumberUrl.class); + RuntimeOptionsFactory runtimeOptionsFactory = new RuntimeOptionsFactory(DotCucumberUrl.class, new Class[]{CucumberOptions.class}); RuntimeOptions runtimeOptions = runtimeOptionsFactory.create(); assertEquals(new URL("https://some.where/.cucumber/"), runtimeOptions.getDotCucumber()); } @Test public void create_with_snippets() { - RuntimeOptionsFactory runtimeOptionsFactory = new RuntimeOptionsFactory(Snippets.class); + RuntimeOptionsFactory runtimeOptionsFactory = new RuntimeOptionsFactory(Snippets.class, new Class[]{CucumberOptions.class}); RuntimeOptions runtimeOptions = runtimeOptionsFactory.create(); assertEquals(SnippetType.CAMELCASE, runtimeOptions.getSnippetType()); } @@ -94,37 +99,57 @@ public void finds_path_for_class_in_toplevel_package() { assertEquals("", packageName("TopLevelClass")); } - @Cucumber.Options(snippets = SnippetType.CAMELCASE) + @Test + public void inherit_formatter_from_baseclass() { + RuntimeOptionsFactory runtimeOptionsFactory = new RuntimeOptionsFactory(SubClassWithFormatter.class, new Class[]{CucumberOptions.class}); + RuntimeOptions runtimeOptions = runtimeOptionsFactory.create(); + + List formatters = runtimeOptions.getFormatters(); + assertEquals(2, formatters.size()); + assertTrue(formatters.get(0) instanceof PrettyFormatter); + assertTrue(formatters.get(1) instanceof JSONFormatter); + } + + @Test + public void override_monochrome_flag_from_baseclass() { + RuntimeOptionsFactory runtimeOptionsFactory = new RuntimeOptionsFactory(SubClassWithMonoChromeTrue.class, new Class[]{CucumberOptions.class}); + RuntimeOptions runtimeOptions = runtimeOptionsFactory.create(); + + assertTrue(runtimeOptions.isMonochrome()); + } + + + @CucumberOptions(snippets = SnippetType.CAMELCASE) static class Snippets { // empty } - @Cucumber.Options(strict = true) + @CucumberOptions(strict = true) static class Strict { // empty } - @Cucumber.Options + @CucumberOptions static class NotStrict { // empty } - @Cucumber.Options(name = {"name1", "name2"}) + @CucumberOptions(name = {"name1", "name2"}) static class MultipleNames { // empty } - @Cucumber.Options + @CucumberOptions static class NoName { // empty } - @Cucumber.Options(dotcucumber = "somewhere/.cucumber") + @CucumberOptions(dotcucumber = "somewhere/.cucumber") static class DotCucumberFile { // empty } - @Cucumber.Options(dotcucumber = "https://some.where/.cucumber") + @CucumberOptions(dotcucumber = "https://some.where/.cucumber") static class DotCucumberUrl { // empty } @@ -132,4 +157,24 @@ static class DotCucumberUrl { static class WithoutOptions { // empty } + + @CucumberOptions(format = "pretty") + static class SubClassWithFormatter extends BaseClassWithFormatter { + // empty + } + + @CucumberOptions(format = "json:test-json-report.json") + static class BaseClassWithFormatter { + // empty + } + + @CucumberOptions(monochrome = true) + static class SubClassWithMonoChromeTrue extends BaseClassWithMonoChromeFalse { + // empty + } + + @CucumberOptions(monochrome = false) + static class BaseClassWithMonoChromeFalse { + // empty + } } diff --git a/core/src/test/java/cucumber/runtime/model/CucumberScenarioOutlineTest.java b/core/src/test/java/cucumber/runtime/model/CucumberScenarioOutlineTest.java index addee5077c..e521d094bf 100644 --- a/core/src/test/java/cucumber/runtime/model/CucumberScenarioOutlineTest.java +++ b/core/src/test/java/cucumber/runtime/model/CucumberScenarioOutlineTest.java @@ -4,10 +4,13 @@ import gherkin.formatter.model.DataTableRow; import gherkin.formatter.model.DocString; import gherkin.formatter.model.ExamplesTableRow; +import gherkin.formatter.model.ScenarioOutline; import gherkin.formatter.model.Step; +import gherkin.formatter.model.Tag; import org.junit.Test; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import static java.util.Arrays.asList; @@ -15,6 +18,7 @@ public class CucumberScenarioOutlineTest { private static final List C = new ArrayList(); + private static final List T = Collections.emptyList(); @Test public void replaces_tokens_in_step_names() { @@ -38,5 +42,41 @@ public void replaces_tokens_in_data_tables() { Step exampleStep = CucumberScenarioOutline.createExampleStep(outlineStep, new ExamplesTableRow(C, asList("n"), 1, ""), new ExamplesTableRow(C, asList("10"), 1, "")); assertEquals(asList("I", "have 10 cukes"), exampleStep.getRows().get(0).getCells()); - } + } + + /*** + * From a scenario outline, we create one or more "Example Scenario"s. This is composed + * of each step from the outline, with the tokens replaced with the pertient values + * for the current example row.

+ * + * Each "Example Scenario" has a name. This was previously just a copy of the outline's + * name. However, we'd like to be able to support token replacement in the scenario too, + * for example: + * + *

+     * Scenario Outline: Time offset check for 
+     * Given my local country is 
+     * When I compare the time difference to GMT
+     * Then the time offset should be 
+     *  
+     * Examples: 
+     * | LOCATION_NAME | OFFSET |
+     * | London        | 1      |
+     * | San Fran      | 8      |
+     * 
+ * + * Will create a scenario named "Time offset check for London" for the first row in the + * examples table. + */ + @Test + public void replaces_tokens_in_scenario_names() { + // Create Gherkin the outline itself ... + ScenarioOutline outline = new ScenarioOutline(C, T,"Scenario Outline", "Time offset check for ", "", new Integer(1), ""); + + // ... then the Cukes implementation + CucumberScenarioOutline cukeOutline = new CucumberScenarioOutline(null, null, outline); + CucumberScenario exampleScenario = cukeOutline.createExampleScenario(new ExamplesTableRow(C, asList("LOCATION_NAME"), 1, ""), new ExamplesTableRow(C, asList("London"), 1, ""), T); + + assertEquals("Time offset check for London", exampleScenario.getGherkinModel().getName()); + } } diff --git a/examples/java-calculator-testng/.gitignore b/examples/java-calculator-testng/.gitignore new file mode 100644 index 0000000000..5e68373517 --- /dev/null +++ b/examples/java-calculator-testng/.gitignore @@ -0,0 +1,3 @@ +/.settings +/.classpath +/.project diff --git a/examples/java-calculator-testng/README.md b/examples/java-calculator-testng/README.md new file mode 100644 index 0000000000..4ae19dc2a6 --- /dev/null +++ b/examples/java-calculator-testng/README.md @@ -0,0 +1,7 @@ +This is a TestNG copy-paste version of the [JUnit Calculator example](https://github.com/cucumber/cucumber-jvm/tree/master/examples/java-calculator) project. + +If you find its TestNG report is not idiomatic, consider making a contribution to improve Cucumber JVM TestNG Support. + +**Note** +Please keep code base of this project up-to-date + diff --git a/examples/java-calculator-testng/pom.xml b/examples/java-calculator-testng/pom.xml new file mode 100644 index 0000000000..610008f125 --- /dev/null +++ b/examples/java-calculator-testng/pom.xml @@ -0,0 +1,32 @@ + + 4.0.0 + + + info.cukes + cucumber-jvm + ../../pom.xml + 1.1.5-SNAPSHOT + + + java-calculator-testng + jar + Examples: Java Calculator TestNG + + + + info.cukes + cucumber-jvm-deps + test + + + info.cukes + cucumber-java + test + + + info.cukes + cucumber-testng + test + + + diff --git a/examples/java-calculator-testng/src/main/java/cucumber/examples/java/calculator/DateCalculator.java b/examples/java-calculator-testng/src/main/java/cucumber/examples/java/calculator/DateCalculator.java new file mode 100644 index 0000000000..4ab3eb3816 --- /dev/null +++ b/examples/java-calculator-testng/src/main/java/cucumber/examples/java/calculator/DateCalculator.java @@ -0,0 +1,15 @@ +package cucumber.examples.java.calculator; + +import java.util.Date; + +public class DateCalculator { + private Date now; + + public DateCalculator(Date now) { + this.now = now; + } + + public String isDateInThePast(Date date) { + return (date.before(now)) ? "yes" : "no"; + } +} diff --git a/examples/java-calculator-testng/src/main/java/cucumber/examples/java/calculator/RpnCalculator.java b/examples/java-calculator-testng/src/main/java/cucumber/examples/java/calculator/RpnCalculator.java new file mode 100644 index 0000000000..c28c15e9cf --- /dev/null +++ b/examples/java-calculator-testng/src/main/java/cucumber/examples/java/calculator/RpnCalculator.java @@ -0,0 +1,40 @@ +package cucumber.examples.java.calculator; + +import java.util.Deque; +import java.util.LinkedList; +import java.util.List; + +import static java.util.Arrays.asList; + +public class RpnCalculator { + private final Deque stack = new LinkedList(); + private static final List OPS = asList("-", "+", "*", "/"); + + public void push(Object arg) { + if (OPS.contains(arg)) { + Number y = stack.removeLast(); + Number x = stack.isEmpty() ? 0 : stack.removeLast(); + Double val = null; + if (arg.equals("-")) { + val = x.doubleValue() - y.doubleValue(); + } else if (arg.equals("+")) { + val = x.doubleValue() + y.doubleValue(); + } else if (arg.equals("*")) { + val = x.doubleValue() * y.doubleValue(); + } else if (arg.equals("/")) { + val = x.doubleValue() / y.doubleValue(); + } + push(val); + } else { + stack.add((Number) arg); + } + } + + public void PI() { + push(Math.PI); + } + + public Number value() { + return stack.getLast(); + } +} diff --git a/examples/java-calculator-testng/src/test/java/cucumber/examples/java/calculator/DateStepdefs.java b/examples/java-calculator-testng/src/test/java/cucumber/examples/java/calculator/DateStepdefs.java new file mode 100644 index 0000000000..bebce6c231 --- /dev/null +++ b/examples/java-calculator-testng/src/test/java/cucumber/examples/java/calculator/DateStepdefs.java @@ -0,0 +1,38 @@ +package cucumber.examples.java.calculator; + +import cucumber.api.Format; +import cucumber.api.java.en.Given; +import cucumber.api.java.en.Then; +import cucumber.api.java.en.When; + +import java.util.Date; + +import static org.junit.Assert.assertEquals; + +public class DateStepdefs { + private String result; + private DateCalculator calculator; + + @Given("^today is (.+)$") + public void today_is(@Format("yyyy-MM-dd") Date date) { + calculator = new DateCalculator(date); + } + + /** + * We don't need to use @Format here, since the date string in the step + * conforms to SimpleDateFormat.SHORT. Cucumber has built-in support for + * SimpleDateFormat.SHORT, SimpleDateFormat.MEDIUM, + * SimpleDateFormat.LONG and SimpleDateFormat.FULL. + * + * @see SimpleDateFormat + */ + @When("^I ask if (.+) is in the past$") + public void I_ask_if_date_is_in_the_past(Date date) { + result = calculator.isDateInThePast(date); + } + + @Then("^the result should be (.+)$") + public void the_result_should_be(String expectedResult) { + assertEquals(expectedResult, result); + } +} diff --git a/examples/java-calculator-testng/src/test/java/cucumber/examples/java/calculator/RpnCalculatorStepdefs.java b/examples/java-calculator-testng/src/test/java/cucumber/examples/java/calculator/RpnCalculatorStepdefs.java new file mode 100644 index 0000000000..067e01d7a8 --- /dev/null +++ b/examples/java-calculator-testng/src/test/java/cucumber/examples/java/calculator/RpnCalculatorStepdefs.java @@ -0,0 +1,63 @@ +package cucumber.examples.java.calculator; + +import cucumber.api.Scenario; +import cucumber.api.java.After; +import cucumber.api.java.Before; +import cucumber.api.java.en.Given; +import cucumber.api.java.en.Then; +import cucumber.api.java.en.When; + +import java.util.List; + +import static org.junit.Assert.assertEquals; + +public class RpnCalculatorStepdefs { + private RpnCalculator calc; + + @Given("^a calculator I just turned on$") + public void a_calculator_I_just_turned_on() { + calc = new RpnCalculator(); + } + + @When("^I add (\\d+) and (\\d+)$") + public void adding(int arg1, int arg2) { + calc.push(arg1); + calc.push(arg2); + calc.push("+"); + } + + @Given("^I press (.+)$") + public void I_press(String what) { + calc.push(what); + } + + @Then("^the result is (\\d+)$") + public void the_result_is(double expected) { + assertEquals(expected, calc.value()); + } + + @Before({"~@foo"}) + public void before() { + System.out.println("Runs before scenarios *not* tagged with @foo"); + } + + @After + public void after(Scenario scenario) { + // result.write("HELLLLOO"); + } + + @Given("^the previous entries:$") + public void thePreviousEntries(List entries) { + for (Entry entry : entries) { + calc.push(entry.first); + calc.push(entry.second); + calc.push(entry.operation); + } + } + + public class Entry { + Integer first; + Integer second; + String operation; + } +} diff --git a/examples/java-calculator-testng/src/test/java/cucumber/examples/java/calculator/RunCukesTest.java b/examples/java-calculator-testng/src/test/java/cucumber/examples/java/calculator/RunCukesTest.java new file mode 100644 index 0000000000..5c3bd57086 --- /dev/null +++ b/examples/java-calculator-testng/src/test/java/cucumber/examples/java/calculator/RunCukesTest.java @@ -0,0 +1,8 @@ +package cucumber.examples.java.calculator; + +import cucumber.api.CucumberOptions; +import cucumber.api.testng.AbstractTestNGCucumberTests; + +@CucumberOptions(format = "json:target/cucumber-report.json") +public class RunCukesTest extends AbstractTestNGCucumberTests { +} diff --git a/examples/java-calculator-testng/src/test/java/cucumber/examples/java/calculator/ShoppingStepdefs.java b/examples/java-calculator-testng/src/test/java/cucumber/examples/java/calculator/ShoppingStepdefs.java new file mode 100644 index 0000000000..a2fb69fdb5 --- /dev/null +++ b/examples/java-calculator-testng/src/test/java/cucumber/examples/java/calculator/ShoppingStepdefs.java @@ -0,0 +1,59 @@ +package cucumber.examples.java.calculator; + +import cucumber.api.Transformer; +import cucumber.api.java.en.Given; +import cucumber.api.java.en.Then; +import cucumber.api.java.en.When; +import cucumber.deps.com.thoughtworks.xstream.annotations.XStreamConverter; + +import java.util.List; + +import static org.junit.Assert.assertEquals; + +public class ShoppingStepdefs { + private RpnCalculator calc = new RpnCalculator(); + + @Given("^the following groceries:$") + public void the_following_groceries(List groceries) { + for (Grocery grocery : groceries) { + calc.push(grocery.price.value); + calc.push("+"); + } + } + + @When("^I pay (\\d+)$") + public void i_pay(int amount) { + calc.push(amount); + calc.push("-"); + } + + @Then("^my change should be (\\d+)$") + public void my_change_should_be_(int change) { + assertEquals(-calc.value().intValue(), change); + } + + public static class Grocery { + public String name; + @XStreamConverter(Price.Converter.class) + public Price price; + + public Grocery() { + super(); + } + } + + public static class Price { + public int value; + + public Price(int value) { + this.value = value; + } + + public static class Converter extends Transformer { + @Override + public Price transform(String value) { + return new Price(Integer.parseInt(value)); + } + } + } +} diff --git a/examples/java-calculator-testng/src/test/resources/cucumber/examples/java/calculator/basic_arithmetic.feature b/examples/java-calculator-testng/src/test/resources/cucumber/examples/java/calculator/basic_arithmetic.feature new file mode 100644 index 0000000000..9144af87f4 --- /dev/null +++ b/examples/java-calculator-testng/src/test/resources/cucumber/examples/java/calculator/basic_arithmetic.feature @@ -0,0 +1,35 @@ +@foo +Feature: Basic Arithmetic + + Background: A Calculator + Given a calculator I just turned on + + Scenario: Addition + # Try to change one of the values below to provoke a failure + When I add 4 and 5 + Then the result is 9 + + Scenario: Another Addition + # Try to change one of the values below to provoke a failure + When I add 4 and 7 + Then the result is 11 + + Scenario Outline: Many additions + Given the previous entries: + | first | second | operation | + | 1 | 1 | + | + | 2 | 1 | + | + When I press + + And I add and + And I press + + Then the result is + + Examples: Single digits + | a | b | c | + | 1 | 2 | 8 | + | 2 | 3 | 10 | + + Examples: Double digits + | a | b | c | + | 10 | 20 | 35 | + | 20 | 30 | 55 | diff --git a/examples/java-calculator-testng/src/test/resources/cucumber/examples/java/calculator/date_calculator.feature b/examples/java-calculator-testng/src/test/resources/cucumber/examples/java/calculator/date_calculator.feature new file mode 100644 index 0000000000..b5295ccbd1 --- /dev/null +++ b/examples/java-calculator-testng/src/test/resources/cucumber/examples/java/calculator/date_calculator.feature @@ -0,0 +1,8 @@ +Feature: Dates with different date formats + This feature shows you can have different date formats, as long as you annotate the + corresponding step definition method accordingly. + + Scenario: Determine past date + Given today is 2011-01-20 + When I ask if Jan 19, 2011 is in the past + Then the result should be yes diff --git a/examples/java-calculator-testng/src/test/resources/cucumber/examples/java/calculator/shopping.feature b/examples/java-calculator-testng/src/test/resources/cucumber/examples/java/calculator/shopping.feature new file mode 100644 index 0000000000..e69b004adf --- /dev/null +++ b/examples/java-calculator-testng/src/test/resources/cucumber/examples/java/calculator/shopping.feature @@ -0,0 +1,10 @@ +Feature: Shopping + + Scenario: Give correct change + Given the following groceries: + | name | price | + | milk | 9 | + | bread | 7 | + | soap | 5 | + When I pay 25 + Then my change should be 4 diff --git a/examples/java-calculator/src/test/java/cucumber/examples/java/calculator/RunCukesTest.java b/examples/java-calculator/src/test/java/cucumber/examples/java/calculator/RunCukesTest.java index f4037a296c..4430f92dbc 100644 --- a/examples/java-calculator/src/test/java/cucumber/examples/java/calculator/RunCukesTest.java +++ b/examples/java-calculator/src/test/java/cucumber/examples/java/calculator/RunCukesTest.java @@ -1,9 +1,10 @@ package cucumber.examples.java.calculator; +import cucumber.api.CucumberOptions; import cucumber.api.junit.Cucumber; import org.junit.runner.RunWith; @RunWith(Cucumber.class) -@Cucumber.Options(format = "json:target/cucumber-report.json") +@CucumberOptions(format = "json:target/cucumber-report.json") public class RunCukesTest { } diff --git a/examples/java-webbit-websockets-selenium/src/test/java/cucumber/examples/java/websockets/RunCukesTest.java b/examples/java-webbit-websockets-selenium/src/test/java/cucumber/examples/java/websockets/RunCukesTest.java index 4927a1cb5a..688e6ede6f 100644 --- a/examples/java-webbit-websockets-selenium/src/test/java/cucumber/examples/java/websockets/RunCukesTest.java +++ b/examples/java-webbit-websockets-selenium/src/test/java/cucumber/examples/java/websockets/RunCukesTest.java @@ -1,9 +1,10 @@ package cucumber.examples.java.websockets; +import cucumber.api.CucumberOptions; import cucumber.api.junit.Cucumber; import org.junit.runner.RunWith; @RunWith(Cucumber.class) -@Cucumber.Options(format = {"html:target/cukes"}) +@CucumberOptions(format = {"html:target/cukes"}) public class RunCukesTest { } diff --git a/examples/java-wicket/README.md b/examples/java-wicket/README.md index 0556f8b7ac..2b5399b809 100644 --- a/examples/java-wicket/README.md +++ b/examples/java-wicket/README.md @@ -13,15 +13,3 @@ mvn install This runs Cucumber features using the JUnit runner. The `@RunWith(Cucumber.class)` annotation on the `RunCukesIT` junit class kicks off Cucumber. - -### Java 7 - -The cargo-maven2-plugin 1.3.0 is built using Java 7. This means that this example will fail if used with older Java versions. -The error will be similar to: - -``` -[ERROR] Failed to execute goal org.codehaus.cargo:cargo-maven2-plugin:1.3.0:start (start-servlet-engine) on project java-wicket-test: Execution start-servlet-engine of goal org.codehaus.cargo:cargo-maven2-plugin:1.3.0:start failed: An API incompatibility was encountered while executing org.codehaus.cargo:cargo-maven2-plugin:1.3.0:start: java.lang.UnsupportedClassVersionError: org/eclipse/jetty/server/Server : Unsupported major.minor version 51.0 -``` - -The thing you want to look for is the ```Unsupported major.minor version 51.0``` that indicates that this has been -compiled using Java 7 and that you are executing it using an older Java version. diff --git a/examples/java-wicket/java-wicket-main/pom.xml b/examples/java-wicket/java-wicket-main/pom.xml index c5971abfcc..409066f060 100644 --- a/examples/java-wicket/java-wicket-main/pom.xml +++ b/examples/java-wicket/java-wicket-main/pom.xml @@ -7,7 +7,7 @@ 1.1.5-SNAPSHOT java-wicket-main - Examples: A Wicket application that can be tested with Selenium WebDriver + Examples: Wicket application war rentit diff --git a/examples/java-wicket/java-wicket-test/pom.xml b/examples/java-wicket/java-wicket-test/pom.xml index 59fea27967..a0cf048972 100644 --- a/examples/java-wicket/java-wicket-test/pom.xml +++ b/examples/java-wicket/java-wicket-test/pom.xml @@ -7,7 +7,7 @@ 1.1.5-SNAPSHOT java-wicket-test - Examples: A Selenium WebDriver example testing a Wicket application + Examples: Wicket application tested with Selenium diff --git a/examples/java-wicket/java-wicket-test/src/test/java/cucumber/examples/java/wicket/steps/RentACarSupport.java b/examples/java-wicket/java-wicket-test/src/test/java/cucumber/examples/java/wicket/steps/RentACarSupport.java index c50010cb14..98bf6f4565 100644 --- a/examples/java-wicket/java-wicket-test/src/test/java/cucumber/examples/java/wicket/steps/RentACarSupport.java +++ b/examples/java-wicket/java-wicket-test/src/test/java/cucumber/examples/java/wicket/steps/RentACarSupport.java @@ -3,11 +3,11 @@ import org.openqa.selenium.By; import org.openqa.selenium.WebDriver; import org.openqa.selenium.WebElement; -import org.openqa.selenium.firefox.FirefoxDriver; +import org.openqa.selenium.htmlunit.HtmlUnitDriver; public class RentACarSupport { public void createCars(int availableCars) { - WebDriver driver = new FirefoxDriver(); + WebDriver driver = new HtmlUnitDriver(); try { driver.get("http://localhost:9878/rentit/create"); @@ -23,7 +23,7 @@ public void createCars(int availableCars) { } public void rentACar() { - WebDriver driver = new FirefoxDriver(); + WebDriver driver = new HtmlUnitDriver(); try { driver.get("http://localhost:9878/rentit/rent"); @@ -35,7 +35,7 @@ public void rentACar() { } public int getAvailableNumberOfCars() { - WebDriver driver = new FirefoxDriver(); + WebDriver driver = new HtmlUnitDriver(); try { driver.get("http://localhost:9878/rentit/available"); diff --git a/examples/java-wicket/pom.xml b/examples/java-wicket/pom.xml index c6e51cb08e..4d6bbd02d4 100644 --- a/examples/java-wicket/pom.xml +++ b/examples/java-wicket/pom.xml @@ -9,7 +9,7 @@ java-wicket-selenium pom - Examples: A Wicket application tested with Selenium WebDriver + Examples: Wicket java-wicket-main java-wicket-test diff --git a/examples/spring-txn/src/test/java/cucumber/examples/spring/txn/RunCukesTest.java b/examples/spring-txn/src/test/java/cucumber/examples/spring/txn/RunCukesTest.java index d95b27957f..8d3fa8c5d5 100644 --- a/examples/spring-txn/src/test/java/cucumber/examples/spring/txn/RunCukesTest.java +++ b/examples/spring-txn/src/test/java/cucumber/examples/spring/txn/RunCukesTest.java @@ -1,9 +1,10 @@ package cucumber.examples.spring.txn; +import cucumber.api.CucumberOptions; import cucumber.api.junit.Cucumber; import org.junit.runner.RunWith; @RunWith(Cucumber.class) -@Cucumber.Options(glue = {"cucumber.examples.spring.txn", "cucumber.runtime.java.spring.hooks"}) +@CucumberOptions(glue = {"cucumber.examples.spring.txn", "cucumber.runtime.java.spring.hooks"}) public class RunCukesTest { } diff --git a/java/src/test/java/cucumber/runtime/java/formatter/RerunFormatterTest.java b/java/src/test/java/cucumber/runtime/java/formatter/RerunFormatterTest.java index eb71c3c17e..81511151e6 100644 --- a/java/src/test/java/cucumber/runtime/java/formatter/RerunFormatterTest.java +++ b/java/src/test/java/cucumber/runtime/java/formatter/RerunFormatterTest.java @@ -1,5 +1,6 @@ package cucumber.runtime.java.formatter; +import cucumber.api.CucumberOptions; import cucumber.api.junit.Cucumber; import gherkin.util.FixJava; import org.junit.Before; @@ -56,12 +57,12 @@ private void assertProducedOutputIsEmpty() { assertEquals(0, new File(TARGET_SAMPLE_OUTPUT).length()); } - @Cucumber.Options(format = "rerun:target/sample.txt", features = {"classpath:cucumber/runtime/java/formatter"}) + @CucumberOptions(format = "rerun:target/sample.txt", features = {"classpath:cucumber/runtime/java/formatter"}) private class RerunFormatterSampleFeatures { } - @Cucumber.Options(format = "rerun:target/sample.txt", features = {"classpath:cucumber/runtime/java/formatter/passing.feature"}) + @CucumberOptions(format = "rerun:target/sample.txt", features = {"classpath:cucumber/runtime/java/formatter/passing.feature"}) private class RerunFormatterPassingFeature { } diff --git a/junit/src/main/java/cucumber/api/junit/Cucumber.java b/junit/src/main/java/cucumber/api/junit/Cucumber.java index deba365193..64a777c44b 100644 --- a/junit/src/main/java/cucumber/api/junit/Cucumber.java +++ b/junit/src/main/java/cucumber/api/junit/Cucumber.java @@ -1,14 +1,15 @@ package cucumber.api.junit; +import cucumber.api.CucumberOptions; import cucumber.api.SnippetType; import cucumber.runtime.Runtime; import cucumber.runtime.RuntimeOptions; +import cucumber.runtime.RuntimeOptionsFactory; import cucumber.runtime.io.MultiLoader; import cucumber.runtime.io.ResourceLoader; import cucumber.runtime.junit.Assertions; import cucumber.runtime.junit.FeatureRunner; import cucumber.runtime.junit.JUnitReporter; -import cucumber.runtime.junit.RuntimeOptionsFactory; import cucumber.runtime.model.CucumberFeature; import cucumber.runtime.snippets.SummaryPrinter; import org.junit.runner.Description; @@ -53,7 +54,7 @@ public Cucumber(Class clazz) throws InitializationError, IOException { ClassLoader classLoader = clazz.getClassLoader(); Assertions.assertNoCucumberAnnotatedMethods(clazz); - RuntimeOptionsFactory runtimeOptionsFactory = new RuntimeOptionsFactory(clazz); + RuntimeOptionsFactory runtimeOptionsFactory = new RuntimeOptionsFactory(clazz, new Class[]{CucumberOptions.class, Options.class}); RuntimeOptions runtimeOptions = runtimeOptionsFactory.create(); ResourceLoader resourceLoader = new MultiLoader(classLoader); @@ -92,12 +93,17 @@ private void addChildren(List cucumberFeatures) throws Initiali } } + // TODO: When Options is removed we should remove reflection from RuntimeOptionsFactory. + /** * This annotation can be used to give additional hints to the {@link Cucumber} runner * about what to run. It provides similar options to the Cucumber command line used by {@link cucumber.api.cli.Main} + * + * @deprecated use {@link cucumber.api.CucumberOptions} instead. */ @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.TYPE}) + @Deprecated public static @interface Options { /** * @return true if this is a dry run diff --git a/junit/src/main/java/cucumber/runtime/junit/RuntimeOptionsFactory.java b/junit/src/main/java/cucumber/runtime/junit/RuntimeOptionsFactory.java deleted file mode 100644 index ff8f0a542e..0000000000 --- a/junit/src/main/java/cucumber/runtime/junit/RuntimeOptionsFactory.java +++ /dev/null @@ -1,150 +0,0 @@ -package cucumber.runtime.junit; - -import cucumber.api.junit.Cucumber; -import cucumber.runtime.RuntimeOptions; -import cucumber.runtime.io.MultiLoader; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - -public class RuntimeOptionsFactory { - private final Class clazz; - - public RuntimeOptionsFactory(Class clazz) { - this.clazz = clazz; - } - - public RuntimeOptions create() { - List args = new ArrayList(); - Cucumber.Options options = getOptions(clazz); - - addDryRun(options, args); - addMonochrome(options, args); - addGlue(options, args, clazz); - addTags(options, args); - addFormats(options, args); - addFeatures(options, args, clazz); - addStrict(options, args); - addName(options, args); - addDotCucumber(options, args); - addSnippets(options, args); - - RuntimeOptions runtimeOptions = new RuntimeOptions(System.getProperties(), args.toArray(new String[args.size()])); - - return runtimeOptions; - } - - private void addDotCucumber(Cucumber.Options options, List args) { - if (options != null) { - if (!options.dotcucumber().isEmpty()) { - args.add("--dotcucumber"); - args.add(options.dotcucumber()); - } - } - } - - private void addName(Cucumber.Options options, List args) { - if (options != null) { - if (options.name().length != 0) { - for (String name : options.name()) { - args.add("--name"); - args.add(name); - } - } - } - } - - private void addSnippets(Cucumber.Options options, List args) { - if (options != null) { - args.add("--snippets"); - args.add(options.snippets().toString()); - } - } - - private Cucumber.Options getOptions(Class clazz) { - return clazz.getAnnotation(Cucumber.Options.class); - } - - private void addDryRun(Cucumber.Options options, List args) { - if (options != null) { - if (options.dryRun()) { - args.add("--dry-run"); - } - } - } - - private void addMonochrome(Cucumber.Options options, List args) { - if (options != null) { - if (options.monochrome() || runningInEnvironmentWithoutAnsiSupport()) { - args.add("--monochrome"); - } - } - } - - private boolean runningInEnvironmentWithoutAnsiSupport() { - boolean intelliJidea = System.getProperty("idea.launcher.bin.path") != null; - // TODO: What does Eclipse use? - return intelliJidea; - } - - private void addGlue(Cucumber.Options options, List args, Class clazz) { - if (options != null && options.glue().length != 0) { - for (String glue : options.glue()) { - args.add("--glue"); - args.add(glue); - } - } else { - args.add("--glue"); - args.add(MultiLoader.CLASSPATH_SCHEME + packagePath(clazz)); - } - } - - private void addTags(Cucumber.Options options, List args) { - if (options != null) { - for (String tags : options.tags()) { - args.add("--tags"); - args.add(tags); - } - } - } - - private void addFormats(Cucumber.Options options, List args) { - if (options != null && options.format().length != 0) { - for (String format : options.format()) { - args.add("--format"); - args.add(format); - } - } else { - args.add("--format"); - args.add("null"); - } - } - - private void addFeatures(Cucumber.Options options, List args, Class clazz) { - if (options != null && options.features().length != 0) { - Collections.addAll(args, options.features()); - } else { - args.add(MultiLoader.CLASSPATH_SCHEME + packagePath(clazz)); - } - } - - private void addStrict(Cucumber.Options options, List args) { - if (options != null && options.strict()) { - args.add("--strict"); - } - } - - static String packagePath(Class clazz) { - return packagePath(packageName(clazz.getName())); - } - - static String packagePath(String packageName) { - return packageName.replace('.', '/'); - } - - static String packageName(String className) { - return className.substring(0, Math.max(0, className.lastIndexOf("."))); - } - -} diff --git a/junit/src/test/java/cucumber/runtime/junit/CucumberTest.java b/junit/src/test/java/cucumber/runtime/junit/CucumberTest.java index 0b65d2a5a1..99e82c5008 100644 --- a/junit/src/test/java/cucumber/runtime/junit/CucumberTest.java +++ b/junit/src/test/java/cucumber/runtime/junit/CucumberTest.java @@ -1,6 +1,7 @@ package cucumber.runtime.junit; import cucumber.annotation.DummyWhen; +import cucumber.api.CucumberOptions; import cucumber.api.junit.Cucumber; import cucumber.runtime.CucumberException; import org.junit.After; @@ -11,7 +12,9 @@ import java.io.File; import java.io.IOException; +import java.util.List; +import static java.util.Collections.emptyList; import static org.junit.Assert.assertEquals; import static org.junit.Assert.fail; @@ -35,7 +38,7 @@ public void ensureOriginalDirectory() { @Test public void finds_features_based_on_implicit_package() throws IOException, InitializationError { - Cucumber cucumber = new Cucumber(ImplicitFeaturePath.class); + Cucumber cucumber = new Cucumber(ImplicitFeatureAndGluePath.class); assertEquals(3, cucumber.getChildren().size()); assertEquals("Feature: FA", cucumber.getChildren().get(0).getName()); } @@ -58,9 +61,10 @@ public void testThatParsingErrorsIsNicelyReported() throws Exception { } @Test - public void finds_no_features_when_explicit_package_has_nothnig() throws IOException, InitializationError { + public void finds_no_features_when_explicit_feature_path_has_no_features() throws IOException, InitializationError { Cucumber cucumber = new Cucumber(ExplicitFeaturePathWithNoFeatures.class); - assertEquals(0, cucumber.getChildren().size()); + List children = cucumber.getChildren(); + assertEquals(emptyList(), children); } @RunWith(Cucumber.class) @@ -91,18 +95,18 @@ public void no_stepdefs_in_cucumber_runner_invalid() { Assertions.assertNoCucumberAnnotatedMethods(RunCukesTestInvalid.class); } - private class ImplicitFeaturePath { + private class ImplicitFeatureAndGluePath { } - @Cucumber.Options(features = {"classpath:cucumber/runtime/junit"}) + @CucumberOptions(features = {"classpath:cucumber/runtime/junit"}) private class ExplicitFeaturePath { } - @Cucumber.Options(features = {"classpath:gibber/ish"}) + @CucumberOptions(features = {"classpath:gibber/ish"}) private class ExplicitFeaturePathWithNoFeatures { } - @Cucumber.Options(features = {"classpath:cucumber/runtime/error/lexer_error.feature"}) + @CucumberOptions(features = {"classpath:cucumber/runtime/error/lexer_error.feature"}) private class LexerErrorFeature { } diff --git a/jython/src/main/java/cucumber/runtime/jython/JythonBackend.java b/jython/src/main/java/cucumber/runtime/jython/JythonBackend.java index 922933b442..0704b099ef 100644 --- a/jython/src/main/java/cucumber/runtime/jython/JythonBackend.java +++ b/jython/src/main/java/cucumber/runtime/jython/JythonBackend.java @@ -1,5 +1,6 @@ package cucumber.runtime.jython; +import cucumber.api.Scenario; import cucumber.runtime.Backend; import cucumber.runtime.CucumberException; import cucumber.runtime.Glue; @@ -9,13 +10,12 @@ import cucumber.runtime.snippets.FunctionNameSanitizer; import cucumber.runtime.snippets.SnippetGenerator; import gherkin.formatter.model.Step; -import org.python.core.PyException; -import org.python.core.PyInstance; -import org.python.core.PyObject; -import org.python.core.PyString; +import org.python.core.*; import org.python.util.PythonInterpreter; import java.io.IOException; +import java.io.PrintWriter; +import java.io.StringWriter; import java.util.List; public class JythonBackend implements Backend { @@ -87,10 +87,26 @@ public void addAfterHook(PyInstance hookDefinition) { glue.addAfterHook(new JythonHookDefinition(this, hookDefinition)); } - public void executeHook(PyInstance hookDefinition, Object[] scenarioResults) { - PyObject[] pyArgs = new PyObject[1]; - pyArgs[0] = pyWorld; - hookDefinition.invoke("execute", pyArgs); + public void executeHook(PyInstance hookDefinition, Scenario scenario) { + try { + // Try to pass the scenario + hookDefinition.invoke("execute", pyWorld, Py.java2py(scenario)); + } catch (PyException e) { + if (getStacktrace(e).contains("takes exactly 1 argument (2 given)")) { + // The stepdef doesn't want the scenario + hookDefinition.invoke("execute", pyWorld); + } else { + // Some other error. Just rethrow. + throw e; + } + } + } + + private String getStacktrace(PyException e) { + StringWriter sw = new StringWriter(); + PrintWriter pw = new PrintWriter(sw); + e.printStackTrace(pw); + return sw.getBuffer().toString(); } public void execute(PyInstance stepdef, Object[] args) throws Throwable { diff --git a/jython/src/main/java/cucumber/runtime/jython/JythonHookDefinition.java b/jython/src/main/java/cucumber/runtime/jython/JythonHookDefinition.java index 0cda040df5..a92d3061b7 100644 --- a/jython/src/main/java/cucumber/runtime/jython/JythonHookDefinition.java +++ b/jython/src/main/java/cucumber/runtime/jython/JythonHookDefinition.java @@ -23,7 +23,7 @@ public JythonHookDefinition(JythonBackend backend, PyInstance hookDefinition) { @Override public void execute(Scenario scenario) throws Throwable { - backend.executeHook(hookDefinition, new Object[]{scenario}); + backend.executeHook(hookDefinition, scenario); } @Override diff --git a/jython/src/test/resources/cucumber/runtime/jython/step_definitions/cuke_steps.py b/jython/src/test/resources/cucumber/runtime/jython/step_definitions/cuke_steps.py index 458ff38383..ea1749f10c 100644 --- a/jython/src/test/resources/cucumber/runtime/jython/step_definitions/cuke_steps.py +++ b/jython/src/test/resources/cucumber/runtime/jython/step_definitions/cuke_steps.py @@ -24,4 +24,8 @@ def I_have_cukes_in_my_belly(self, arg1): if (self.n != val): raise(Exception("Default cukes were %d, not %d" % (self.n, val))) +@After() +def we_can_get_the_scenario(self, scenario): + if(scenario.getStatus() != 'passed'): + print("Oh no!") diff --git a/picocontainer/src/test/java/cucumber/runtime/java/picocontainer/RunCukesTest.java b/picocontainer/src/test/java/cucumber/runtime/java/picocontainer/RunCukesTest.java index a80691528a..eb9a1b747f 100644 --- a/picocontainer/src/test/java/cucumber/runtime/java/picocontainer/RunCukesTest.java +++ b/picocontainer/src/test/java/cucumber/runtime/java/picocontainer/RunCukesTest.java @@ -4,6 +4,5 @@ import org.junit.runner.RunWith; @RunWith(Cucumber.class) -//@Cucumber.Options(features = "classpath:cucumber/runtime/java/picocontainer/dates.feature:3:11") public class RunCukesTest { } diff --git a/pom.xml b/pom.xml index 417ad693ca..7d1d2ca7e6 100644 --- a/pom.xml +++ b/pom.xml @@ -1,4 +1,5 @@ - + 4.0.0 info.cukes cucumber-jvm @@ -124,6 +125,11 @@ cucumber-junit ${project.version} + + info.cukes + cucumber-testng + ${project.version} + info.cukes cucumber-picocontainer @@ -330,12 +336,12 @@ selenium-server ${selenium.version} - - org.seleniumhq.selenium - selenium-java - ${selenium.version} - test - + + org.seleniumhq.selenium + selenium-java + ${selenium.version} + test + org.webbitserver webbit @@ -388,9 +394,9 @@ - de.akquinet.jbosscc - jbosscc-needle - ${needle.version} + de.akquinet.jbosscc + jbosscc-needle + ${needle.version} @@ -480,8 +486,10 @@ examples + examples/java-wicket examples/spring-txn examples/java-calculator + examples/java-calculator-testng examples/groovy-calculator examples/scala-calculator examples/java-webbit-websockets-selenium @@ -509,6 +517,7 @@ org.apache.maven.plugins maven-release-plugin + 2.4.1 v@{project.version} @@ -517,6 +526,7 @@ org.apache.maven.plugins maven-gpg-plugin + 1.4 true @@ -605,7 +615,7 @@ org.apache.maven.plugins maven-compiler-plugin - 3.0 + 3.1 UTF-8 1.6 @@ -623,7 +633,7 @@ org.apache.maven.plugins maven-javadoc-plugin - 2.9 + 2.9.1 true false @@ -656,19 +666,19 @@ org.apache.maven.plugins maven-project-info-reports-plugin - 2.5 + 2.7 org.apache.maven.plugins maven-release-plugin - 2.3.2 + 2.4.1 org.apache.maven.plugins maven-shade-plugin - 1.7.1 + 2.1 package @@ -685,13 +695,13 @@ org.apache.maven.plugins maven-site-plugin - 3.1 + 3.3 org.apache.maven.plugins maven-source-plugin - 2.2 + 2.2.1 bind-sources @@ -705,7 +715,7 @@ org.apache.maven.plugins maven-surefire-plugin - 2.12.2 + 2.16 -Duser.language=en -Xmx1024m @@ -720,25 +730,25 @@ net.alchim31.maven scala-maven-plugin - 3.1.0 + 3.1.5 org.codehaus.gmaven gmaven-plugin - 1.4 + 1.5 org.codehaus.mojo build-helper-maven-plugin - 1.7 + 1.8 com.theoryinpractise clojure-maven-plugin - 1.3.11 + 1.3.17 @@ -748,7 +758,7 @@ org.apache.maven.wagon wagon-ssh - 2.2 + 2.4 diff --git a/testng/pom.xml b/testng/pom.xml index 7a7f8bb2aa..5e7da21f3c 100644 --- a/testng/pom.xml +++ b/testng/pom.xml @@ -15,7 +15,7 @@ info.cukes - cucumber-junit + cucumber-core org.testng diff --git a/testng/src/main/java/cucumber/api/testng/AbstractTestNGCucumberTests.java b/testng/src/main/java/cucumber/api/testng/AbstractTestNGCucumberTests.java index 7a5a43d428..eb98e31bad 100644 --- a/testng/src/main/java/cucumber/api/testng/AbstractTestNGCucumberTests.java +++ b/testng/src/main/java/cucumber/api/testng/AbstractTestNGCucumberTests.java @@ -1,11 +1,12 @@ package cucumber.api.testng; +import cucumber.api.CucumberOptions; import cucumber.runtime.CucumberException; import cucumber.runtime.Runtime; import cucumber.runtime.RuntimeOptions; import cucumber.runtime.io.MultiLoader; import cucumber.runtime.io.ResourceLoader; -import cucumber.runtime.junit.RuntimeOptionsFactory; +import cucumber.runtime.RuntimeOptionsFactory; import org.testng.IHookCallBack; import org.testng.IHookable; import org.testng.ITestResult; @@ -21,7 +22,7 @@ public AbstractTestNGCucumberTests() { classLoader = getClass().getClassLoader(); resourceLoader = new MultiLoader(classLoader); - RuntimeOptionsFactory runtimeOptionsFactory = new RuntimeOptionsFactory(getClass()); + RuntimeOptionsFactory runtimeOptionsFactory = new RuntimeOptionsFactory(getClass(), new Class[]{CucumberOptions.class}); RuntimeOptions runtimeOptions = runtimeOptionsFactory.create(); reporter = new TestNgReporter(System.out);