' => nil})
39 |
40 | py_string_with_replaced_arg.to_s.should == "''"
41 | end
42 |
43 | end
44 |
45 | end
46 | end
47 | end
--------------------------------------------------------------------------------
/specs/cucumber/formatter/html/index.html:
--------------------------------------------------------------------------------
1 |
3 |
4 |
5 |
6 |
7 | Cucumber results
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 | Feature: Sample
17 |
18 | Scenario: Missing
19 | Given missing
20 |
21 | Scenario: Passing
22 | Given passing
23 |
24 | | 1 |
25 | 22 |
26 |
27 |
28 | | 4444 |
29 | 333 |
30 |
31 |
32 | Scenario: Failing
33 | Given failing
34 | """
35 | Aslak
36 | Hellesøy
37 | """
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
--------------------------------------------------------------------------------
/examples/tickets/features/step_definitons/tickets_steps.rb:
--------------------------------------------------------------------------------
1 | require 'spec/expectations'
2 |
3 | World do
4 | Object.new
5 | end
6 |
7 | After do |scenario|
8 | end
9 |
10 | Given "be_empty" do
11 | [1,2].should_not be_empty
12 | end
13 |
14 | Given "nested step is called" do
15 | Given "I like mushroom", Cucumber::Ast::Table.new([
16 | %w{sponge bob},
17 | %w{is cool}
18 | ])
19 | end
20 |
21 | Given 'nested step is called using text table' do
22 | Given "I like mushroom", table(%{
23 | | sponge | bob |
24 | | is | cool |
25 | })
26 |
27 | # Alternative syntax (file and line will be reported on parse error)
28 | # Given "I like mushroom", table(<<-EOT, __FILE__, __LINE__)
29 | # | sponge | bob |
30 | # | is | cool
31 | # EOT
32 | end
33 |
34 | Given "I like $what" do |what, table|
35 | @magic = what
36 | @tool = table.raw[0][0]
37 | end
38 |
39 | Then "nested step should be executed" do
40 | @magic.should == 'mushroom'
41 | @tool.should == 'sponge'
42 | end
43 |
44 | Given /^the following table$/ do |table|
45 | @table = table
46 | end
47 |
48 | Then /^I should be (\w+) in (\w+)$/ do |key, value|
49 | hash = @table.hashes[0]
50 | hash[key].should == value
51 | end
52 |
53 | Then /^I should see a multiline string like$/ do |s|
54 | s.should == %{A string
55 | that spans
56 | several lines}
57 | end
58 |
59 | Given /^the following users exist in the system$/ do |table|
60 | table.hashes[0][:role_assignments].should == 'HUMAN RESOURCE'
61 | end
62 |
63 | Given /^I have a pending step$/ do
64 | pending
65 | end
66 |
67 | When /^I run this feature with the progress format$/ do
68 | pending
69 | end
70 |
71 | Then /^I should get a no method error for 'backtrace_line'$/ do
72 | pending
73 | end
74 |
--------------------------------------------------------------------------------
/features/report_called_undefined_steps.feature:
--------------------------------------------------------------------------------
1 | Feature: Cucumber command line
2 | In order to find out what step definitions need to be implemented
3 | Developers should always see what step definition is missing
4 |
5 | Scenario: Get info at arbitrary levels of nesting
6 | When I run cucumber features/call_undefined_step_from_step_def.feature
7 | Then it should pass with
8 | """
9 | Feature: Calling undefined step
10 |
11 | Scenario: Call directly # features/call_undefined_step_from_step_def.feature:3
12 | Given a step definition that calls an undefined step # features/step_definitions/sample_steps.rb:19
13 | Undefined step: "this does not exist" (Cucumber::Undefined)
14 | ./features/step_definitions/sample_steps.rb:20:in `/^a step definition that calls an undefined step$/'
15 | features/call_undefined_step_from_step_def.feature:4:in `Given a step definition that calls an undefined step'
16 |
17 | Scenario: Call via another # features/call_undefined_step_from_step_def.feature:6
18 | Given call step "a step definition that calls an undefined step" # features/step_definitions/sample_steps.rb:23
19 | Undefined step: "this does not exist" (Cucumber::Undefined)
20 | ./features/step_definitions/sample_steps.rb:20:in `/^a step definition that calls an undefined step$/'
21 | features/call_undefined_step_from_step_def.feature:7:in `Given call step "a step definition that calls an undefined step"'
22 |
23 | 2 scenarios
24 | 2 undefined steps
25 |
26 | You can implement step definitions for missing steps with these snippets:
27 |
28 | Given /^this does not exist$/ do
29 | pending
30 | end
31 |
32 |
33 | """
34 |
35 |
--------------------------------------------------------------------------------
/lib/cucumber/ast/step_collection.rb:
--------------------------------------------------------------------------------
1 | module Cucumber
2 | module Ast
3 | # Holds an Array of Step or StepDefinition
4 | class StepCollection
5 | include Enumerable
6 |
7 | def initialize(steps)
8 | @steps = steps
9 | @steps.each{|step| step.step_collection = self}
10 | end
11 |
12 | def accept(visitor, &proc)
13 | @steps.each do |step|
14 | visitor.visit_step(step) if proc.nil? || proc.call(step)
15 | end
16 | end
17 |
18 | def step_invocations(background = false)
19 | StepCollection.new(@steps.map{ |step|
20 | i = step.step_invocation
21 | i.background = background
22 | i
23 | })
24 | end
25 |
26 | def step_invocations_from_cells(cells)
27 | @steps.map{|step| step.step_invocation_from_cells(cells)}
28 | end
29 |
30 | # Duplicates this instance and adds +step_invocations+ to the end
31 | def dup(step_invocations = [])
32 | StepCollection.new(@steps + step_invocations)
33 | end
34 |
35 | def each(&proc)
36 | @steps.each(&proc)
37 | end
38 |
39 | def previous_step(step)
40 | i = @steps.index(step) || -1
41 | @steps[i-1]
42 | end
43 |
44 | def matches_lines?(lines)
45 | @steps.detect {|step| step.matches_lines?(lines)}
46 | end
47 |
48 | def empty?
49 | @steps.empty?
50 | end
51 |
52 | def max_line_length(feature_element)
53 | lengths = (@steps + [feature_element]).map{|e| e.text_length}
54 | lengths.max
55 | end
56 |
57 | def exception
58 | @exception ||= ((failed = @steps.detect {|step| step.exception}) && failed.exception)
59 | end
60 |
61 | def to_sexp
62 | @steps.map{|step| step.to_sexp}
63 | end
64 | end
65 | end
66 | end
--------------------------------------------------------------------------------
/lib/cucumber/ast/py_string.rb:
--------------------------------------------------------------------------------
1 | module Cucumber
2 | module Ast
3 | # Represents an inline argument in a step. Example:
4 | #
5 | # Given the message
6 | # """
7 | # I like
8 | # Cucumber sandwich
9 | # """
10 | #
11 | # The text between the pair of """ is stored inside a PyString,
12 | # which is yielded to the StepDefinition block as the last argument.
13 | #
14 | # The StepDefinition can then access the String via the #to_s method. In the
15 | # example above, that would return: "I like\nCucumber sandwich"
16 | #
17 | # Note how the indentation from the source is stripped away.
18 | #
19 | class PyString
20 |
21 | def self.default_arg_name
22 | "string"
23 | end
24 |
25 | def initialize(start_line, end_line, string, quotes_indent)
26 | @start_line, @end_line = start_line, end_line
27 | @string, @quotes_indent = string.gsub(/\\"/, '"'), quotes_indent
28 | @status = :passed
29 | end
30 |
31 | def status=(status)
32 | @status = status
33 | end
34 |
35 | def to_s
36 | @string.indent(-@quotes_indent)
37 | end
38 |
39 | def matches_lines?(lines)
40 | lines.detect{|l| l >= @start_line && l <= @end_line}
41 | end
42 |
43 | def accept(visitor)
44 | visitor.visit_py_string(to_s, @status)
45 | end
46 |
47 | def arguments_replaced(arguments) #:nodoc:
48 | string = @string
49 | arguments.each do |name, value|
50 | value ||= ''
51 | string = string.gsub(name, value)
52 | end
53 | PyString.new(@start_line, @end_line, string, @quotes_indent)
54 | end
55 |
56 | # For testing only
57 | def to_sexp #:nodoc:
58 | [:py_string, to_s]
59 | end
60 | end
61 | end
62 | end
63 |
--------------------------------------------------------------------------------
/lib/cucumber/cli/language_help_formatter.rb:
--------------------------------------------------------------------------------
1 | require 'cucumber/formatter/pretty'
2 |
3 | module Cucumber
4 | module Cli
5 | class LanguageHelpFormatter < Formatter::Pretty
6 | INCOMPLETE = %{
7 | The Cucumber grammar has evolved since this translation was written.
8 | Please help us complete the translation by translating the missing words in
9 |
10 | #{Cucumber::LANGUAGE_FILE}
11 |
12 | Then contribute back to the Cucumber project. Details here:
13 | http://wiki.github.com/aslakhellesoy/cucumber/spoken-languages
14 | }
15 |
16 | def self.list_languages(io)
17 | raw = Cucumber::LANGUAGES.keys.sort.map do |lang|
18 | [lang, Cucumber::LANGUAGES[lang]['name'], Cucumber::LANGUAGES[lang]['native']]
19 | end
20 | table = Ast::Table.new(raw)
21 | new(nil, io, {:check_lang=>true}, '').visit_multiline_arg(table)
22 | end
23 |
24 | def self.list_keywords(io, lang)
25 | raw = Cucumber::KEYWORD_KEYS.map do |key|
26 | [key, Cucumber::LANGUAGES[lang][key]]
27 | end
28 | table = Ast::Table.new(raw)
29 | new(nil, io, {:incomplete => Cucumber.language_incomplete?(lang)}, '').visit_multiline_arg(table)
30 | end
31 |
32 | def visit_multiline_arg(table)
33 | if @options[:incomplete]
34 | @io.puts(format_string(INCOMPLETE, :failed))
35 | end
36 | super
37 | end
38 |
39 | def visit_table_row(table_row)
40 | @col = 1
41 | super
42 | end
43 |
44 | def visit_table_cell_value(value, width, status)
45 | if @col == 1
46 | if(@options[:check_lang])
47 | @incomplete = Cucumber.language_incomplete?(value)
48 | end
49 | status = :comment
50 | elsif @incomplete
51 | status = :undefined
52 | end
53 |
54 | @col += 1
55 | super(value, width, status)
56 | end
57 | end
58 | end
59 | end
--------------------------------------------------------------------------------
/lib/cucumber/world.rb:
--------------------------------------------------------------------------------
1 | module Cucumber
2 | # All steps are run in the context of an object that extends this module
3 | module World
4 | class << self
5 | def alias_adverb(adverb)
6 | alias_method adverb, :__cucumber_invoke
7 | end
8 | end
9 |
10 | attr_writer :__cucumber_step_mother, :__cucumber_visitor, :__cucumber_current_step
11 |
12 | # Call a step from within a step definition
13 | def __cucumber_invoke(name, multiline_argument=nil) #:nodoc:
14 | begin
15 | step_match = @__cucumber_step_mother.step_match(name)
16 | step_match.invoke(self, multiline_argument)
17 | rescue Exception => e
18 | e.nested! if Undefined === e
19 | @__cucumber_current_step.exception = e
20 | raise e
21 | end
22 | end
23 |
24 | def table(text, file=nil, line_offset=0)
25 | @table_parser ||= Parser::TableParser.new
26 | @table_parser.parse_or_fail(text.strip, file, line_offset)
27 | end
28 |
29 | # Output +announcement+ alongside the formatted output.
30 | # This is an alternative to using Kernel#puts - it will display
31 | # nicer, and in all outputs (in case you use several formatters)
32 | #
33 | # Beware that the output will be printed *before* the corresponding
34 | # step. This is because the step itself will not be printed until
35 | # after it has run, so it can be coloured according to its status.
36 | def announce(announcement)
37 | @__cucumber_visitor.announce(announcement)
38 | end
39 |
40 | def pending(message = "TODO")
41 | if block_given?
42 | begin
43 | yield
44 | rescue Exception => e
45 | raise Pending.new(message)
46 | end
47 | raise Pending.new("Expected pending '#{message}' to fail. No Error was raised. No longer pending?")
48 | else
49 | raise Pending.new(message)
50 | end
51 | end
52 | end
53 | end
--------------------------------------------------------------------------------
/specs/cucumber/core_ext/string_spec.rb:
--------------------------------------------------------------------------------
1 | require File.dirname(__FILE__) + '/../../../lib/cucumber/core_ext/string'
2 |
3 | describe String, "#gzub" do
4 | it "should format groups with format string" do
5 | "I ate 1 egg this morning".gzub(/I (\w+) (\d+) (\w+) this (\w+)/, "%s").should ==
6 | "I ate 1 egg this morning"
7 | end
8 |
9 | it "should format groups with format string when there are dupes" do
10 | "I bob 1 bo this bobs".gzub(/I (\w+) (\d+) (\w+) this (\w+)/, "%s").should ==
11 | "I bob 1 bo this bobs"
12 | end
13 |
14 | it "should format groups with block" do
15 | f = "I ate 1 egg this morning".gzub(/I (\w+) (\d+) (\w+) this (\w+)/) do |m|
16 | "#{m}"
17 | end
18 | f.should == "I ate 1 egg this morning"
19 | end
20 |
21 | it "should format groups with proc object" do
22 | proc = lambda do |m|
23 | "#{m}"
24 | end
25 | f = "I ate 1 egg this morning".gzub(/I (\w+) (\d+) (\w+) this (\w+)/, proc)
26 | f.should == "I ate 1 egg this morning"
27 | end
28 |
29 | it "should format groups with block with not all placeholders having a value" do
30 | f = "another member named Bob joins the group".gzub(/(a|another) (user|member) named ([^ ]+)( who is not logged in)?/) do |m|
31 | "#{m}"
32 | end
33 | f.should == "another member named Bob joins the group"
34 | end
35 |
36 | it "should format match groups in a textile table row" do
37 | f = "I ate 1 egg this morning".gzub(/I (\w+) (\d+) (\w+) this (\w+)/) do |m|
38 | "#{m}"
39 | end
40 | f.should == "I ate 1 egg this morning"
41 | end
42 | end
43 |
--------------------------------------------------------------------------------
/specs/cucumber/ast/scenario_spec.rb:
--------------------------------------------------------------------------------
1 | require File.dirname(__FILE__) + '/../../spec_helper'
2 | require 'cucumber/step_mother'
3 | require 'cucumber/ast'
4 |
5 | module Cucumber
6 | module Ast
7 | describe Scenario do
8 | before do
9 | @step_mother = Object.new
10 | @step_mother.extend(StepMother)
11 | $x = $y = nil
12 | @step_mother.Given /y is (\d+)/ do |n|
13 | $y = n.to_i
14 | end
15 | @visitor = Visitor.new(@step_mother)
16 | @visitor.options = {}
17 | end
18 |
19 | it "should skip steps when previous is not passed" do
20 | scenario = Scenario.new(
21 | background=nil,
22 | comment=Comment.new(""),
23 | tags=Tags.new(98, []),
24 | line=99,
25 | keyword="",
26 | name="",
27 | steps=[
28 | Step.new(7, "Given", "this is missing"),
29 | Step.new(8, "Given", "y is 5")
30 | ])
31 | @visitor.visit_feature_element(scenario)
32 |
33 | $y.should == nil
34 | end
35 |
36 | it "should be at exact line" do
37 | s = Scenario.new(background=nil, comment=Comment.new(""),
38 | tags=Tags.new(44, []), 45, keyword="", name="", steps=[])
39 |
40 | s.should be_matches_lines([44])
41 | s.should be_matches_lines([45])
42 | end
43 |
44 | it "should be at line if tags or steps are" do
45 | s = Scenario.new(
46 | background=nil,
47 | comment=Comment.new(""),
48 | tags=Tags.new(43, []),
49 | line=45,
50 | keyword="",
51 | name="",
52 | steps=[
53 | Step.new(46, "Given", ""),
54 | Step.new(47, "Given", ""),
55 | Step.new(48, "Given", ""),
56 | ]
57 | )
58 |
59 | s.should be_matches_lines([43])
60 | s.should be_matches_lines([47])
61 | s.should_not be_matches_lines([49])
62 | end
63 | end
64 | end
65 | end
66 |
--------------------------------------------------------------------------------
/specs/cucumber/ast/feature_factory.rb:
--------------------------------------------------------------------------------
1 | require 'cucumber/ast'
2 | require 'cucumber/step_mother'
3 |
4 | module Cucumber
5 | module Ast
6 | module FeatureFactory
7 | class MyWorld
8 | def flunk
9 | raise "I flunked"
10 | end
11 | end
12 |
13 | def create_feature(step_mother)
14 | step_mother.extend(StepMother)
15 | step_mother.Given /^a (.*) step with an inline arg:$/ do |what, table|
16 | end
17 | step_mother.Given /^a (.*) step$/ do |what|
18 | flunk if what == 'failing'
19 | end
20 | step_mother.World do
21 | MyWorld.new
22 | end
23 |
24 | table = Ast::Table.new([
25 | %w{1 22 333},
26 | %w{4444 55555 666666}
27 | ])
28 | py_string = Ast::PyString.new(21, 22, %{
29 | I like
30 | Cucumber sandwich
31 | }, 10)
32 |
33 | background = Ast::Background.new(Ast::Comment.new(""), 2, "Background:", "",
34 | [
35 | Step.new(3, "Given", "a passing step")
36 | ]
37 | )
38 |
39 | f = Ast::Feature.new(
40 | background,
41 | Ast::Comment.new("# My feature comment\n"),
42 | Ast::Tags.new(6, ['one', 'two']),
43 | "Pretty printing",
44 | [Ast::Scenario.new(
45 | background,
46 | Ast::Comment.new(" # My scenario comment \n# On two lines \n"),
47 | Ast::Tags.new(8, ['three', 'four']),
48 | 9,
49 | "Scenario:", "A Scenario",
50 | [
51 | Step.new(10, "Given", "a passing step with an inline arg:", table),
52 | Step.new(11, "Given", "a happy step with an inline arg:", py_string),
53 | Step.new(12, "Given", "a failing step")
54 | ]
55 | )]
56 | )
57 | f.file = 'features/pretty_printing.feature'
58 | f.features = Features.new
59 | f
60 | end
61 | end
62 | end
63 | end
--------------------------------------------------------------------------------
/lib/cucumber.rb:
--------------------------------------------------------------------------------
1 | $:.unshift(File.dirname(__FILE__)) unless
2 | $:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
3 |
4 | require 'yaml'
5 | require 'cucumber/platform'
6 | require 'cucumber/parser'
7 | require 'cucumber/version'
8 | require 'cucumber/step_mother'
9 | require 'cucumber/cli/main'
10 | require 'cucumber/broadcaster'
11 | require 'cucumber/core_ext/exception'
12 |
13 | module Cucumber
14 | KEYWORD_KEYS = %w{name native encoding feature background scenario scenario_outline examples given when then but}
15 |
16 | class << self
17 | attr_reader :lang
18 |
19 | def load_language(lang) #:nodoc:
20 | return if @lang
21 | @lang = lang
22 | alias_step_definitions(lang)
23 | Parser.load_parser(keyword_hash)
24 | end
25 |
26 | def language_incomplete?(lang=@lang)
27 | KEYWORD_KEYS.detect{|key| keyword_hash(lang)[key].nil?}
28 | end
29 |
30 | # File mode that accounts for Ruby platform and current language
31 | def file_mode(m)
32 | Cucumber::RUBY_1_9 ? "#{m}:#{keyword_hash['encoding']}" : m
33 | end
34 |
35 | # Returns a Hash of the currently active
36 | # language, or for a specific language if +lang+ is
37 | # specified.
38 | def keyword_hash(lang=@lang)
39 | LANGUAGES[lang]
40 | end
41 |
42 | def alias_step_definitions(lang) #:nodoc:
43 | keywords = %w{given when then and but}.map{|keyword| keyword_hash(lang)[keyword]}
44 | alias_steps(keywords)
45 | end
46 |
47 | # Sets up additional method aliases for Given, When and Then.
48 | # This does *not* affect how feature files are parsed. If you
49 | # want to create aliases in the parser, you have to do this in
50 | # languages.yml. For example:
51 | #
52 | # and: And|With
53 | def alias_steps(keywords)
54 | keywords.each do |adverb|
55 | StepMother.alias_adverb(adverb)
56 | World.alias_adverb(adverb)
57 | end
58 | end
59 | end
60 |
61 | # Make sure we always have English aliases
62 | alias_step_definitions('en')
63 | end
--------------------------------------------------------------------------------
/lib/cucumber/ast/feature.rb:
--------------------------------------------------------------------------------
1 | module Cucumber
2 | module Ast
3 | # Represents the root node of a parsed feature.
4 | class Feature
5 | attr_accessor :file
6 | attr_writer :features, :lines
7 |
8 | def initialize(background, comment, tags, name, feature_elements)
9 | @background, @comment, @tags, @name, @feature_elements = background, comment, tags, name, feature_elements
10 | @lines = []
11 |
12 | @feature_elements.each do |feature_element|
13 | feature_element.feature = self
14 | end
15 | end
16 |
17 | def accept(visitor)
18 | visitor.current_feature_lines = @lines
19 | visitor.visit_comment(@comment)
20 | visitor.visit_tags(@tags)
21 | visitor.visit_feature_name(@name)
22 | visitor.visit_background(@background) if @background
23 | @feature_elements.each do |feature_element|
24 | visitor.visit_feature_element(feature_element) if feature_element.descend?(visitor)
25 | end
26 | end
27 |
28 | def descend?(visitor)
29 | @feature_elements.detect{ |feature_element| feature_element.descend?(visitor) }
30 | end
31 |
32 | def has_tags?(tags)
33 | @tags.has_tags?(tags)
34 | end
35 |
36 | def next_feature_element(feature_element, &proc)
37 | index = @feature_elements.index(feature_element)
38 | next_one = @feature_elements[index+1]
39 | proc.call(next_one) if next_one
40 | end
41 |
42 | def backtrace_line(step_name, line)
43 | "#{file_colon_line(line)}:in `#{step_name}'"
44 | end
45 |
46 | def file_colon_line(line)
47 | "#{@file}:#{line}"
48 | end
49 |
50 | def to_sexp
51 | sexp = [:feature, @file, @name]
52 | comment = @comment.to_sexp
53 | sexp += [comment] if comment
54 | tags = @tags.to_sexp
55 | sexp += tags if tags.any?
56 | sexp += [@background.to_sexp] if @background
57 | sexp += @feature_elements.map{|fe| fe.to_sexp}
58 | sexp
59 | end
60 | end
61 | end
62 | end
63 |
--------------------------------------------------------------------------------
/lib/cucumber/core_ext/exception.rb:
--------------------------------------------------------------------------------
1 | # Exception extension that tweaks Exception backtraces:
2 | #
3 | # * The line of the failing .feature line is appended to the backtrace
4 | # * The line that calls #cucumber_instance_exec is replaced with the StepDefinition's regexp
5 | # * Non intersting lines are stripped out
6 | #
7 | # The result is that backtraces look like this:
8 | #
9 | # features/step_definitions/the_step_definition.rb:in `/some step/'
10 | # features/the_feature_file.feature:41:in `Given some step'
11 | #
12 | # or if the exception is raised in the tested code:
13 | #
14 | # lib/myapp/some_file.rb:in `/some_method/'
15 | # lib/myapp/some_other_file.rb:in `/some_other_method/'
16 | # features/step_definitions/the_step_definition.rb:in `/some step/'
17 | # features/the_feature_file.feature:41:in `Given some step'
18 | #
19 | # All backtrace munging can be turned off with the --backtrace switch
20 | #
21 | class Exception
22 | CUCUMBER_FILTER_PATTERNS = [
23 | /vendor\/rails|lib\/cucumber|bin\/cucumber:|lib\/rspec|gems\//
24 | ]
25 |
26 | INSTANCE_EXEC_OFFSET = (Cucumber::RUBY_1_9 || Cucumber::JRUBY) ? -3 : -4
27 |
28 | def self.cucumber_full_backtrace=(v)
29 | @@cucumber_full_backtrace = v
30 | end
31 | self.cucumber_full_backtrace = false
32 |
33 | # Strips the backtrace from +line+ and down
34 | def self.cucumber_strip_backtrace!(backtrace, instance_exec_invocation_line, pseudo_method)
35 | return if @@cucumber_full_backtrace
36 |
37 | instance_exec_pos = backtrace.index(instance_exec_invocation_line)
38 | if instance_exec_pos
39 | replacement_line = instance_exec_pos + INSTANCE_EXEC_OFFSET
40 | backtrace[replacement_line].gsub!(/`.*'/, "`#{pseudo_method}'") if pseudo_method
41 | backtrace[replacement_line+1..-1] = nil
42 |
43 | backtrace.compact!
44 | else
45 | # This happens with rails, because they screw up the backtrace
46 | # before we get here (injecting erb stacktrace and such)
47 | end
48 |
49 | backtrace.reject! do |line|
50 | CUCUMBER_FILTER_PATTERNS.detect{|p| line =~ p}
51 | end
52 | end
53 | end
--------------------------------------------------------------------------------
/lib/cucumber/ast/scenario.rb:
--------------------------------------------------------------------------------
1 | require 'cucumber/ast/feature_element'
2 |
3 | module Cucumber
4 | module Ast
5 | class Scenario
6 | include FeatureElement
7 |
8 | def initialize(background, comment, tags, line, keyword, name, steps)
9 | @background, @comment, @tags, @line, @keyword, @name = background, comment, tags, line, keyword, name
10 | attach_steps(steps)
11 |
12 | step_invocations = steps.map{|step| step.step_invocation}
13 | if @background
14 | @steps = @background.step_collection(step_invocations)
15 | else
16 | @steps = StepCollection.new(step_invocations)
17 | end
18 | end
19 |
20 | def feature=(feature)
21 | @feature = feature
22 | @background.feature = feature if @background
23 | end
24 |
25 | def descend?(visitor)
26 | visitor.matches_lines?(self) &&
27 | visitor.included_by_tags?(self) &&
28 | !visitor.excluded_by_tags?(self) &&
29 | visitor.matches_scenario_names?(self)
30 | end
31 |
32 | def accept(visitor)
33 | visitor.visit_comment(@comment)
34 | visitor.visit_tags(@tags)
35 | visitor.visit_scenario_name(@keyword, @name, file_colon_line(@line), source_indent(text_length))
36 |
37 | skip = @background && @background.failed?
38 | skip_invoke! if skip
39 | visitor.step_mother.before_and_after(self, skip) do
40 | visitor.visit_steps(@steps)
41 | end
42 | end
43 |
44 | def skip_invoke!
45 | @steps.each{|step_invocation| step_invocation.skip_invoke!}
46 | @feature.next_feature_element(self) do |next_one|
47 | next_one.skip_invoke!
48 | end
49 | end
50 |
51 | def to_sexp
52 | sexp = [:scenario, @line, @keyword, @name]
53 | comment = @comment.to_sexp
54 | sexp += [comment] if comment
55 | tags = @tags.to_sexp
56 | sexp += tags if tags.any?
57 | steps = @steps.to_sexp
58 | sexp += steps if steps.any?
59 | sexp
60 | end
61 |
62 | end
63 | end
64 | end
65 |
--------------------------------------------------------------------------------
/lib/cucumber/parser/treetop_ext.rb:
--------------------------------------------------------------------------------
1 | begin
2 | require 'treetop'
3 | require 'treetop/runtime'
4 | require 'treetop/ruby_extensions'
5 | rescue LoadError
6 | require "rubygems"
7 | gem "treetop"
8 | require 'treetop'
9 | require 'treetop/runtime'
10 | require 'treetop/ruby_extensions'
11 | end
12 |
13 | module Cucumber
14 | module Parser
15 | module TreetopExt
16 | FILE_COLON_LINE_PATTERN = /^([\w\W]*?):([\d:]+)$/
17 |
18 | # Parses a file and returns a Cucumber::Ast
19 | def parse_file(file)
20 | _, path, lines = *FILE_COLON_LINE_PATTERN.match(file)
21 | if path
22 | lines = lines.split(':').map { |line| line.to_i }
23 | else
24 | path = file
25 | lines = []
26 | end
27 |
28 | loader = lambda { |io| parse_or_fail(io.read, path) }
29 | feature = if path =~ /^http/
30 | require 'open-uri'
31 | open(path, &loader)
32 | else
33 | File.open(path, Cucumber.file_mode('r'), &loader)
34 | end
35 | feature.lines = lines
36 | feature
37 | end
38 |
39 | def parse_or_fail(s, file=nil, line_offset=0)
40 | parse_tree = parse(s)
41 | if parse_tree.nil?
42 | raise Cucumber::Parser::SyntaxError.new(self, file, line_offset)
43 | else
44 | ast = parse_tree.build
45 | ast.file = file
46 | ast
47 | end
48 | end
49 | end
50 |
51 | class SyntaxError < StandardError
52 | def initialize(parser, file, line_offset)
53 | tf = parser.terminal_failures
54 | expected = tf.size == 1 ? tf[0].expected_string.inspect : "one of #{tf.map{|f| f.expected_string.inspect}.uniq*', '}"
55 | line = parser.failure_line + line_offset
56 | message = "#{file}:#{line}:#{parser.failure_column}: Parse error, expected #{expected}."
57 | super(message)
58 | end
59 | end
60 | end
61 | end
62 |
63 | module Treetop
64 | module Runtime
65 | class SyntaxNode
66 | def line
67 | input.line_of(interval.first)
68 | end
69 | end
70 |
71 | class CompiledParser
72 | include Cucumber::Parser::TreetopExt
73 | end
74 | end
75 | end
--------------------------------------------------------------------------------
/lib/cucumber/core_ext/instance_exec.rb:
--------------------------------------------------------------------------------
1 | require 'cucumber/platform'
2 |
3 | module Cucumber
4 | class ArityMismatchError < StandardError
5 | end
6 | end
7 |
8 | class Object
9 | def cucumber_instance_exec(check_arity, pseudo_method, *args, &block)
10 | cucumber_run_with_backtrace_filtering(pseudo_method) do
11 | if check_arity && !cucumber_compatible_arity?(args, block)
12 | instance_exec do
13 | s1 = cucumber_arity(block) == 1 ? "" : "s"
14 | s2 = args.length == 1 ? "" : "s"
15 | raise Cucumber::ArityMismatchError.new(
16 | "Your block takes #{cucumber_arity(block)} argument#{s1}, but the Regexp matched #{args.length} argument#{s2}."
17 | )
18 | end
19 | else
20 | instance_exec(*args, &block)
21 | end
22 | end
23 | end
24 |
25 | def cucumber_arity(block)
26 | a = block.arity
27 | Cucumber::RUBY_1_9 ? a : (a == -1 ? 0 : a)
28 | end
29 |
30 | def cucumber_compatible_arity?(args, block)
31 | a = cucumber_arity(block)
32 | return true if (a == -1) && Cucumber::RUBY_1_9
33 | a == args.length
34 | end
35 |
36 | def cucumber_run_with_backtrace_filtering(pseudo_method)
37 | begin
38 | yield
39 | rescue Exception => e
40 | instance_exec_invocation_line = "#{__FILE__}:#{__LINE__ - 2}:in `cucumber_run_with_backtrace_filtering'"
41 | Exception.cucumber_strip_backtrace!((e.backtrace || []), instance_exec_invocation_line, pseudo_method)
42 | raise e
43 | end
44 | end
45 |
46 | unless defined? instance_exec # 1.9
47 | # http://eigenclass.org/hiki/bounded+space+instance_exec
48 | module InstanceExecHelper; end
49 | include InstanceExecHelper
50 | def instance_exec(*args, &block)
51 | begin
52 | old_critical, Thread.critical = Thread.critical, true
53 | n = 0
54 | n += 1 while respond_to?(mname="__instance_exec#{n}")
55 | InstanceExecHelper.module_eval{ define_method(mname, &block) }
56 | ensure
57 | Thread.critical = old_critical
58 | end
59 | begin
60 | ret = send(mname, *args)
61 | ensure
62 | InstanceExecHelper.module_eval{ remove_method(mname) } rescue nil
63 | end
64 | ret
65 | end
66 | end
67 | end
68 |
--------------------------------------------------------------------------------
/specs/cucumber/core_ext/proc_spec.rb:
--------------------------------------------------------------------------------
1 | require File.dirname(__FILE__) + '/../../spec_helper'
2 | require 'cucumber/core_ext/instance_exec'
3 |
4 | describe Proc do
5 | it "should remove extraneous path info for file" do
6 | proc = lambda {|a,b|}
7 | proc.file_colon_line.should =~ /^specs\/cucumber\/core_ext\/proc_spec\.rb:6/
8 | end
9 |
10 | it "should raise ArityMismatchError for too many args (expecting 0)" do
11 | lambda {
12 | Object.new.cucumber_instance_exec(true, 'foo', 1) do
13 | end
14 | }.should raise_error(Cucumber::ArityMismatchError, "Your block takes 0 arguments, but the Regexp matched 1 argument.")
15 | end
16 |
17 | it "should raise ArityMismatchError for too many args (expecting 1)" do
18 | lambda {
19 | Object.new.cucumber_instance_exec(true, 'foo', 1,2) do |a|
20 | end
21 | }.should raise_error(Cucumber::ArityMismatchError, "Your block takes 1 argument, but the Regexp matched 2 arguments.")
22 | end
23 |
24 | it "should raise ArityMismatchError for too few args (expecting 1)" do
25 | lambda {
26 | Object.new.cucumber_instance_exec(true, 'foo') do |a|
27 | end
28 | }.should raise_error(Cucumber::ArityMismatchError, "Your block takes 1 argument, but the Regexp matched 0 arguments.")
29 | end
30 |
31 | it "should raise ArityMismatchError for too few args (expecting 2)" do
32 | lambda {
33 | Object.new.cucumber_instance_exec(true, 'foo', 1) do |a,b|
34 | end
35 | }.should raise_error(Cucumber::ArityMismatchError, "Your block takes 2 arguments, but the Regexp matched 1 argument.")
36 | end
37 |
38 | if Cucumber::RUBY_1_9
39 | it "should allow varargs" do
40 | lambda {
41 | Object.new.cucumber_instance_exec(true, 'foo', 1) do |*args|
42 | end
43 | }.should_not raise_error(Cucumber::ArityMismatchError)
44 | end
45 | else
46 | # Ruby 1.8
47 | it "should not allow varargs because Ruby 1.8 reports same arity as with no args, so we can't really tell the difference." do
48 | lambda {
49 | Object.new.cucumber_instance_exec(true, 'foo', 1) do |*args|
50 | end
51 | }.should raise_error(Cucumber::ArityMismatchError, "Your block takes 0 arguments, but the Regexp matched 1 argument.")
52 | end
53 | end
54 | end
55 |
--------------------------------------------------------------------------------
/specs/cucumber/ast/scenario_outline_spec.rb:
--------------------------------------------------------------------------------
1 | require File.dirname(__FILE__) + '/../../spec_helper'
2 | require 'cucumber/step_mother'
3 | require 'cucumber/ast'
4 | require 'cucumber/core_ext/string'
5 |
6 | module Cucumber
7 | module Ast
8 | describe ScenarioOutline do
9 | before do
10 | @step_mother = Object.new
11 | @step_mother.extend(StepMother)
12 |
13 | @step_mother.Given(/^there are (\d+) cucumbers$/) do |n|
14 | @initial = n.to_i
15 | end
16 | @step_mother.When(/^I eat (\d+) cucumbers$/) do |n|
17 | @eaten = n.to_i
18 | end
19 | @step_mother.Then(/^I should have (\d+) cucumbers$/) do |n|
20 | (@initial - @eaten).should == n.to_i
21 | end
22 | @step_mother.Then(/^I should have (\d+) cucumbers in my belly$/) do |n|
23 | @eaten.should == n.to_i
24 | end
25 |
26 | @scenario_outline = ScenarioOutline.new(
27 | background=nil,
28 | Comment.new(""),
29 | Tags.new(18, []),
30 | 19,
31 | "Scenario:", "My outline",
32 | [
33 | Step.new(20, 'Given', 'there are cucumbers'),
34 | Step.new(21, 'When', 'I eat cucumbers'),
35 | Step.new(22, 'Then', 'I should have cucumbers'),
36 | Step.new(23, 'And', 'I should have cucumbers in my belly')
37 | ],
38 | [
39 | [
40 | 24,
41 | 'Examples:',
42 | 'First table',
43 | [
44 | %w{start eat left},
45 | %w{12 5 7},
46 | %w{20 6 14}
47 | ]
48 | ]
49 | ]
50 |
51 | )
52 | end
53 |
54 | it "should replace all variables and call outline once for each table row" do
55 | visitor = Visitor.new(@step_mother)
56 | visitor.should_receive(:visit_table_row).exactly(3).times
57 | visitor.visit_feature_element(@scenario_outline)
58 | end
59 |
60 | it "should pretty print" do
61 | require 'cucumber/formatter/pretty'
62 | visitor = Formatter::Pretty.new(@step_mother, STDOUT, {:comment => true})
63 | visitor.visit_feature_element(@scenario_outline)
64 | end
65 | end
66 | end
67 | end
68 |
--------------------------------------------------------------------------------
/specs/cucumber/ast/step_spec.rb:
--------------------------------------------------------------------------------
1 | require File.dirname(__FILE__) + '/../../spec_helper'
2 | require 'cucumber/step_mother'
3 | require 'cucumber/ast'
4 | require 'cucumber/core_ext/string'
5 |
6 | module Cucumber
7 | module Ast
8 | describe Step do
9 | it "should replace arguments in name" do
10 | step = Step.new(1, 'Given', 'a cucumber')
11 |
12 | invocation_table = Table.new([
13 | %w{color taste},
14 | %w{green juicy}
15 | ])
16 | cells = invocation_table.cells_rows[1]
17 | step_invocation = step.step_invocation_from_cells(cells)
18 |
19 | step_invocation.name.should == 'a green cucumber'
20 | end
21 |
22 | it "should use empty string for the replacement of arguments in name when replace value is nil" do
23 | step = Step.new(1, 'Given', 'a cucumber')
24 |
25 | invocation_table = Table.new([
26 | ['color'],
27 | [nil]
28 | ])
29 | cells = invocation_table.cells_rows[1]
30 | step_invocation = step.step_invocation_from_cells(cells)
31 |
32 | step_invocation.name.should == 'a cucumber'
33 | end
34 |
35 | it "should replace arguments in table arg" do
36 | arg_table = Table.new([%w{taste_ color_}])
37 |
38 | step = Step.new(1, 'Given', 'a cucumber', arg_table)
39 |
40 | invocation_table = Table.new([
41 | %w{color taste},
42 | %w{green juicy}
43 | ])
44 | cells = invocation_table.cells_rows[1]
45 | step_invocation = step.step_invocation_from_cells(cells)
46 |
47 | step_invocation.instance_variable_get('@multiline_arg').raw.should == [%w{taste_juicy color_green}]
48 | end
49 |
50 | it "should replace arguments in py string arg" do
51 | py_string = PyString.new(1, 2, 'taste_ color_', 0)
52 |
53 | step = Step.new(1, 'Given', 'a cucumber', py_string)
54 |
55 | invocation_table = Table.new([
56 | %w{color taste},
57 | %w{green juicy}
58 | ])
59 | cells = invocation_table.cells_rows[1]
60 | step_invocation = step.step_invocation_from_cells(cells)
61 |
62 | step_invocation.instance_variable_get('@multiline_arg').to_s.should == 'taste_juicy color_green'
63 | end
64 | end
65 | end
66 | end
67 |
--------------------------------------------------------------------------------
/lib/cucumber/ast/outline_table.rb:
--------------------------------------------------------------------------------
1 | module Cucumber
2 | module Ast
3 | class OutlineTable < Table
4 | def initialize(raw, scenario_outline)
5 | super(raw)
6 | @scenario_outline = scenario_outline
7 | @cells_class = ExampleCells
8 |
9 | cells_rows.each do |cells|
10 | cells.create_step_invocations!(scenario_outline)
11 | end
12 | end
13 |
14 | def accept(visitor)
15 | cells_rows.each_with_index do |row, n|
16 | if n == 0 || matches?(visitor, row)
17 | visitor.visit_table_row(row)
18 | end
19 | end
20 | nil
21 | end
22 |
23 | def descend?(visitor)
24 | cells_rows.detect{|cells_row| cells_row.descend?(visitor)}
25 | end
26 |
27 | def matches?(visitor, cells)
28 | @scenario_outline.matches_tags_and_name?(visitor) &&
29 | (visitor.matches_lines?(cells) || visitor.matches_lines?(@scenario_outline))
30 | end
31 |
32 | def skip_invoke!
33 | cells_rows.each do |cells|
34 | cells.skip_invoke!
35 | end
36 | end
37 |
38 | class ExampleCells < Cells
39 | def create_step_invocations!(scenario_outline)
40 | @step_invocations = scenario_outline.step_invocations(self)
41 | end
42 |
43 | def descend?(visitor)
44 | @table.matches?(visitor, self)
45 | end
46 |
47 | def skip_invoke!
48 | @step_invocations.each do |step_invocation|
49 | step_invocation.skip_invoke!
50 | end
51 | end
52 |
53 | def accept(visitor)
54 | if header?
55 | @cells.each do |cell|
56 | cell.status = :skipped_param
57 | visitor.visit_table_cell(cell)
58 | end
59 | else
60 | visitor.step_mother.before_and_after(self) do
61 | @step_invocations.each do |step_invocation|
62 | step_invocation.invoke(visitor.step_mother, visitor.options)
63 | @exception ||= step_invocation.exception
64 | end
65 |
66 | @cells.each do |cell|
67 | visitor.visit_table_cell(cell)
68 | end
69 | end
70 | end
71 | end
72 |
73 | private
74 |
75 | def header?
76 | index == 0
77 | end
78 | end
79 | end
80 | end
81 | end
82 |
--------------------------------------------------------------------------------
/lib/cucumber/rails/world.rb:
--------------------------------------------------------------------------------
1 | # Based on code from Brian Takita, Yurii Rashkovskii and Ben Mabey
2 | # Adapted by Aslak Hellesøy
3 |
4 | if defined?(ActiveRecord::Base)
5 | require 'test_help'
6 | else
7 | require 'action_controller/test_process'
8 | require 'action_controller/integration'
9 | end
10 | require 'test/unit/testresult'
11 |
12 | # So that Test::Unit doesn't launch at the end - makes it think it has already been run.
13 | Test::Unit.run = true if Test::Unit.respond_to?(:run=)
14 |
15 | $__cucumber_toplevel = self
16 |
17 | module Cucumber #:nodoc:
18 | module Rails
19 | # All scenarios will execute in the context of a new instance of World.
20 | class World < ActionController::IntegrationTest
21 | if defined?(ActiveRecord::Base)
22 | self.use_transactional_fixtures = false
23 | else
24 | def self.fixture_table_names; []; end # Workaround for projects that don't use ActiveRecord
25 | end
26 |
27 | def initialize #:nodoc:
28 | @_result = Test::Unit::TestResult.new
29 | end
30 | end
31 |
32 | def self.use_transactional_fixtures
33 | World.use_transactional_fixtures = true
34 | if defined?(ActiveRecord::Base)
35 | $__cucumber_toplevel.Before do
36 | @__cucumber_ar_connection = ActiveRecord::Base.connection
37 | if @__cucumber_ar_connection.respond_to?(:increment_open_transactions)
38 | @__cucumber_ar_connection.increment_open_transactions
39 | else
40 | ActiveRecord::Base.__send__(:increment_open_transactions)
41 | end
42 | @__cucumber_ar_connection.begin_db_transaction
43 | ActionMailer::Base.deliveries = [] if defined?(ActionMailer::Base)
44 | end
45 |
46 | $__cucumber_toplevel.After do
47 | @__cucumber_ar_connection.rollback_db_transaction
48 | if @__cucumber_ar_connection.respond_to?(:decrement_open_transactions)
49 | @__cucumber_ar_connection.decrement_open_transactions
50 | else
51 | ActiveRecord::Base.__send__(:decrement_open_transactions)
52 | end
53 | end
54 | end
55 | end
56 |
57 | def self.bypass_rescue
58 | ActionController::Base.class_eval do
59 | def rescue_action(exception)
60 | raise exception
61 | end
62 | end
63 | ActionController::Dispatcher.class_eval do
64 | def self.failsafe_response(output, status, exception = nil)
65 | raise exception
66 | end
67 | end
68 | end
69 | end
70 | end
71 |
72 | World do
73 | Cucumber::Rails::World.new
74 | end
75 |
--------------------------------------------------------------------------------
/config/hoe.rb:
--------------------------------------------------------------------------------
1 | # encoding: utf-8
2 | require 'cucumber/version'
3 |
4 | AUTHOR = 'Aslak Hellesøy' # can also be an array of Authors
5 | EMAIL = "aslak.hellesoy@gmail.com"
6 | DESCRIPTION = "Executable Feature scenarios"
7 | GEM_NAME = 'cucumber' # what ppl will type to install your gem
8 | HOMEPATH = "http://cukes.info"
9 | RUBYFORGE_PROJECT = 'rspec'
10 |
11 | @config_file = "~/.rubyforge/user-config.yml"
12 | @config = nil
13 | RUBYFORGE_USERNAME = "aslak_hellesoy"
14 | def rubyforge_username
15 | unless @config
16 | begin
17 | @config = YAML.load(File.read(File.expand_path(@config_file)))
18 | rescue
19 | puts <<-EOS
20 | ERROR: No rubyforge config file found: #{@config_file}
21 | Run 'rubyforge setup' to prepare your env for access to Rubyforge
22 | - See http://newgem.rubyforge.org/rubyforge.html for more details
23 | EOS
24 | exit
25 | end
26 | end
27 | RUBYFORGE_USERNAME.replace @config["username"]
28 | end
29 |
30 |
31 | REV = nil
32 | # UNCOMMENT IF REQUIRED:
33 | # REV = YAML.load(`svn info`)['Revision']
34 | VERS = Cucumber::VERSION::STRING + (REV ? ".#{REV}" : "")
35 | RDOC_OPTS = ['--quiet', '--title', 'Cucumber documentation',
36 | "--opname", "index.html",
37 | "--line-numbers",
38 | "--main", "README.textile",
39 | "--inline-source"]
40 |
41 | class Hoe
42 | def extra_deps
43 | @extra_deps.reject! { |x| Array(x).first == 'hoe' }
44 | @extra_deps
45 | end
46 | end
47 |
48 | # Generate all the Rake tasks
49 | # Run 'rake -T' to see list of generated tasks (from gem root directory)
50 | $hoe = Hoe.new(GEM_NAME, VERS) do |p|
51 | p.developer(AUTHOR, EMAIL)
52 | p.description = DESCRIPTION
53 | p.summary = DESCRIPTION
54 | p.url = HOMEPATH
55 | p.rubyforge_name = RUBYFORGE_PROJECT if RUBYFORGE_PROJECT
56 | p.clean_globs |= ['**/.*.sw?', '*.gem', '.config', '**/.DS_Store', '**/*.class', '**/*.jar'] #An array of file patterns to delete on clean.
57 |
58 | # == Optional
59 | p.changes = p.paragraphs_of("History.txt", 0..1).join("\n\n")
60 | #p.extra_deps = [] # An array of rubygem dependencies [name, version], e.g. [ ['active_support', '>= 1.3.1'] ]
61 | p.extra_deps = [
62 | ['term-ansicolor', '>= 1.0.3'],
63 | ['treetop', '>= 1.2.5'],
64 | ['polyglot', '>= 0.2.5'], # Remove this when Treetop no longer loads polyglot by default.
65 | ['diff-lcs', '>= 1.1.2'],
66 | ['builder', '>= 2.1.2']
67 | ]
68 |
69 | #p.spec_extras = {} # A hash of extra values to set in the gemspec.
70 |
71 | end
72 |
73 | CHANGES = $hoe.paragraphs_of('History.txt', 0..1).join("\\n\\n")
74 | PATH = (RUBYFORGE_PROJECT == GEM_NAME) ? RUBYFORGE_PROJECT : "#{RUBYFORGE_PROJECT}/#{GEM_NAME}"
75 | $hoe.remote_rdoc_dir = File.join(PATH.gsub(/^#{RUBYFORGE_PROJECT}\/?/,''), 'rdoc')
76 | $hoe.rsync_args = '-av --delete --ignore-errors'
--------------------------------------------------------------------------------
/features/cucumber_cli_outlines.feature:
--------------------------------------------------------------------------------
1 | Feature: Cucumber command line
2 | In order to write better software
3 | Developers should be able to execute requirements as tests
4 |
5 | Scenario: Run scenario outline steps only
6 | When I run cucumber -q features/outline_sample.feature:7
7 | Then it should fail with
8 | """
9 | Feature: Outline Sample
10 |
11 | Scenario Outline: Test state
12 | Given without a table
13 | Given without a table
14 |
15 | Examples: Rainbow colours
16 | | state | other_state |
17 | | missing | passing |
18 | | passing | passing |
19 | | failing | passing |
20 | FAIL (RuntimeError)
21 | ./features/step_definitions/sample_steps.rb:2:in `flunker'
22 | ./features/step_definitions/sample_steps.rb:16:in `/^failing without a table$/'
23 | features/outline_sample.feature:6:in `Given without a table'
24 |
25 | Examples: Only passing
26 | | state | other_state |
27 | | passing | passing |
28 |
29 | 4 scenarios
30 | 1 failed step
31 | 2 skipped steps
32 | 1 undefined step
33 | 4 passed steps
34 |
35 | """
36 |
37 | Scenario: Run single failing scenario outline table row
38 | When I run cucumber features/outline_sample.feature:12
39 | Then it should fail with
40 | """
41 | Feature: Outline Sample
42 |
43 | Scenario Outline: Test state # features/outline_sample.feature:5
44 | Given without a table # features/step_definitions/sample_steps.rb:12
45 | Given without a table # features/step_definitions/sample_steps.rb:12
46 |
47 | Examples: Rainbow colours
48 | | state | other_state |
49 | | failing | passing |
50 | FAIL (RuntimeError)
51 | ./features/step_definitions/sample_steps.rb:2:in `flunker'
52 | ./features/step_definitions/sample_steps.rb:16:in `/^failing without a table$/'
53 | features/outline_sample.feature:6:in `Given without a table'
54 |
55 | 1 scenario
56 | 1 failed step
57 | 1 skipped step
58 |
59 | """
60 |
61 | Scenario: Run all with progress formatter
62 | When I run cucumber -q --format progress features/outline_sample.feature
63 | Then it should fail with
64 | """
65 | --U-..F-..
66 |
67 | (::) failed steps (::)
68 |
69 | FAIL (RuntimeError)
70 | ./features/step_definitions/sample_steps.rb:2:in `flunker'
71 | ./features/step_definitions/sample_steps.rb:16:in `/^failing without a table$/'
72 | features/outline_sample.feature:6:in `Given without a table'
73 |
74 | 5 scenarios
75 | 1 failed step
76 | 2 skipped steps
77 | 1 undefined step
78 | 4 passed steps
79 |
80 | """
81 |
82 |
--------------------------------------------------------------------------------