cucumber-core-1.1.3/0000755000175000017500000000000012515135405013626 5ustar boutilboutilcucumber-core-1.1.3/metadata.yml0000644000175000017500000001575312515135405016144 0ustar boutilboutil--- !ruby/object:Gem::Specification name: cucumber-core version: !ruby/object:Gem::Version version: 1.1.3 platform: ruby authors: - Aslak Hellesøy - Matt Wynne - Steve Tooke - Oleg Sukhodolsky - Tom Brand autorequire: bindir: bin cert_chain: [] date: 2015-03-25 00:00:00.000000000 Z dependencies: - !ruby/object:Gem::Dependency name: gherkin requirement: !ruby/object:Gem::Requirement requirements: - - "~>" - !ruby/object:Gem::Version version: 2.12.0 type: :runtime prerelease: false version_requirements: !ruby/object:Gem::Requirement requirements: - - "~>" - !ruby/object:Gem::Version version: 2.12.0 - !ruby/object:Gem::Dependency name: bundler requirement: !ruby/object:Gem::Requirement requirements: - - ">=" - !ruby/object:Gem::Version version: 1.3.5 type: :development prerelease: false version_requirements: !ruby/object:Gem::Requirement requirements: - - ">=" - !ruby/object:Gem::Version version: 1.3.5 - !ruby/object:Gem::Dependency name: rake requirement: !ruby/object:Gem::Requirement requirements: - - ">=" - !ruby/object:Gem::Version version: 0.9.2 type: :development prerelease: false version_requirements: !ruby/object:Gem::Requirement requirements: - - ">=" - !ruby/object:Gem::Version version: 0.9.2 - !ruby/object:Gem::Dependency name: rspec requirement: !ruby/object:Gem::Requirement requirements: - - "~>" - !ruby/object:Gem::Version version: '3' type: :development prerelease: false version_requirements: !ruby/object:Gem::Requirement requirements: - - "~>" - !ruby/object:Gem::Version version: '3' - !ruby/object:Gem::Dependency name: unindent requirement: !ruby/object:Gem::Requirement requirements: - - ">=" - !ruby/object:Gem::Version version: '1.0' type: :development prerelease: false version_requirements: !ruby/object:Gem::Requirement requirements: - - ">=" - !ruby/object:Gem::Version version: '1.0' - !ruby/object:Gem::Dependency name: kramdown requirement: !ruby/object:Gem::Requirement requirements: - - "~>" - !ruby/object:Gem::Version version: 1.4.2 type: :development prerelease: false version_requirements: !ruby/object:Gem::Requirement requirements: - - "~>" - !ruby/object:Gem::Version version: 1.4.2 - !ruby/object:Gem::Dependency name: coveralls requirement: !ruby/object:Gem::Requirement requirements: - - "~>" - !ruby/object:Gem::Version version: '0.7' type: :development prerelease: false version_requirements: !ruby/object:Gem::Requirement requirements: - - "~>" - !ruby/object:Gem::Version version: '0.7' description: Core library for the Cucumber BDD app email: cukes@googlegroups.com executables: [] extensions: [] extra_rdoc_files: [] files: - ".coveralls.yml" - ".rspec" - ".ruby-gemset" - ".travis.yml" - ".yardopts" - CONTRIBUTING.md - Gemfile - HISTORY.md - LICENSE - README.md - Rakefile - cucumber-core.gemspec - lib/cucumber/core.rb - lib/cucumber/core/ast.rb - lib/cucumber/core/ast/background.rb - lib/cucumber/core/ast/comment.rb - lib/cucumber/core/ast/data_table.rb - lib/cucumber/core/ast/describes_itself.rb - lib/cucumber/core/ast/doc_string.rb - lib/cucumber/core/ast/empty_background.rb - lib/cucumber/core/ast/empty_multiline_argument.rb - lib/cucumber/core/ast/examples_table.rb - lib/cucumber/core/ast/feature.rb - lib/cucumber/core/ast/location.rb - lib/cucumber/core/ast/names.rb - lib/cucumber/core/ast/outline_step.rb - lib/cucumber/core/ast/scenario.rb - lib/cucumber/core/ast/scenario_outline.rb - lib/cucumber/core/ast/step.rb - lib/cucumber/core/ast/tag.rb - lib/cucumber/core/compiler.rb - lib/cucumber/core/filter.rb - lib/cucumber/core/gherkin/ast_builder.rb - lib/cucumber/core/gherkin/document.rb - lib/cucumber/core/gherkin/parser.rb - lib/cucumber/core/gherkin/writer.rb - lib/cucumber/core/gherkin/writer/helpers.rb - lib/cucumber/core/platform.rb - lib/cucumber/core/report/summary.rb - lib/cucumber/core/test/action.rb - lib/cucumber/core/test/around_hook.rb - lib/cucumber/core/test/case.rb - lib/cucumber/core/test/filters.rb - lib/cucumber/core/test/filters/locations_filter.rb - lib/cucumber/core/test/filters/name_filter.rb - lib/cucumber/core/test/filters/tag_filter.rb - lib/cucumber/core/test/result.rb - lib/cucumber/core/test/runner.rb - lib/cucumber/core/test/step.rb - lib/cucumber/core/test/timer.rb - lib/cucumber/core/version.rb - spec/capture_warnings.rb - spec/coverage.rb - spec/cucumber/core/ast/background_spec.rb - spec/cucumber/core/ast/data_table_spec.rb - spec/cucumber/core/ast/doc_string_spec.rb - spec/cucumber/core/ast/empty_multiline_argument_spec.rb - spec/cucumber/core/ast/examples_table_spec.rb - spec/cucumber/core/ast/location_spec.rb - spec/cucumber/core/ast/outline_step_spec.rb - spec/cucumber/core/ast/step_spec.rb - spec/cucumber/core/compiler_spec.rb - spec/cucumber/core/filter_spec.rb - spec/cucumber/core/gherkin/parser_spec.rb - spec/cucumber/core/gherkin/writer_spec.rb - spec/cucumber/core/test/action_spec.rb - spec/cucumber/core/test/case_spec.rb - spec/cucumber/core/test/duration_matcher.rb - spec/cucumber/core/test/filters/locations_filter_spec.rb - spec/cucumber/core/test/result_spec.rb - spec/cucumber/core/test/runner_spec.rb - spec/cucumber/core/test/step_spec.rb - spec/cucumber/core/test/timer_spec.rb - spec/cucumber/core_spec.rb - spec/readme_spec.rb - spec/report_api_spy.rb homepage: http://cukes.info licenses: - MIT metadata: {} post_install_message: rdoc_options: - "--charset=UTF-8" require_paths: - lib required_ruby_version: !ruby/object:Gem::Requirement requirements: - - ">=" - !ruby/object:Gem::Version version: 1.9.3 required_rubygems_version: !ruby/object:Gem::Requirement requirements: - - ">=" - !ruby/object:Gem::Version version: '0' requirements: [] rubyforge_project: rubygems_version: 2.2.2 signing_key: specification_version: 4 summary: cucumber-core-1.1.3 test_files: - spec/capture_warnings.rb - spec/coverage.rb - spec/cucumber/core/ast/background_spec.rb - spec/cucumber/core/ast/data_table_spec.rb - spec/cucumber/core/ast/doc_string_spec.rb - spec/cucumber/core/ast/empty_multiline_argument_spec.rb - spec/cucumber/core/ast/examples_table_spec.rb - spec/cucumber/core/ast/location_spec.rb - spec/cucumber/core/ast/outline_step_spec.rb - spec/cucumber/core/ast/step_spec.rb - spec/cucumber/core/compiler_spec.rb - spec/cucumber/core/filter_spec.rb - spec/cucumber/core/gherkin/parser_spec.rb - spec/cucumber/core/gherkin/writer_spec.rb - spec/cucumber/core/test/action_spec.rb - spec/cucumber/core/test/case_spec.rb - spec/cucumber/core/test/duration_matcher.rb - spec/cucumber/core/test/filters/locations_filter_spec.rb - spec/cucumber/core/test/result_spec.rb - spec/cucumber/core/test/runner_spec.rb - spec/cucumber/core/test/step_spec.rb - spec/cucumber/core/test/timer_spec.rb - spec/cucumber/core_spec.rb - spec/readme_spec.rb - spec/report_api_spy.rb cucumber-core-1.1.3/spec/0000755000175000017500000000000012515135405014560 5ustar boutilboutilcucumber-core-1.1.3/spec/report_api_spy.rb0000644000175000017500000000060412515135405020144 0ustar boutilboutilclass ReportAPISpy def initialize @result = [] end def test_case(*args) @result << [:test_case, *args] yield self if block_given? end def test_step(*args) @result << [:test_step, *args] yield self if block_given? end def done(*args) @result << [:done, *args] yield self if block_given? end def messages @result.map(&:first) end end cucumber-core-1.1.3/spec/readme_spec.rb0000644000175000017500000000144412515135405017357 0ustar boutilboutilrequire 'stringio' require 'kramdown' describe "README.md code snippet" do let(:code_blocks) do markdown = File.read(File.expand_path(File.dirname(__FILE__) + '/../README.md')) parse_ruby_from(markdown) end it "executes with the expected output" do code, output = *code_blocks expect(execute_ruby(code)).to eq output end def execute_ruby(code) capture_stdout do eval code, binding end end def parse_ruby_from(markdown) code_blocks = Kramdown::Parser::GFM.parse(markdown).first.children.select { |e| e.type == :codeblock }.map(&:value) expect(code_blocks).not_to be_empty code_blocks end def capture_stdout original = $stdout $stdout = StringIO.new yield result = $stdout.string $stdout = original result end end cucumber-core-1.1.3/spec/cucumber/0000755000175000017500000000000012515135405016365 5ustar boutilboutilcucumber-core-1.1.3/spec/cucumber/core_spec.rb0000644000175000017500000002263312515135405020662 0ustar boutilboutilrequire 'report_api_spy' require 'cucumber/core' require 'cucumber/core/filter' require 'cucumber/core/gherkin/writer' require 'cucumber/core/platform' require 'cucumber/core/report/summary' require 'cucumber/core/test/around_hook' module Cucumber describe Core do include Core include Core::Gherkin::Writer describe "compiling features to a test suite" do it "compiles two scenarios into two test cases" do visitor = ReportAPISpy.new compile([ gherkin do feature do background do step end scenario do step end scenario do step step end end end ], visitor) expect( visitor.messages ).to eq [ :test_case, :test_step, :test_step, :test_case, :test_step, :test_step, :test_step, :done, ] end it "filters out test cases based on a tag expression" do visitor = double.as_null_object expect( visitor ).to receive(:test_case) do |test_case| expect( test_case.name ).to eq 'foo, bar (#1)' end.exactly(1).times gherkin = gherkin do feature do scenario tags: '@b' do step end scenario_outline 'foo' do step '' examples tags: '@a'do row 'arg' row 'x' end examples 'bar', tags: '@a @b' do row 'arg' row 'y' end end end end compile [gherkin], visitor, [Cucumber::Core::Test::TagFilter.new(['@a', '@b'])] end describe 'with tag filters that have limits' do let(:visitor) { double.as_null_object } let(:gherkin_doc) do gherkin do feature tags: '@feature' do scenario tags: '@one @three' do step end scenario tags: '@one' do step end scenario_outline do step '' examples tags: '@three'do row 'arg' row 'x' end end scenario tags: '@ignore' do step end end end end require 'unindent' def expect_tag_excess(error_message) expect { compile [gherkin_doc], visitor, tag_filters }.to raise_error( Cucumber::Core::Test::TagFilter::TagExcess, error_message.unindent.chomp ) end context 'on scenarios' do let(:tag_filters) { [ Cucumber::Core::Test::TagFilter.new(['@one:1']) ] } it 'raises a tag excess error with the location of the test cases' do expect_tag_excess <<-STR @one occurred 2 times, but the limit was set to 1 features/test.feature:5 features/test.feature:9 STR end end context 'on scenario outlines' do let(:tag_filters) { [ Cucumber::Core::Test::TagFilter.new(['@three:1']) ] } it 'raises a tag excess error with the location of the test cases' do expect_tag_excess <<-STR @three occurred 2 times, but the limit was set to 1 features/test.feature:5 features/test.feature:18 STR end end context 'on a feature with scenarios' do let(:tag_filters) { [ Cucumber::Core::Test::TagFilter.new(['@feature:2']) ] } it 'raises a tag excess error with the location of the test cases' do expect_tag_excess <<-STR @feature occurred 4 times, but the limit was set to 2 features/test.feature:5 features/test.feature:9 features/test.feature:18 features/test.feature:21 STR end end context 'with negated tags' do let(:tag_filters) { [ Cucumber::Core::Test::TagFilter.new(['~@one:1']) ] } it 'raises a tag excess error with the location of the test cases' do expect_tag_excess <<-STR @one occurred 2 times, but the limit was set to 1 features/test.feature:5 features/test.feature:9 STR end end context 'whith multiple tag limits' do let(:tag_filters) { [ Cucumber::Core::Test::TagFilter.new(['@one:1, @three:1', '~@feature:3']) ] } it 'raises a tag excess error with the location of the test cases' do expect_tag_excess <<-STR @one occurred 2 times, but the limit was set to 1 features/test.feature:5 features/test.feature:9 @three occurred 2 times, but the limit was set to 1 features/test.feature:5 features/test.feature:18 @feature occurred 4 times, but the limit was set to 3 features/test.feature:5 features/test.feature:9 features/test.feature:18 features/test.feature:21 STR end end end end describe "executing a test suite" do context "without hooks" do class WithSteps < Core::Filter.new def test_case(test_case) test_steps = test_case.test_steps.map do |step| case step.name when /fail/ step.with_action { raise Failure } when /pass/ step.with_action {} else step end end test_case.with_steps(test_steps).describe_to(receiver) end end it "executes the test cases in the suite" do gherkin = gherkin do feature 'Feature name' do scenario 'The one that passes' do step 'passing' end scenario 'The one that fails' do step 'passing' step 'failing' step 'passing' step 'undefined' end end end report = Core::Report::Summary.new execute [gherkin], report, [WithSteps.new] expect( report.test_cases.total ).to eq 2 expect( report.test_cases.total_passed ).to eq 1 expect( report.test_cases.total_failed ).to eq 1 expect( report.test_steps.total ).to eq 5 expect( report.test_steps.total_failed ).to eq 1 expect( report.test_steps.total_passed ).to eq 2 expect( report.test_steps.total_skipped ).to eq 1 expect( report.test_steps.total_undefined ).to eq 1 end end context "with around hooks" do class WithAroundHooks < Core::Filter.new(:logger) def test_case(test_case) base_step = Core::Test::Step.new(test_case.source) test_steps = [ base_step.with_action { logger << :step }, ] around_hook = Core::Test::AroundHook.new do |run_scenario| logger << :before_all run_scenario.call logger << :middle run_scenario.call logger << :after_all end test_case.with_steps(test_steps).with_around_hooks([around_hook]).describe_to(receiver) end end it "executes the test cases in the suite" do gherkin = gherkin do feature do scenario do step end end end report = Core::Report::Summary.new logger = [] execute [gherkin], report, [WithAroundHooks.new(logger)] expect( report.test_cases.total ).to eq 1 expect( report.test_cases.total_passed ).to eq 1 expect( report.test_cases.total_failed ).to eq 0 expect( logger ).to eq [ :before_all, :step, :middle, :step, :after_all ] end end require 'cucumber/core/test/filters' it "filters test cases by tag" do gherkin = gherkin do feature do scenario do step end scenario tags: '@a @b' do step end scenario tags: '@a' do step end end end report = Core::Report::Summary.new execute [gherkin], report, [ Cucumber::Core::Test::TagFilter.new(['@a']) ] expect( report.test_cases.total ).to eq 2 end it "filters test cases by name" do gherkin = gherkin do feature 'first feature' do scenario 'first scenario' do step 'missing' end scenario 'second' do step 'missing' end end end report = Core::Report::Summary.new execute [gherkin], report, [ Cucumber::Core::Test::NameFilter.new([/scenario/]) ] expect( report.test_cases.total ).to eq 1 end end end end cucumber-core-1.1.3/spec/cucumber/core/0000755000175000017500000000000012515135405017315 5ustar boutilboutilcucumber-core-1.1.3/spec/cucumber/core/test/0000755000175000017500000000000012515135405020274 5ustar boutilboutilcucumber-core-1.1.3/spec/cucumber/core/test/timer_spec.rb0000644000175000017500000000122512515135405022753 0ustar boutilboutilrequire 'cucumber/core/test/timer' require 'cucumber/core/test/duration_matcher' module Cucumber module Core module Test describe Timer do before do time = double allow( Time ).to receive(:now) { time } allow( time ).to receive(:nsec).and_return(946752000, 946752001) allow( time ).to receive(:to_i).and_return(1377009235, 1377009235) end it "returns a Result::Duration object" do timer = Timer.new.start expect( timer.duration ).to be_duration 1 end it "would be slow to test" do # so we won't end end end end end cucumber-core-1.1.3/spec/cucumber/core/test/step_spec.rb0000644000175000017500000000466212515135405022616 0ustar boutilboutilrequire 'cucumber/core/test/step' module Cucumber::Core::Test describe Step do describe "describing itself" do it "describes itself to a visitor" do visitor = double args = double test_step = Step.new([double]) expect( visitor ).to receive(:test_step).with(test_step, args) test_step.describe_to(visitor, args) end it "describes its source to a visitor" do feature, scenario, step_or_hook = double, double, double visitor = double args = double expect( feature ).to receive(:describe_to).with(visitor, args) expect( scenario ).to receive(:describe_to).with(visitor, args) expect( step_or_hook ).to receive(:describe_to).with(visitor, args) test_step = Step.new([feature, scenario, step_or_hook]) test_step.describe_source_to(visitor, args) end end describe "executing" do let(:ast_step) { double } it "passes arbitrary arguments to the action's block" do args_spy = nil expected_args = [double, double] test_step = Step.new([ast_step]).with_action do |*actual_args| args_spy = actual_args end test_step.execute(*expected_args) expect(args_spy).to eq expected_args end context "when a passing action exists" do it "returns a passing result" do test_step = Step.new([ast_step]).with_action {} expect( test_step.execute ).to be_passed end end context "when a failing action exists" do let(:exception) { StandardError.new('oops') } it "returns a failing result" do test_step = Step.new([ast_step]).with_action { raise exception } result = test_step.execute expect( result ).to be_failed expect( result.exception ).to eq exception end end context "with no action" do it "returns an Undefined result" do test_step = Step.new([ast_step]) result = test_step.execute expect( result ).to be_undefined end end end it "exposes the name and location of the AST step or hook as attributes" do name, location = double, double step_or_hook = double(name: name, location: location) test_step = Step.new([step_or_hook]) expect( test_step.name ).to eq name expect( test_step.location ).to eq location end end end cucumber-core-1.1.3/spec/cucumber/core/test/runner_spec.rb0000644000175000017500000002244012515135405023146 0ustar boutilboutilrequire 'cucumber/core/test/runner' require 'cucumber/core/test/case' require 'cucumber/core/test/step' require 'cucumber/core/test/duration_matcher' module Cucumber::Core::Test describe Runner do let(:test_case) { Case.new(test_steps, source) } let(:source) { [double('ast node', location: double)] } let(:runner) { Runner.new(report) } let(:report) { double.as_null_object } let(:passing) { Step.new([double]).with_action {} } let(:failing) { Step.new([double]).with_action { raise exception } } let(:pending) { Step.new([double]).with_action { raise Result::Pending.new("TODO") } } let(:skipping) { Step.new([double]).with_action { raise Result::Skipped.new } } let(:undefined) { Step.new([double]) } let(:exception) { StandardError.new('test error') } before do allow(report).to receive(:before_test_case) end context "reporting the duration of a test case" do before do time = double allow(Time).to receive(:now).and_return(time) allow(time).to receive(:nsec).and_return(946752000, 946752001) allow(time).to receive(:to_i).and_return(1377009235, 1377009235) end context "for a passing test case" do let(:test_steps) { [passing] } it "records the nanoseconds duration of the execution on the result" do expect( report ).to receive(:after_test_case) do |reported_test_case, result| expect( result.duration ).to be_duration 1 end test_case.describe_to runner end end context "for a failing test case" do let(:test_steps) { [failing] } it "records the duration" do expect( report ).to receive(:after_test_case) do |reported_test_case, result| expect( result.duration ).to be_duration 1 end test_case.describe_to runner end end end context "reporting the exception that failed a test case" do let(:test_steps) { [failing] } it "sets the exception on the result" do allow(report).to receive(:before_test_case) expect( report ).to receive(:after_test_case) do |reported_test_case, result| expect( result.exception ).to eq exception end test_case.describe_to runner end end context "with a single case" do context "without steps" do let(:test_steps) { [] } it "calls the report before running the case" do expect( report ).to receive(:before_test_case).with(test_case) test_case.describe_to runner end it "calls the report after running the case" do expect( report ).to receive(:after_test_case) do |reported_test_case, result| expect( reported_test_case ).to eq test_case expect( result ).to be_unknown end test_case.describe_to runner end end context 'with steps' do context 'that all pass' do let(:test_steps) { [ passing, passing ] } it 'reports a passing test case' do expect( report ).to receive(:after_test_case) do |test_case, result| expect( result ).to be_passed end test_case.describe_to runner end end context 'an undefined step' do let(:test_steps) { [ undefined ] } it 'reports an undefined test case' do expect( report ).to receive(:after_test_case) do |test_case, result| expect( result ).to be_undefined end test_case.describe_to runner end end context 'a pending step' do let(:test_steps) { [ pending ] } it 'reports a pending test case' do expect( report ).to receive(:after_test_case) do |test_case, result| expect( result ).to be_pending end test_case.describe_to runner end end context "a skipping step" do let(:test_steps) { [skipping] } it "reports a skipped test case" do expect( report ).to receive(:after_test_case) do |test_case, result| expect( result ).to be_skipped end test_case.describe_to runner end end context 'that fail' do let(:test_steps) { [ failing ] } it 'reports a failing test case' do expect( report ).to receive(:after_test_case) do |test_case, result| expect( result ).to be_failed end test_case.describe_to runner end end context 'where the first step fails' do let(:test_steps) { [ failing, passing ] } it 'executes the after hook at the end regardless of the failure' do expect( report ).to receive(:after_test_case) do |test_case, result| expect( result ).to be_failed expect( result.exception ).to eq exception end test_case.describe_to runner end it 'reports the first step as failed' do expect( report ).to receive(:after_test_step).with(failing, anything) do |test_step, result| expect( result ).to be_failed end test_case.describe_to runner end it 'reports the second step as skipped' do expect( report ).to receive(:after_test_step).with(passing, anything) do |test_step, result| expect( result ).to be_skipped end test_case.describe_to runner end it 'reports the test case as failed' do expect( report ).to receive(:after_test_case) do |test_case, result| expect( result ).to be_failed expect( result.exception ).to eq exception end test_case.describe_to runner end it 'skips, rather than executing the second step' do expect( passing ).not_to receive(:execute) expect( passing ).to receive(:skip) test_case.describe_to runner end end end end context 'with multiple test cases' do context 'when the first test case fails' do let(:first_test_case) { Case.new([failing], source) } let(:last_test_case) { Case.new([passing], source) } let(:test_cases) { [first_test_case, last_test_case] } it 'reports the results correctly for the following test case' do expect( report ).to receive(:after_test_case).with(last_test_case, anything) do |reported_test_case, result| expect( result ).to be_passed end test_cases.each { |c| c.describe_to runner } end end end context "passing latest result to a mapping" do it "passes a Failed result when the scenario is failing" do result_spy = nil hook_mapping = UnskippableAction.new do |last_result| result_spy = last_result end after_hook = Step.new([double], hook_mapping) failing_step = Step.new([double]).with_action { fail } test_case = Case.new([failing_step, after_hook], source) test_case.describe_to runner expect(result_spy).to be_failed end end require 'cucumber/core/test/around_hook' context "with around hooks" do it "passes normally when around hooks don't fail" do around_hook = AroundHook.new { |block| block.call } passing_step = Step.new([double]).with_action {} test_case = Case.new([passing_step], source, [around_hook]) expect(report).to receive(:after_test_case).with(test_case, anything) do |reported_test_case, result| expect(result).to be_passed end test_case.describe_to runner end it "gets a failed result if the Around hook fails before the test case is run" do around_hook = AroundHook.new { |block| raise exception } passing_step = Step.new([double]).with_action {} test_case = Case.new([passing_step], source, [around_hook]) expect(report).to receive(:after_test_case).with(test_case, anything) do |reported_test_case, result| expect(result).to be_failed expect(result.exception).to eq exception end test_case.describe_to runner end it "gets a failed result if the Around hook fails after the test case is run" do around_hook = AroundHook.new { |block| block.call; raise exception } passing_step = Step.new([double]).with_action {} test_case = Case.new([passing_step], source, [around_hook]) expect(report).to receive(:after_test_case).with(test_case, anything) do |reported_test_case, result| expect(result).to be_failed expect(result.exception).to eq exception end test_case.describe_to runner end it "fails when a step fails if the around hook works" do around_hook = AroundHook.new { |block| block.call } failing_step = Step.new([double]).with_action { raise exception } test_case = Case.new([failing_step], source, [around_hook]) expect(report).to receive(:after_test_case).with(test_case, anything) do |reported_test_case, result| expect(result).to be_failed expect(result.exception).to eq exception end test_case.describe_to runner end end end end cucumber-core-1.1.3/spec/cucumber/core/test/result_spec.rb0000644000175000017500000001601112515135405023150 0ustar boutilboutil# -*- encoding: utf-8 -*- require 'cucumber/core/test/result' require 'cucumber/core/test/duration_matcher' module Cucumber::Core::Test describe Result do let(:visitor) { double('visitor') } let(:args) { double('args') } describe Result::Passed do subject(:result) { Result::Passed.new(duration) } let(:duration) { Result::Duration.new(1 * 1000 * 1000) } it "describes itself to a visitor" do expect( visitor ).to receive(:passed).with(args) expect( visitor ).to receive(:duration).with(duration, args) result.describe_to(visitor, args) end it "converts to a string" do expect( result.to_s ).to eq "✓" end it "has a duration" do expect( result.duration ).to eq duration end it "requires the constructor argument" do expect { Result::Passed.new }.to raise_error(ArgumentError) end it { expect( result ).to be_passed } it { expect( result ).not_to be_failed } it { expect( result ).not_to be_undefined } it { expect( result ).not_to be_unknown } it { expect( result ).not_to be_skipped } end describe Result::Failed do subject(:result) { Result::Failed.new(duration, exception) } let(:duration) { Result::Duration.new(1 * 1000 * 1000) } let(:exception) { StandardError.new("error message") } it "describes itself to a visitor" do expect( visitor ).to receive(:failed).with(args) expect( visitor ).to receive(:duration).with(duration, args) expect( visitor ).to receive(:exception).with(exception, args) result.describe_to(visitor, args) end it "has a duration" do expect( result.duration ).to eq duration end it "requires both constructor arguments" do expect { Result::Failed.new }.to raise_error(ArgumentError) expect { Result::Failed.new(duration) }.to raise_error(ArgumentError) end it { expect( result ).not_to be_passed } it { expect( result ).to be_failed } it { expect( result ).not_to be_undefined } it { expect( result ).not_to be_unknown } it { expect( result ).not_to be_skipped } end describe Result::Unknown do subject(:result) { Result::Unknown.new } it "doesn't describe itself to a visitor" do visitor = double('never receives anything') result.describe_to(visitor, args) end it { expect( result ).not_to be_passed } it { expect( result ).not_to be_failed } it { expect( result ).not_to be_undefined } it { expect( result ).to be_unknown } it { expect( result ).not_to be_skipped } end describe Result::Undefined do subject(:result) { Result::Undefined.new } it "describes itself to a visitor" do expect( visitor ).to receive(:undefined).with(args) expect( visitor ).to receive(:duration).with(an_unknown_duration, args) result.describe_to(visitor, args) end it { expect( result ).not_to be_passed } it { expect( result ).not_to be_failed } it { expect( result ).to be_undefined } it { expect( result ).not_to be_unknown } it { expect( result ).not_to be_skipped } end describe Result::Skipped do subject(:result) { Result::Skipped.new } it "describes itself to a visitor" do expect( visitor ).to receive(:skipped).with(args) expect( visitor ).to receive(:duration).with(an_unknown_duration, args) result.describe_to(visitor, args) end it { expect( result ).not_to be_passed } it { expect( result ).not_to be_failed } it { expect( result ).not_to be_undefined } it { expect( result ).not_to be_unknown } it { expect( result ).to be_skipped } end describe Result::Summary do let(:summary) { Result::Summary.new } let(:failed) { Result::Failed.new(Result::Duration.new(10), exception) } let(:passed) { Result::Passed.new(Result::Duration.new(11)) } let(:skipped) { Result::Skipped.new } let(:unknown) { Result::Unknown.new } let(:undefined) { Result::Undefined.new } let(:exception) { StandardError.new } it "counts failed results" do failed.describe_to summary expect( summary.total_failed ).to eq 1 expect( summary.total ).to eq 1 end it "counts passed results" do passed.describe_to summary expect( summary.total_passed ).to eq 1 expect( summary.total ).to eq 1 end it "counts skipped results" do skipped.describe_to summary expect( summary.total_skipped ).to eq 1 expect( summary.total ).to eq 1 end it "counts undefined results" do undefined.describe_to summary expect( summary.total_undefined ).to eq 1 expect( summary.total ).to eq 1 end it "counts abitrary raisable results" do flickering = Class.new(Result::Raisable) do def describe_to(visitor, *args) visitor.flickering(*args) end end flickering.new.describe_to summary expect( summary.total_flickering ).to eq 1 expect( summary.total ).to eq 1 end it "returns zero for a status where no messges have been received" do expect( summary.total_passed ).to eq 0 expect( summary.total_ponies ).to eq 0 end it "doesn't count unknown results" do unknown.describe_to summary expect( summary.total ).to eq 0 end it "counts combinations" do [passed, passed, failed, skipped, undefined].each { |r| r.describe_to summary } expect( summary.total ).to eq 5 expect( summary.total_passed ).to eq 2 expect( summary.total_failed ).to eq 1 expect( summary.total_skipped ).to eq 1 expect( summary.total_undefined ).to eq 1 end it "records durations" do [passed, failed].each { |r| r.describe_to summary } expect( summary.durations[0] ).to be_duration 11 expect( summary.durations[1] ).to be_duration 10 end it "records exceptions" do [passed, failed].each { |r| r.describe_to summary } expect( summary.exceptions ).to eq [exception] end end describe Result::Duration do subject(:duration) { Result::Duration.new(10) } it "#nanoseconds can be accessed in #tap" do expect( duration.tap { |duration| @duration = duration.nanoseconds } ).to eq duration expect( @duration ).to eq 10 end end describe Result::UnknownDuration do subject(:duration) { Result::UnknownDuration.new } it "#tap does not execute the passed block" do expect( duration.tap { raise "tap executed block" } ).to eq duration end it "accessing #nanoseconds outside #tap block raises exception" do expect { duration.nanoseconds }.to raise_error(RuntimeError) end end end end cucumber-core-1.1.3/spec/cucumber/core/test/filters/0000755000175000017500000000000012515135405021744 5ustar boutilboutilcucumber-core-1.1.3/spec/cucumber/core/test/filters/locations_filter_spec.rb0000644000175000017500000000352112515135405026644 0ustar boutilboutilrequire 'cucumber/core/gherkin/writer' require 'cucumber/core' require 'cucumber/core/test/filters/locations_filter' module Cucumber::Core::Test describe LocationsFilter do include Cucumber::Core::Gherkin::Writer include Cucumber::Core let(:receiver) { SpyReceiver.new } let(:doc) do gherkin do feature do scenario 'x' do step 'a step' end scenario 'y' do step 'a step' end end end end it "sorts by the given locations" do locations = [ Cucumber::Core::Ast::Location.new('features/test.feature', 6), Cucumber::Core::Ast::Location.new('features/test.feature', 3) ] filter = LocationsFilter.new(locations) compile [doc], receiver, [filter] expect(receiver.test_case_locations).to eq ["features/test.feature:6", "features/test.feature:3"] end it "works with wildcard locations" do locations = [ Cucumber::Core::Ast::Location.new('features/test.feature') ] filter = LocationsFilter.new(locations) compile [doc], receiver, [filter] expect(receiver.test_case_locations).to eq ["features/test.feature:3", "features/test.feature:6"] end it "filters out scenarios that don't match" do locations = [ Cucumber::Core::Ast::Location.new('features/test.feature', 3) ] filter = LocationsFilter.new(locations) compile [doc], receiver, [filter] expect(receiver.test_case_locations).to eq ["features/test.feature:3"] end class SpyReceiver def test_case(test_case) test_cases << test_case end def done end def test_case_locations test_cases.map(&:location).map(&:to_s) end private def test_cases @test_cases ||= [] end end end end cucumber-core-1.1.3/spec/cucumber/core/test/duration_matcher.rb0000644000175000017500000000101412515135405024145 0ustar boutilboutil# -*- encoding: utf-8 -*- require 'cucumber/core/test/result' require 'rspec/expectations' module Cucumber::Core::Test RSpec::Matchers.define :be_duration do |expected| match do |actual| actual.tap { |duration| @nanoseconds = duration.nanoseconds } @nanoseconds == expected end end RSpec::Matchers.define :an_unknown_duration do match do |actual| actual.tap { raise "#tap block was executed, not an UnknownDuration" } expect(actual).to respond_to(:nanoseconds) end end end cucumber-core-1.1.3/spec/cucumber/core/test/case_spec.rb0000644000175000017500000003505412515135405022555 0ustar boutilboutil# -*- coding: utf-8 -*- require 'cucumber/core' require 'cucumber/core/gherkin/writer' require 'cucumber/core/platform' require 'cucumber/core/test/case' require 'unindent' module Cucumber module Core module Test describe Case do include Core include Core::Gherkin::Writer let(:test_case) { Test::Case.new(test_steps, [feature, scenario]) } let(:feature) { double } let(:scenario) { double } let(:test_steps) { [double, double] } context 'describing itself' do it "describes itself to a visitor" do visitor = double args = double expect( visitor ).to receive(:test_case).with(test_case, args) test_case.describe_to(visitor, args) end it "asks each test_step to describe themselves to the visitor" do visitor = double args = double test_steps.each do |test_step| expect( test_step ).to receive(:describe_to).with(visitor, args) end allow( visitor ).to receive(:test_case).and_yield(visitor) test_case.describe_to(visitor, args) end it "describes around hooks in order" do visitor = double allow( visitor ).to receive(:test_case).and_yield(visitor) first_hook, second_hook = double, double expect( first_hook ).to receive(:describe_to).ordered.and_yield expect( second_hook ).to receive(:describe_to).ordered.and_yield around_hooks = [first_hook, second_hook] Test::Case.new([], [], around_hooks).describe_to(visitor, double) end it "describes its source to a visitor" do visitor = double args = double expect( feature ).to receive(:describe_to).with(visitor, args) expect( scenario ).to receive(:describe_to).with(visitor, args) test_case.describe_source_to(visitor, args) end end describe "#name" do context "created from a scenario" do it "takes its name from the name of a scenario" do gherkin = gherkin do feature do scenario 'Scenario name' do step 'passing' end end end receiver = double.as_null_object expect( receiver ).to receive(:test_case) do |test_case| expect( test_case.name ).to eq 'Scenario name' expect( test_case.keyword ).to eq 'Scenario' end compile([gherkin], receiver) end end context "created from a scenario outline example" do it "takes its name from the name of the scenario outline and examples table" do gherkin = gherkin do feature do scenario_outline 'outline name' do step 'passing with arg' examples 'examples name' do row 'arg' row 'a' row 'b' end examples '' do row 'arg' row 'c' end end end end receiver = double.as_null_object expect( receiver ).to receive(:test_case) do |test_case| expect( test_case.name ).to eq 'outline name, examples name (#1)' expect( test_case.keyword ).to eq 'Scenario Outline' end.once.ordered expect( receiver ).to receive(:test_case) do |test_case| expect( test_case.name ).to eq 'outline name, examples name (#2)' end.once.ordered expect( receiver ).to receive(:test_case) do |test_case| expect( test_case.name ).to eq 'outline name, Examples (#1)' end.once.ordered compile [gherkin], receiver end end end describe "#location" do context "created from a scenario" do it "takes its location from the location of the scenario" do gherkin = gherkin('features/foo.feature') do feature do scenario do step end end end receiver = double.as_null_object expect( receiver ).to receive(:test_case) do |test_case| expect( test_case.location.to_s ).to eq 'features/foo.feature:3' end compile([gherkin], receiver) end end context "created from a scenario outline example" do it "takes its location from the location of the scenario outline example row" do gherkin = gherkin('features/foo.feature') do feature do scenario_outline do step 'passing with arg' examples do row 'arg' row '1' row '2' end end end end receiver = double.as_null_object expect( receiver ).to receive(:test_case) do |test_case| expect( test_case.location.to_s ).to eq 'features/foo.feature:8' end.once.ordered expect( receiver ).to receive(:test_case) do |test_case| expect( test_case.location.to_s ).to eq 'features/foo.feature:9' end.once.ordered compile [gherkin], receiver end end end describe "#tags" do it "includes all tags from the parent feature" do gherkin = gherkin do feature tags: ['@a', '@b'] do scenario tags: ['@c'] do step end scenario_outline tags: ['@d'] do step 'passing with arg' examples tags: ['@e'] do row 'arg' row 'x' end end end end receiver = double.as_null_object expect( receiver ).to receive(:test_case) do |test_case| expect( test_case.tags.map(&:name) ).to eq ['@a', '@b', '@c'] end.once.ordered expect( receiver ).to receive(:test_case) do |test_case| expect( test_case.tags.map(&:name) ).to eq ['@a', '@b', '@d', '@e'] end.once.ordered compile [gherkin], receiver end end describe "matching tags" do it "matches boolean expressions of tags" do gherkin = gherkin do feature tags: ['@a', '@b'] do scenario tags: ['@c'] do step end end end receiver = double.as_null_object expect( receiver ).to receive(:test_case) do |test_case| expect( test_case.match_tags?('@a') ).to be_truthy end compile [gherkin], receiver end end describe "matching names" do it "matches names against regexp" do gherkin = gherkin do feature 'first feature' do scenario 'scenario' do step 'missing' end end end receiver = double.as_null_object expect( receiver ).to receive(:test_case) do |test_case| expect( test_case.match_name?(/feature/) ).to be_truthy end compile [gherkin], receiver end end describe "#language" do it 'takes its language from the feature' do gherkin = Gherkin::Document.new('features/treasure.feature', %{# language: en-pirate Ahoy matey!: Treasure map Heave to: Find the treasure Gangway!: a map }) receiver = double.as_null_object expect( receiver ).to receive(:test_case) do |test_case| expect( test_case.language.iso_code ).to eq 'en-pirate' end compile([gherkin], receiver) end end describe "matching location" do let(:file) { 'features/path/to/the.feature' } let(:test_cases) do receiver = double.as_null_object result = [] allow( receiver ).to receive(:test_case) { |test_case| result << test_case } compile [source], receiver result end context "for a scenario" do let(:source) do Gherkin::Document.new(file, <<-END.unindent) Feature: Scenario: one Given one a # comment @tags Scenario: two Given two a And two b Scenario: three Given three b Scenario: with docstring Given a docstring """ this is a docstring """ Scenario: with a table Given a table | a | b | | 1 | 2 | Scenario: empty END end let(:test_case) do test_cases.find { |c| c.name == 'two' } end it 'matches the precise location of the scenario' do location = Ast::Location.new(file, 8) expect( test_case.match_locations?([location]) ).to be_truthy end it 'matches the precise location of an empty scenario' do empty_scenario_test_case = test_cases.find { |c| c.name == 'empty' } location = Ast::Location.new(file, 26) expect( empty_scenario_test_case.match_locations?([location]) ).to be_truthy end it 'matches multiple locations' do good_location = Ast::Location.new(file, 8) bad_location = Ast::Location.new(file, 5) expect( test_case.match_locations?([good_location, bad_location]) ).to be_truthy end it 'matches a location on the last step of the scenario' do location = Ast::Location.new(file, 10) expect( test_case.match_locations?([location]) ).to be_truthy end it "matches a location on the scenario's comment" do location = Ast::Location.new(file, 6) expect( test_case.match_locations?([location]) ).to be_truthy end it "matches a location on the scenario's tags" do location = Ast::Location.new(file, 7) expect( test_case.match_locations?([location]) ).to be_truthy end it "doesn't match a location after the last step of the scenario" do location = Ast::Location.new(file, 11) expect( test_case.match_locations?([location]) ).to be_falsey end it "doesn't match a location before the scenario" do location = Ast::Location.new(file, 5) expect( test_case.match_locations?([location]) ).to be_falsey end context "with a docstring" do let(:test_case) do test_cases.find { |c| c.name == 'with docstring' } end it "matches a location at the start the docstring" do location = Ast::Location.new(file, 17) expect( test_case.match_locations?([location]) ).to be_truthy end it "matches a location in the middle of the docstring" do location = Ast::Location.new(file, 18) expect( test_case.match_locations?([location]) ).to be_truthy end end context "with a table" do let(:test_case) do test_cases.find { |c| c.name == 'with a table' } end it "matches a location on the first table row" do location = Ast::Location.new(file, 23) expect( test_case.match_locations?([location]) ).to be_truthy end end end context "for a scenario outline" do let(:source) do Gherkin::Document.new(file, <<-END.unindent) Feature: Scenario: one Given one a # comment on line 6 @tags-on-line-7 Scenario Outline: two Given two a And two # comment on line 12 @tags-on-line-13 Examples: x1 | arg | | b | Examples: x2 | arg | | c | Scenario: three Given three b END end let(:test_case) do test_cases.find { |c| c.name == "two, x1 (#1)" } end it 'matches the precise location of the scenario outline examples table row' do location = Ast::Location.new(file, 16) expect( test_case.match_locations?([location]) ).to be_truthy end it 'matches a location on a step of the scenario outline' do location = Ast::Location.new(file, 10) expect( test_case.match_locations?([location]) ).to be_truthy end it "matches a location on the scenario outline's comment" do location = Ast::Location.new(file, 6) expect( test_case.match_locations?([location]) ).to be_truthy end it "matches a location on the scenario outline's tags" do location = Ast::Location.new(file, 7) expect( test_case.match_locations?([location]) ).to be_truthy end it "doesn't match a location after the last row of the examples table" do location = Ast::Location.new(file, 17) expect( test_case.match_locations?([location]) ).to be_falsey end it "doesn't match a location before the scenario outline" do location = Ast::Location.new(file, 5) expect( test_case.match_locations?([location]) ).to be_falsey end end end end end end end cucumber-core-1.1.3/spec/cucumber/core/test/action_spec.rb0000644000175000017500000000776512515135405023127 0ustar boutilboutilrequire 'cucumber/core/test/action' require 'cucumber/core/test/duration_matcher' module Cucumber module Core module Test describe Action do context "constructed without a block" do it "raises an error" do expect { Action.new }.to raise_error(ArgumentError) end end context "executing" do it "executes the block passed to the constructor" do executed = false action = Action.new { executed = true } action.execute expect( executed ).to be_truthy end it "returns a passed result if the block doesn't fail" do action = Action.new {} expect( action.execute ).to be_passed end it "returns a failed result when the block raises an error" do exception = StandardError.new action = Action.new { raise exception } result = action.execute expect( result ).to be_failed expect( result.exception ).to eq exception end it "yields the args passed to #execute to the block" do args = [double, double] args_spy = nil action = Action.new { |arg1, arg2| args_spy = [arg1, arg2] } action.execute(*args) expect(args_spy).to eq args end it "returns a pending result if a Result::Pending error is raised" do exception = Result::Pending.new("TODO") action = Action.new { raise exception } result = action.execute expect( result ).to be_pending expect( result.message ).to eq "TODO" end it "returns a skipped result if a Result::Skipped error is raised" do exception = Result::Skipped.new("Not working right now") action = Action.new { raise exception } result = action.execute expect( result ).to be_skipped expect( result.message ).to eq "Not working right now" end it "returns an undefined result if a Result::Undefined error is raised" do exception = Result::Undefined.new("new step") action = Action.new { raise exception } result = action.execute expect( result ).to be_undefined expect( result.message ).to eq "new step" end context "recording the duration" do before do time = double allow( Time ).to receive(:now) { time } allow( time ).to receive(:nsec).and_return(946752000, 946752001) allow( time ).to receive(:to_i).and_return(1377009235, 1377009235) end it "records the nanoseconds duration of the execution on the result" do action = Action.new { } duration = action.execute.duration expect( duration ).to be_duration 1 end it "records the duration of a failed execution" do action = Action.new { raise StandardError } duration = action.execute.duration expect( duration ).to be_duration 1 end end end context "skipping" do it "does not execute the block" do executed = false action = Action.new { executed = true } action.skip expect( executed ).to be_falsey end it "returns a skipped result" do action = Action.new {} expect( action.skip ).to be_skipped end end end describe UndefinedAction do let(:action) { UndefinedAction.new } let(:test_step) { double } context "executing" do it "returns an undefined result" do expect( action.execute ).to be_undefined end end context "skipping" do it "returns an undefined result" do expect( action.skip ).to be_undefined end end end end end end cucumber-core-1.1.3/spec/cucumber/core/gherkin/0000755000175000017500000000000012515135405020744 5ustar boutilboutilcucumber-core-1.1.3/spec/cucumber/core/gherkin/writer_spec.rb0000644000175000017500000002030712515135405023621 0ustar boutilboutilrequire 'cucumber/core/gherkin/writer' require 'unindent' module Cucumber::Core::Gherkin describe Writer do include Writer context 'specifying uri' do it 'generates a uri by default' do source = gherkin { feature } expect( source.uri ).to eq 'features/test.feature' end it 'allows you to specify a URI' do source = gherkin('features/path/to/my.feature') { feature } expect( source.uri ).to eq 'features/path/to/my.feature' end end context 'a feature' do it 'generates the feature statement' do source = gherkin { feature } expect( source ).to eq "Feature:\n" end context 'when a name is provided' do it 'includes the name in the feature statement' do source = gherkin do feature "A Feature\n" end expect( source ).to eq "Feature: A Feature\n" end end context 'when a description is provided' do it 'includes the description in the feature statement' do source = gherkin do feature "A Feature", description: <<-END This is the description which can span multiple lines. END end expected = <<-END Feature: A Feature This is the description which can span multiple lines. END expect( source ).to eq expected.unindent end end context 'when a keyword is provided' do it 'uses the supplied keyword' do source = gherkin do feature "A Feature", keyword: "Business Need" end expect( source ).to eq "Business Need: A Feature\n" end end context 'when a language is supplied' do it 'inserts a language statement' do source = gherkin do feature language: 'ru' end expect( source ).to eq "# language: ru\nFeature:\n" end end context 'when a comment is supplied' do it 'inserts a comment' do source = gherkin do comment 'wow' comment 'great' feature end expect( source.to_s ).to eq "# wow\n# great\nFeature:\n" end end context 'with a scenario' do it 'includes the scenario statement' do source = gherkin do feature "A Feature" do scenario end end expect( source.to_s ).to match(/Scenario:/) end context 'when a comment is provided' do it 'includes the comment in the scenario statement' do source = gherkin do feature do comment 'wow' scenario end end expect( source.to_s ).to eq <<-END.unindent Feature: # wow Scenario: END end end context 'when a description is provided' do it 'includes the description in the scenario statement' do source = gherkin do feature do scenario description: <<-END This is the description which can span multiple lines. END end end expect( source ).to eq <<-END.unindent Feature: Scenario: This is the description which can span multiple lines. END end end context 'with a step' do it 'includes the step statement' do source = gherkin do feature "A Feature" do scenario do step 'passing' end end end expect( source.to_s ).to match(/Given passing\Z/m) end context 'when a docstring is provided' do it 'includes the content type when provided' do source = gherkin do feature do scenario do step 'failing' do doc_string 'some text', 'text/plain' end end end end expect( source ).to eq <<-END.unindent Feature: Scenario: Given failing """text/plain some text """ END end end end end context 'with a background' do it 'can have a description' do source = gherkin do feature do background description: "One line,\nand two.." end end expect( source ).to eq <<-END.unindent Feature: Background: One line, and two.. END end end context 'with a scenario outline' do it 'can have a description' do source = gherkin do feature do scenario_outline description: "Doesn't need to be multi-line." end end expect( source ).to eq <<-END.unindent Feature: Scenario Outline: Doesn't need to be multi-line. END end context 'and examples table' do it 'can have a description' do source = gherkin do feature do scenario_outline do examples description: "Doesn't need to be multi-line." do end end end end expect( source ).to eq <<-END.unindent Feature: Scenario Outline: Examples: Doesn't need to be multi-line. END end end end end it 'generates a complex feature' do source = gherkin do comment 'wow' feature 'Fully featured', language: 'en', tags: '@always' do comment 'cool' background do step 'passing' end scenario do step 'passing' end comment 'here' scenario 'with doc string', tags: '@first @second' do comment 'and here' step 'passing' step 'failing', keyword: 'When' do doc_string <<-END I wish I was a little bit taller. I wish I was a baller. END end end scenario 'with a table...' do step 'passes:' do table do row 'name', 'age', 'location' row 'Janine', '43', 'Antarctica' end end end comment 'yay' scenario_outline 'eating' do step 'there are cucumbers' step 'I eat cucumbers', keyword: 'When' step 'I should have cucumbers', keyword: 'Then' comment 'hmmm' examples do row 'start', 'eat', 'left' row '12', '5', '7' row '20', '5', '15' end end end end expect( source.to_s ).to eq <<-END.unindent # language: en # wow @always Feature: Fully featured # cool Background: Given passing Scenario: Given passing # here @first @second Scenario: with doc string # and here Given passing When failing """ I wish I was a little bit taller. I wish I was a baller. """ Scenario: with a table... Given passes: | name | age | location | | Janine | 43 | Antarctica | # yay Scenario Outline: eating Given there are cucumbers When I eat cucumbers Then I should have cucumbers # hmmm Examples: | start | eat | left | | 12 | 5 | 7 | | 20 | 5 | 15 | END end end end cucumber-core-1.1.3/spec/cucumber/core/gherkin/parser_spec.rb0000644000175000017500000001502512515135405023602 0ustar boutilboutil# -*- encoding: utf-8 -*- require 'cucumber/core/gherkin/parser' require 'cucumber/core/gherkin/writer' module Cucumber module Core module Gherkin describe Parser do let(:receiver) { double } let(:parser) { Parser.new(receiver) } let(:visitor) { double } def parse parser.document(source) end context "for invalid gherkin" do let(:source) { Gherkin::Document.new(path, 'not gherkin') } let(:path) { 'path_to/the.feature' } it "raises an error" do expect { parse }.to raise_error(ParseError) do |error| expect( error.message ).to match(/not gherkin/) expect( error.message ).to match(/#{path}/) end end end RSpec::Matchers.define :a_null_feature do match do |actual| allow( visitor ).to receive(:feature).and_throw actual.describe_to( visitor ) end end context "for empty files" do let(:source) { Gherkin::Document.new(path, '') } let(:path) { 'path_to/the.feature' } it "creates a NullFeature" do expect( receiver ).to receive(:feature).with(a_null_feature) parse end end include Writer def self.source(&block) let(:source) { gherkin(&block) } end def feature result = nil allow( receiver ).to receive(:feature) { |feature| result = feature } parse result end context "when the Gherkin has a language header" do source do feature(language: 'ja', keyword: '機能') end it "sets the language from the Gherkin" do expect( feature.language.iso_code ).to eq 'ja' end end context "a Scenario with a DocString" do source do feature do scenario do step do doc_string("content") end end end end it "parses doc strings without error" do allow( visitor ).to receive(:feature).and_yield(visitor) allow( visitor ).to receive(:scenario).and_yield(visitor) allow( visitor ).to receive(:step).and_yield(visitor) location = double expected = Ast::DocString.new("content", "", location) expect( visitor ).to receive(:doc_string).with(expected) feature.describe_to(visitor) end end context "a Scenario with a DataTable" do source do feature do scenario do step do table do row "name", "surname" row "rob", "westgeest" end end end end end it "parses the DataTable" do visitor = double allow( visitor ).to receive(:feature).and_yield(visitor) allow( visitor ).to receive(:scenario).and_yield(visitor) allow( visitor ).to receive(:step).and_yield(visitor) expected = Ast::DataTable.new([['name', 'surname'], ['rob', 'westgeest']], Ast::Location.new('foo.feature', 23)) expect( visitor ).to receive(:data_table).with(expected) feature.describe_to(visitor) end end context "a Scenario with a Comment" do source do feature do comment 'wow' scenario end end it "parses the comment into the AST" do visitor = double allow( visitor ).to receive(:feature).and_yield(visitor) expect( visitor ).to receive(:scenario) do |scenario| expect( scenario.comments.join ).to eq "# wow" end feature.describe_to(visitor) end end context "a Scenario Outline" do source do feature do scenario_outline 'outline name' do step 'passing ' examples do row 'arg' row '1' row '2' end examples do row 'arg' row 'a' end end end end it "creates a scenario outline node" do allow( visitor ).to receive(:feature).and_yield(visitor) expect( visitor ).to receive(:scenario_outline) do |outline| expect( outline.name ).to eq 'outline name' end feature.describe_to(visitor) end it "creates a step node for each step of the scenario outline" do allow( visitor ).to receive(:feature).and_yield(visitor) allow( visitor ).to receive(:scenario_outline).and_yield(visitor) allow( visitor ).to receive(:examples_table) expect( visitor ).to receive(:outline_step) do |step| expect( step.name ).to eq 'passing ' end feature.describe_to(visitor) end it "creates an examples table node for each examples table" do allow( visitor ).to receive(:feature).and_yield(visitor) allow( visitor ).to receive(:scenario_outline).and_yield(visitor) allow( visitor ).to receive(:outline_step) expect( visitor ).to receive(:examples_table).exactly(2).times.and_yield(visitor) expect( visitor ).to receive(:examples_table_row) do |row| expect( row.number ).to eq 1 expect( row.values ).to eq ['1'] end.once.ordered expect( visitor ).to receive(:examples_table_row) do |row| expect( row.number ).to eq 2 expect( row.values ).to eq ['2'] end.once.ordered expect( visitor ).to receive(:examples_table_row) do |row| expect( row.number ).to eq 1 expect( row.values ).to eq ['a'] end.once.ordered feature.describe_to(visitor) end end context "a Scenario Outline with no Examples" do source do feature do scenario_outline do step 'passing ' end end end it "throws an error" do expect { feature.describe_to(double.as_null_object) }.to raise_error(ParseError) end end end end end end cucumber-core-1.1.3/spec/cucumber/core/filter_spec.rb0000644000175000017500000000566612515135405022156 0ustar boutilboutilrequire 'cucumber/core/gherkin/writer' require 'cucumber/core' require 'cucumber/core/filter' module Cucumber::Core describe Filter do include Cucumber::Core::Gherkin::Writer include Cucumber::Core describe ".new" do let(:receiver) { double.as_null_object } let(:doc) { gherkin do feature do scenario 'x' do step 'a step' end scenario 'y' do step 'a step' end end end } it "creates a filter class that can pass-through by default" do my_filter_class = Filter.new my_filter = my_filter_class.new expect(receiver).to receive(:test_case) { |test_case| expect(test_case.test_steps.length).to eq 1 expect(test_case.test_steps.first.name).to eq 'a step' }.exactly(2).times compile [doc], receiver, [my_filter] end context "customizing by subclassing" do # Each filter imlicitly gets a :receiver attribute # that you need to call with the new test case # once you've received yours and modified it. class BasicBlankingFilter < Filter.new def test_case(test_case) test_case.with_steps([]).describe_to(receiver) end end # You can pass the names of attributes when building a # filter, allowing you to have custom attributes. class NamedBlankingFilter < Filter.new(:name_pattern) def test_case(test_case) if test_case.name =~ name_pattern test_case.with_steps([]).describe_to(receiver) else test_case.describe_to(receiver) # or just call `super` end self end end it "can override methods from the base class" do expect(receiver).to receive(:test_case) { |test_case| expect(test_case.test_steps.length).to eq 0 }.exactly(2).times run BasicBlankingFilter.new end it "can take arguments" do expect(receiver).to receive(:test_case) { |test_case| expect(test_case.test_steps.length).to eq 0 }.once.ordered expect(receiver).to receive(:test_case) { |test_case| expect(test_case.test_steps.length).to eq 1 }.once.ordered run NamedBlankingFilter.new(/x/) end end context "customizing by using a block" do BlockBlankingFilter = Filter.new do def test_case(test_case) test_case.with_steps([]).describe_to(receiver) end end it "allows methods to be overridden" do expect(receiver).to receive(:test_case) { |test_case| expect(test_case.test_steps.length).to eq 0 }.exactly(2).times run BlockBlankingFilter.new end end def run(filter) compile [doc], receiver, [filter] end end end end cucumber-core-1.1.3/spec/cucumber/core/compiler_spec.rb0000644000175000017500000001653712515135405022502 0ustar boutilboutilrequire 'cucumber/core' require 'cucumber/core/compiler' require 'cucumber/core/gherkin/writer' module Cucumber::Core describe Compiler do include Gherkin::Writer include Cucumber::Core def self.stubs(*names) names.each do |name| let(name) { double(name.to_s) } end end it "compiles a feature with a single scenario" do gherkin_documents = [ gherkin do feature do scenario do step 'passing' end end end ] compile(gherkin_documents) do |visitor| expect( visitor ).to receive(:test_case).once.ordered.and_yield(visitor) expect( visitor ).to receive(:test_step).once.ordered expect( visitor ).to receive(:done).once.ordered end end it "compiles a feature with a background" do gherkin_documents = [ gherkin do feature do background do step 'passing' end scenario do step 'passing' end end end ] compile(gherkin_documents) do |visitor| expect( visitor ).to receive(:test_case).once.ordered.and_yield(visitor) expect( visitor ).to receive(:test_step).exactly(2).times.ordered expect( visitor ).to receive(:done).once.ordered end end it "compiles multiple features" do gherkin_documents = [ gherkin do feature do background do step 'passing' end scenario do step 'passing' end end end, gherkin do feature do background do step 'passing' end scenario do step 'passing' end end end ] compile(gherkin_documents) do |visitor| expect( visitor ).to receive(:test_case).once.ordered expect( visitor ).to receive(:test_step).twice.ordered expect( visitor ).to receive(:test_case).once.ordered expect( visitor ).to receive(:test_step).twice.ordered expect( visitor ).to receive(:done).once end end context "compiling scenario outlines" do it "compiles a scenario outline to test cases" do gherkin_documents = [ gherkin do feature do background do step 'passing' end scenario_outline do step 'passing ' step 'passing' examples 'examples 1' do row 'arg' row '1' row '2' end examples 'examples 2' do row 'arg' row 'a' end end end end ] compile(gherkin_documents) do |visitor| expect( visitor ).to receive(:test_case).exactly(3).times.and_yield(visitor) expect( visitor ).to receive(:test_step).exactly(9).times expect( visitor ).to receive(:done).once end end it 'replaces arguments correctly when generating test steps' do gherkin_documents = [ gherkin do feature do scenario_outline do step 'passing with ' step 'as well as ' examples do row 'arg1', 'arg2', 'arg3' row '1', '2', '3' end end end end ] compile(gherkin_documents) do |visitor| expect( visitor ).to receive(:test_step) do |test_step| visit_source(test_step) do |source_visitor| expect( source_visitor ).to receive(:step) do |step| expect(step.name).to eq 'passing 1 with 2' end end end.once.ordered expect( visitor ).to receive(:test_step) do |test_step| visit_source(test_step) do |source_visitor| expect( source_visitor ).to receive(:step) do |step| expect(step.name).to eq 'as well as 3' end end end.once.ordered expect( visitor ).to receive(:done).once.ordered end end end describe Compiler::FeatureCompiler do let(:receiver) { double('receiver') } let(:compiler) { Compiler::FeatureCompiler.new(receiver) } context "a scenario with a background" do stubs(:feature, :background, :background_step, :scenario, :scenario_step) it "sets the source correctly on the test steps" do expect( receiver ).to receive(:on_background_step).with( [feature, background, background_step] ) expect( receiver ).to receive(:on_step).with( [feature, scenario, scenario_step] ) expect( receiver ).to receive(:on_test_case).with( [feature, scenario] ) compiler.feature(feature) do |f| f.background(background) do |b| b.step background_step end f.scenario(scenario) do |s| s.step scenario_step end end end end context "a scenario outline" do stubs(:feature, :background, :background_step, :scenario_outline, :outline_step, :examples_table_1, :examples_table_1_row_1, :outline_ast_step, :examples_table_2, :examples_table_2_row_1, ) it "sets the source correctly on the test steps" do allow( outline_step ).to receive(:to_step) { outline_ast_step } expect( receiver ).to receive(:on_step).with( [feature, scenario_outline, examples_table_1, examples_table_1_row_1, outline_ast_step] ).ordered expect( receiver ).to receive(:on_test_case).with( [feature, scenario_outline, examples_table_1, examples_table_1_row_1] ).ordered expect( receiver ).to receive(:on_step).with( [feature, scenario_outline, examples_table_2, examples_table_2_row_1, outline_ast_step] ).ordered expect( receiver ).to receive(:on_test_case).with( [feature, scenario_outline, examples_table_2, examples_table_2_row_1] ).ordered compiler.feature(feature) do |f| f.scenario_outline(scenario_outline) do |o| o.outline_step outline_step o.examples_table(examples_table_1) do |t| t.examples_table_row(examples_table_1_row_1) end o.examples_table(examples_table_2) do |t| t.examples_table_row(examples_table_2_row_1) end end end end end end def visit_source(node) visitor = double.as_null_object yield visitor node.describe_source_to(visitor) end def compile(gherkin_documents) visitor = double allow( visitor ).to receive(:test_suite).and_yield(visitor) allow( visitor ).to receive(:test_case).and_yield(visitor) yield visitor super(gherkin_documents, visitor) end end end cucumber-core-1.1.3/spec/cucumber/core/ast/0000755000175000017500000000000012515135405020104 5ustar boutilboutilcucumber-core-1.1.3/spec/cucumber/core/ast/step_spec.rb0000644000175000017500000001401612515135405022420 0ustar boutilboutilrequire 'cucumber/core/ast/step' require 'gherkin/i18n' module Cucumber module Core module Ast describe Step do let(:step) do node, language, location, keyword, name = *double multiline_arg = EmptyMultilineArgument.new Step.new(node, language, location, keyword, name, multiline_arg) end describe "describing itself" do let(:visitor) { double } it "describes itself as a step" do expect( visitor ).to receive(:step).with(step) step.describe_to(visitor) end context "with no multiline argument" do it "does not try to describe any children" do allow( visitor ).to receive(:step).with(step).and_yield(visitor) step.describe_to(visitor) end end context "with a multiline argument" do let(:step) { Step.new(double, double, double, double, double, multiline_arg) } let(:multiline_arg) { double } it "tells its multiline argument to describe itself" do allow( visitor ).to receive(:step).with(step).and_yield(visitor) expect( multiline_arg ).to receive(:describe_to).with(visitor) step.describe_to(visitor) end end end describe "backtrace line" do let(:step) { Step.new(double, double, "path/file.feature:10", "Given ", "this step passes", double) } it "knows how to form the backtrace line" do expect( step.backtrace_line ).to eq("path/file.feature:10:in `Given this step passes'") end end describe "actual keyword" do let(:language) { ::Gherkin::I18n.get('en') } context "for keywords 'given', 'when' and 'then'" do let(:given_step) { Step.new(double, language, double, "Given ", double, double) } let(:when_step) { Step.new(double, language, double, "When ", double, double) } let(:then_step) { Step.new(double, language, double, "Then ", double, double) } it "returns the keyword itself" do expect( given_step.actual_keyword(nil) ).to eq("Given ") expect( when_step.actual_keyword(nil) ).to eq("When ") expect( then_step.actual_keyword(nil) ).to eq("Then ") end end context "for keyword 'and', 'but', and '*'" do let(:and_step) { Step.new(double, language, double, "And ", double, double) } let(:but_step) { Step.new(double, language, double, "But ", double, double) } let(:asterisk_step) { Step.new(double, language, double, "* ", double, double) } context "when the previous step keyword exist" do it "returns the previous step keyword" do expect( and_step.actual_keyword("Then ") ).to eq("Then ") expect( but_step.actual_keyword("Then ") ).to eq("Then ") expect( asterisk_step.actual_keyword("Then ") ).to eq("Then ") end end context "when the previous step keyword does not exist" do it "returns the 'given' keyword" do expect( and_step.actual_keyword(nil) ).to eq("Given ") expect( but_step.actual_keyword(nil) ).to eq("Given ") expect( asterisk_step.actual_keyword(nil) ).to eq("Given ") end end end context "for i18n languages" do let(:language) { ::Gherkin::I18n.get('en-lol') } let(:and_step) { Step.new(double, language, double, "AN ", double, double) } it "returns the keyword in the correct language" do expect( and_step.actual_keyword(nil) ).to eq("I CAN HAZ ") end end end end describe ExpandedOutlineStep do let(:outline_step) { double } let(:step) do node, language, location, keyword, name = *double multiline_arg = EmptyMultilineArgument.new ExpandedOutlineStep.new(outline_step, node, language, location, keyword, name, multiline_arg) end describe "describing itself" do let(:visitor) { double } it "describes itself as a step" do expect( visitor ).to receive(:step).with(step) step.describe_to(visitor) end context "with no multiline argument" do it "does not try to describe any children" do allow( visitor ).to receive(:step).with(step).and_yield(visitor) step.describe_to(visitor) end end context "with a multiline argument" do let(:step) { Step.new(double, double, double, double, double, multiline_arg) } let(:multiline_arg) { double } it "tells its multiline argument to describe itself" do allow( visitor ).to receive(:step).with(step).and_yield(visitor) expect( multiline_arg ).to receive(:describe_to).with(visitor) step.describe_to(visitor) end end end describe "matching location" do let(:location) { double } it "also match the outline steps location" do allow( location).to receive(:any?).and_return(nil) expect( outline_step ).to receive(:match_locations?).with(location) step.match_locations?(location) end end describe "backtrace line" do let(:outline_step) { OutlineStep.new(double, double, "path/file.feature:5", "Given ", "this step ", double) } let(:step) { ExpandedOutlineStep.new(outline_step, double, double, "path/file.feature:10", "Given ", "this step passes", double) } it "includes the outline step in the backtrace line" do expect( step.backtrace_line ).to eq("path/file.feature:10:in `Given this step passes'\n" + "path/file.feature:5:in `Given this step '") end end end end end end cucumber-core-1.1.3/spec/cucumber/core/ast/outline_step_spec.rb0000644000175000017500000000604712515135405024164 0ustar boutilboutilrequire 'cucumber/core/ast/outline_step' require 'cucumber/core/ast/examples_table' require 'cucumber/core/ast/data_table' require 'cucumber/core/ast/doc_string' require 'cucumber/core/ast/empty_multiline_argument' module Cucumber module Core module Ast describe OutlineStep do let(:outline_step) { OutlineStep.new(node, language, location, keyword, name, multiline_arg) } let(:node) { double } let(:language) { double } let(:location) { double } let(:keyword) { double } let(:name) { 'anything' } let(:multiline_arg) { EmptyMultilineArgument.new } describe 'location' do it "has a location" do expect( outline_step ).to respond_to(:location) end it 'knows the file and line' do allow( location ).to receive(:to_s) { 'file_name:8' } expect( outline_step.file_colon_line ).to eq 'file_name:8' end end describe "converting to a Step" do context "a single argument in the name" do let(:name) { 'a cucumber' } it "replaces the argument" do row = ExamplesTable::Row.new({'color' => 'green'}, 1, location, language) expect( outline_step.to_step(row).name ).to eq 'a green cucumber' end end context "when the step has a DataTable" do let(:outline_step) { OutlineStep.new(node, language, location, keyword, name, table) } let(:name) { "anything" } let(:table) { DataTable.new([['x', 'y'],['a', 'a ']], Location.new('foo.feature', 23)) } it "replaces the arguments in the DataTable" do visitor = double allow( visitor ).to receive(:step).and_yield(visitor) expect( visitor ).to receive(:data_table) do |data_table| expect( data_table.raw ).to eq [['x', 'y'], ['a', 'a replacement']] end row = ExamplesTable::Row.new({'arg' => 'replacement'}, 1, location, language) step = outline_step.to_step(row) step.describe_to(visitor) end end context "when the step has a DocString" do let(:location) { double } let(:outline_step) { OutlineStep.new(node, language, location, keyword, name, doc_string) } let(:doc_string) { DocString.new('a that needs replacing', '', location) } let(:name) { 'anything' } it "replaces the arguments in the DocString" do visitor = double allow( visitor ).to receive(:step).and_yield(visitor) expect( visitor ).to receive(:doc_string) do |doc_string| expect( doc_string.content ).to eq "a replacement that needs replacing" end row = ExamplesTable::Row.new({'arg' => 'replacement'}, 1, location, language) step = outline_step.to_step(row) step.describe_to(visitor) end end end end end end end cucumber-core-1.1.3/spec/cucumber/core/ast/location_spec.rb0000644000175000017500000000736412515135405023265 0ustar boutilboutilrequire 'cucumber/core/ast/location' module Cucumber::Core::Ast describe Location do let(:line) { 12 } let(:file) { "foo.feature" } describe "equality" do it "is equal to another Location on the same line of the same file" do one_location = Location.new(file, line) another_location = Location.new(file, line) expect( one_location ).to eq another_location end it "is not equal to a wild card of the same file" do expect( Location.new(file, line) ).not_to eq Location.new(file) end context "collections of locations" do it "behave as expected with uniq" do unique_collection = [Location.new(file, line), Location.new(file, line)].uniq expect( unique_collection ).to eq [Location.new(file, line)] end end end describe "line" do it "is an integer" do expect(Location.new(file, line).line).to be_kind_of(Integer) expect(Location.new(file, 1..2).line).to be_kind_of(Integer) expect(Location.of_caller.line).to be_kind_of(Integer) end end describe "to_s" do it "is file:line for a precise location" do expect( Location.new("foo.feature", 12).to_s ).to eq "foo.feature:12" end it "is file for a wildcard location" do expect( Location.new("foo.feature").to_s ).to eq "foo.feature" end it "is file:first_line..last_line for a ranged location" do expect( Location.new("foo.feature", 13..19).to_s ).to eq "foo.feature:13..19" end end describe "matches" do let(:matching) { Location.new(file, line) } let(:same_file_other_line) { Location.new(file, double) } let(:not_matching) { Location.new(other_file, line) } let(:other_file) { double } context 'a precise location' do let(:precise) { Location.new(file, line) } it "matches a precise location of the same file and line" do expect( matching ).to be_match(precise) end it "does not match a precise location on a differnt line in the same file" do expect( matching ).not_to be_match(same_file_other_line) end end context 'a wildcard' do let(:wildcard) { Location.new(file) } it "matches any location with the same filename" do expect( wildcard ).to be_match(matching) end it "is matched by any location of the same file" do expect( matching ).to be_match(wildcard) end it "does not match a location in a different file" do expect( wildcard ).not_to be_match(not_matching) end end context 'a range wildcard' do let(:range) { Location.new("foo.feature", 13..17) } it "matches the first line in the same file" do other = Location.new("foo.feature", 13) expect( range ).to be_match(other) end it "matches a line within the docstring in the same file" do other = Location.new("foo.feature", 15) expect( range ).to be_match(other) end it "is matched by a line within the docstring in the same file" do other = Location.new("foo.feature", 15) expect( other ).to be_match(range) end it "matches a wildcard in the same file" do wildcard = Location.new("foo.feature") expect( range ).to be_match(wildcard) end it "does not match a location outside of the range" do other = Location.new("foo.feature", 18) expect( range ).not_to be_match(other) end it "does not match a location in another file" do other = Location.new("bar.feature", 13) expect( range ).not_to be_match(other) end end end end end cucumber-core-1.1.3/spec/cucumber/core/ast/examples_table_spec.rb0000644000175000017500000000662212515135405024436 0ustar boutilboutilrequire 'cucumber/core/ast/examples_table' module Cucumber::Core::Ast describe ExamplesTable do let(:location) { double(:to_s => 'file.feature:8') } let(:language) { double } describe ExamplesTable::Header do let(:header) { ExamplesTable::Header.new(%w{foo bar baz}, location) } describe 'location' do it 'knows the file and line number' do expect( header.file_colon_line ).to eq 'file.feature:8' end end context 'building a row' do it 'includes the header values as keys' do expect( header.build_row(%w{1 2 3}, 1, location, language) ).to eq ExamplesTable::Row.new({'foo' => '1', 'bar' => '2', 'baz' => '3'}, 1, location, language) end end end describe ExamplesTable::Row do describe 'location' do it 'knows the file and line number' do row = ExamplesTable::Row.new({}, 1, location, language) expect( row.file_colon_line ).to eq 'file.feature:8' end end describe 'language' do it "has a language" do expect( ExamplesTable::Row.new({}, 1, location, language) ).to respond_to(:language) end end describe "expanding a string" do context "when an argument matches" do it "replaces the argument with the value from the row" do row = ExamplesTable::Row.new({'arg' => 'replacement'}, 1, location, language) text = 'this a test' expect( row.expand(text) ).to eq 'this replacement a test' end end context "when the replacement value is nil" do it "uses an empty string for the replacement" do row = ExamplesTable::Row.new({'color' => nil}, 1, location, language) text = 'a cucumber' expect( row.expand(text) ).to eq 'a cucumber' end end context "when an argument does not match" do it "ignores the arguments that do not match" do row = ExamplesTable::Row.new({'x' => '1', 'y' => '2'}, 1, location, language) text = 'foo bar ' expect( row.expand(text) ).to eq 'foo 1 bar ' end end end describe 'accesing the values' do it 'returns the actual row values' do row = ExamplesTable::Row.new({'x' => '1', 'y' => '2'}, 1, location, language) expect( row.values ).to eq ['1', '2'] end end describe 'equality' do let(:data) { {} } let(:number) { double } let(:location) { double } let(:original) { ExamplesTable::Row.new(data, number, location, language) } it 'is equal to another instance with the same data, number and location' do expect( original ).to eq ExamplesTable::Row.new(data, number, location, language) end it 'is not equal to another instance with different data, number or location' do expect( original ).not_to eq ExamplesTable::Row.new({'x' => 'y'}, number, location, language) expect( original ).not_to eq ExamplesTable::Row.new(data, double, location, language) expect( original ).not_to eq ExamplesTable::Row.new(data, number, double, double) end it 'is not equal to another type of object' do expect( original ).not_to eq double(data: data, number: number, location: location, language: language) end end end end end cucumber-core-1.1.3/spec/cucumber/core/ast/empty_multiline_argument_spec.rb0000644000175000017500000000106712515135405026571 0ustar boutilboutilrequire 'cucumber/core/ast/location' require 'cucumber/core/ast/empty_multiline_argument' module Cucumber module Core module Ast describe EmptyMultilineArgument do let(:location) { double } let(:arg) { EmptyMultilineArgument.new } describe "#data_table?" do it "returns false" do expect(arg).not_to be_data_table end end describe "#doc_string" do it "returns false" do expect(arg).not_to be_doc_string end end end end end end cucumber-core-1.1.3/spec/cucumber/core/ast/doc_string_spec.rb0000644000175000017500000000677612515135405023616 0ustar boutilboutilrequire 'cucumber/core/ast/location' require 'cucumber/core/ast/doc_string' require 'unindent' module Cucumber module Core module Ast describe DocString do let(:location) { double } let(:doc_string) { DocString.new(content, content_type, location) } describe "#data_table?" do let(:doc_string) { DocString.new("test", "text/plain" , location) } it "returns false" do expect(doc_string).not_to be_data_table end end describe "#doc_string" do let(:doc_string) { DocString.new("test", "text/plain" , location) } it "returns true" do expect(doc_string).to be_doc_string end end context '#map' do let(:content) { 'original content' } let(:content_type) { double } it 'yields with the content' do expect { |b| doc_string.map(&b) }.to yield_with_args(content) end it 'returns a new docstring with new content' do expect( doc_string.map { 'foo' }.content ).to eq 'foo' end it 'raises an error if no block is given' do expect { doc_string.map }.to raise_error ArgumentError end end context 'equality' do let(:content) { 'foo' } let(:content_type) { 'text/plain' } it 'is equal to another DocString with the same content and content_type' do expect( doc_string ).to eq DocString.new(content, content_type, location) end it 'is not equal to another DocString with different content' do expect( doc_string ).not_to eq DocString.new('bar', content_type, location) end it 'is not equal to another DocString with different content_type' do expect( doc_string ).not_to eq DocString.new(content, 'text/html', location) end it 'is equal to a string with the same content' do expect( doc_string ).to eq 'foo' end it 'returns false when compared with something odd' do expect( doc_string ).not_to eq 5 end end context 'quacking like a String' do let(:content) { 'content' } let(:content_type) { 'text/plain' } it 'delegates #encoding to the content string' do content.force_encoding('us-ascii') expect( doc_string.encoding ).to eq Encoding.find('US-ASCII') end it 'allows implicit convertion to a String' do expect( 'expected content' ).to include(doc_string) end it 'allows explicit convertion to a String' do expect( doc_string.to_s ).to eq 'content' end it 'delegates #gsub to the content string' do expect( doc_string.gsub(/n/, '_') ).to eq 'co_te_t' end it 'delegates #split to the content string' do expect(doc_string.split('n')).to eq ['co', 'te', 't'] end end end context "inspect" do let(:location) { Location.new("features/feature.feature", 8) } let(:content_type) { 'text/plain' } it "provides a useful inspect method" do doc_string = DocString.new("some text", content_type, location) expect(doc_string.inspect).to eq <<-END.chomp.unindent # END end end end end end cucumber-core-1.1.3/spec/cucumber/core/ast/data_table_spec.rb0000644000175000017500000000435212515135405023527 0ustar boutilboutil# encoding: utf-8 require 'cucumber/core/ast/data_table' module Cucumber module Core module Ast describe DataTable do let(:location) { Location.new('foo.feature', 9..12) } before do @table = DataTable.new([ %w{one four seven}, %w{4444 55555 666666} ], location) end describe "equality" do it "is equal to another table with the same data" do expect( DataTable.new([[1,2],[3,4]], location) ).to eq DataTable.new([[1,2],[3,4]], location) end it "is not equal to another table with different data" do expect( DataTable.new([[1,2],[3,4]], location) ).not_to eq DataTable.new([[1,2]], location) end it "is not equal to a non table" do expect( DataTable.new([[1,2],[3,4]], location) ).not_to eq Object.new end end describe "#data_table?" do let(:table) { DataTable.new([[1,2],[3,4]], location) } it "returns true" do expect(table).to be_data_table end end describe "#doc_string" do let(:table) { DataTable.new([[1,2],[3,4]], location) } it "returns false" do expect(table).not_to be_doc_string end end describe "#map" do let(:table) { DataTable.new([ %w{foo bar}, %w{1 2} ], location) } it 'yields the contents of each cell to the block' do expect { |b| table.map(&b) }.to yield_successive_args('foo', 'bar', '1', '2') end it 'returns a new table with the cells modified by the block' do expect( table.map { |cell| "*#{cell}*" } ).to eq DataTable.new([%w{*foo* *bar*}, %w{*1* *2*}], location) end end describe "#transpose" do before(:each) do @table = DataTable.new([ %w{one 1111}, %w{two 22222} ], location) end it "should transpose the table" do transposed = DataTable.new([ %w{one two}, %w{1111 22222} ], location) expect( @table.transpose ).to eq( transposed ) end end end end end end cucumber-core-1.1.3/spec/cucumber/core/ast/background_spec.rb0000644000175000017500000000065212515135405023565 0ustar boutilboutilrequire 'cucumber/core/ast/background' module Cucumber::Core::Ast describe Background do it "has a useful inspect" do location = Location.new("features/a_feature.feature", 3) background = Background.new(double, double, location, double, "Background", "the name", double, []) expect(background.inspect).to eq(%{#}) end end end cucumber-core-1.1.3/spec/coverage.rb0000644000175000017500000000037612515135405016706 0ustar boutilboutilrequire 'simplecov' formatters = [ SimpleCov::Formatter::HTMLFormatter ] if ENV['TRAVIS'] require 'coveralls' formatters << Coveralls::SimpleCov::Formatter end SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter[*formatters] SimpleCov.start cucumber-core-1.1.3/spec/capture_warnings.rb0000644000175000017500000000356612515135405020472 0ustar boutilboutil# With thanks to @myronmarston # https://github.com/vcr/vcr/blob/master/spec/capture_warnings.rb module CaptureWarnings def report_warnings(&block) current_dir = Dir.pwd warnings, errors = capture_error(&block).partition { |line| line.include?('warning') } project_warnings, other_warnings = warnings.uniq.partition { |line| line.include?(current_dir) } if errors.any? puts errors.join("\n") end if other_warnings.any? puts "#{ other_warnings.count } non-cucumber-core warnings detected, set VIEW_OTHER_WARNINGS=true to see them." print_warnings('other', other_warnings) if ENV['VIEW_OTHER_WARNINGS'] end # Until they fix https://bugs.ruby-lang.org/issues/10661 if RUBY_VERSION == "2.2.0" project_warnings = project_warnings.reject { |w| w =~ /warning: possible reference to past scope/ } end if project_warnings.any? puts "#{ project_warnings.count } cucumber-core warnings detected" print_warnings('cucumber-core', project_warnings) fail "Please remove all cucumber-core warnings." end ensure_system_exit_if_required end def capture_error(&block) old_stderr = STDERR.clone pipe_r, pipe_w = IO.pipe pipe_r.sync = true error = "" reader = Thread.new do begin loop do error << pipe_r.readpartial(1024) end rescue EOFError end end STDERR.reopen(pipe_w) block.call ensure capture_system_exit STDERR.reopen(old_stderr) pipe_w.close reader.join return error.split("\n") end def print_warnings(type, warnings) puts puts "-" * 30 + " #{type} warnings: " + "-" * 30 puts puts warnings.join("\n") puts puts "-" * 75 puts end def ensure_system_exit_if_required raise @system_exit if @system_exit end def capture_system_exit @system_exit = $! end end cucumber-core-1.1.3/lib/0000755000175000017500000000000012515135405014374 5ustar boutilboutilcucumber-core-1.1.3/lib/cucumber/0000755000175000017500000000000012515135405016201 5ustar boutilboutilcucumber-core-1.1.3/lib/cucumber/core/0000755000175000017500000000000012515135405017131 5ustar boutilboutilcucumber-core-1.1.3/lib/cucumber/core/version.rb0000644000175000017500000000016012515135405021140 0ustar boutilboutilmodule Cucumber module Core class Version def self.to_s "1.1.3" end end end end cucumber-core-1.1.3/lib/cucumber/core/test/0000755000175000017500000000000012515135405020110 5ustar boutilboutilcucumber-core-1.1.3/lib/cucumber/core/test/timer.rb0000644000175000017500000000103412515135405021553 0ustar boutilboutilrequire 'cucumber/core/test/result' module Cucumber module Core module Test class Timer def start @start_time = time_in_nanoseconds self end def duration Result::Duration.new(nsec) end def nsec time_in_nanoseconds - @start_time end def sec nsec / 10 ** 9.0 end private def time_in_nanoseconds t = Time.now t.to_i * 10 ** 9 + t.nsec end end end end end cucumber-core-1.1.3/lib/cucumber/core/test/step.rb0000644000175000017500000000236512515135405021416 0ustar boutilboutilrequire 'cucumber/core/test/result' require 'cucumber/core/test/action' module Cucumber module Core module Test class Step attr_reader :source def initialize(source, action = Test::UndefinedAction.new) raise ArgumentError if source.any?(&:nil?) @source, @action = source, action end def describe_to(visitor, *args) visitor.test_step(self, *args) end def describe_source_to(visitor, *args) source.reverse.each do |node| node.describe_to(visitor, *args) end self end def skip(*args) @action.skip(*args) end def execute(*args) @action.execute(*args) end def with_action(&block) self.class.new(source, Test::Action.new(&block)) end def name source.last.name end def location source.last.location end def match_locations?(queried_locations) return true if queried_locations.include? location source.any? { |s| s.match_locations?(queried_locations) } end def inspect "#<#{self.class}: #{location}>" end end end end end cucumber-core-1.1.3/lib/cucumber/core/test/runner.rb0000644000175000017500000000663212515135405021755 0ustar boutilboutilrequire 'cucumber/core/test/timer' module Cucumber module Core module Test class Runner attr_reader :report, :running_test_case private :report, :running_test_case def initialize(report) @report = report end def test_case(test_case, &descend) @running_test_case = RunningTestCase.new report.before_test_case(test_case) descend.call(self) report.after_test_case(test_case, running_test_case.result) self end def test_step(test_step) report.before_test_step test_step step_result = running_test_case.execute(test_step) report.after_test_step test_step, step_result self end def around_hook(hook, &continue) running_test_case.execute(hook, &continue) self end def done report.done self end class RunningTestCase def initialize @timer = Timer.new.start @status = Status::Unknown.new(Result::Unknown.new) end def execute(test_step, &continue) status.execute(test_step, self, &continue) end def result status.result(@timer.duration) end def failed(step_result) @status = Status::Failing.new(step_result) self end def passed(step_result) @status = Status::Passing.new(step_result) self end def pending(message, step_result) @status = Status::Pending.new(step_result) self end def skipped(step_result) @status = Status::Skipping.new(step_result) self end def undefined(step_result) failed(step_result) self end def exception(step_exception, step_result) self end def duration(step_duration, step_result) self end attr_reader :status private :status module Status class Base attr_reader :step_result private :step_result def initialize(step_result) @step_result = step_result end def execute(test_step, monitor, &continue) result = test_step.execute(monitor.result, &continue) result.describe_to(monitor, result) end def result raise NoMethodError, "Override me" end end class Unknown < Base def result(duration) Result::Unknown.new end end class Passing < Base def result(duration) Result::Passed.new(duration) end end class Failing < Base def execute(test_step, monitor, &continue) test_step.skip(monitor.result) end def result(duration) step_result.with_duration(duration) end end Pending = Class.new(Failing) class Skipping < Failing def result(duration) step_result.with_duration(duration) end end end end end end end end cucumber-core-1.1.3/lib/cucumber/core/test/result.rb0000644000175000017500000001222512515135405021755 0ustar boutilboutil# encoding: UTF-8 module Cucumber module Core module Test module Result # Defines predicate methods on a result class with only the given one # returning true def self.status_queries(status) Module.new do [:passed, :failed, :undefined, :unknown, :skipped, :pending].each do |possible_status| define_method("#{possible_status}?") do possible_status == status end end end end # Null object for results. Represents the state where we haven't run anything yet class Unknown include Result.status_queries :unknown def describe_to(visitor, *args) self end end class Passed include Result.status_queries(:passed) attr_accessor :duration def initialize(duration) raise ArgumentError unless duration @duration = duration end def describe_to(visitor, *args) visitor.passed(*args) visitor.duration(duration, *args) self end def to_s "✓" end end class Failed include Result.status_queries(:failed) attr_reader :duration, :exception def initialize(duration, exception) raise ArgumentError unless duration raise ArgumentError unless exception @duration = duration @exception = exception end def describe_to(visitor, *args) visitor.failed(*args) visitor.duration(duration, *args) visitor.exception(exception, *args) if exception self end def to_s "✗" end def with_duration(new_duration) self.class.new(new_duration, exception) end end # Base class for exceptions that can be raised in a step defintion causing # the step to have that result. class Raisable < StandardError attr_reader :message, :duration def initialize(message = "", duration = UnknownDuration.new, backtrace = nil) @message, @duration = message, duration super(message) set_backtrace(backtrace) if backtrace end def with_message(new_message) self.class.new(new_message, duration, backtrace) end def with_duration(new_duration) self.class.new(message, new_duration, backtrace) end end class Undefined < Raisable include Result.status_queries :undefined def describe_to(visitor, *args) visitor.undefined(*args) visitor.duration(duration, *args) self end def to_s "?" end end class Skipped < Raisable include Result.status_queries :skipped def describe_to(visitor, *args) visitor.skipped(*args) visitor.duration(duration, *args) self end def to_s "-" end end class Pending < Raisable include Result.status_queries :pending def describe_to(visitor, *args) visitor.pending(self, *args) visitor.duration(duration, *args) self end def to_s "P" end end # # An object that responds to the description protocol from the results # and collects summary information. # # e.g. # summary = Result::Summary.new # Result::Passed.new(0).describe_to(summary) # puts summary.total_passed # => 1 # class Summary attr_reader :exceptions, :durations def initialize @totals = Hash.new { 0 } @exceptions = [] @durations = [] end def method_missing(name, *args) if name =~ /^total_/ get_total(name) else increment_total(name) end end def exception(exception) @exceptions << exception self end def duration(duration) @durations << duration self end def total @totals.reduce(0) { |total, status| total += status[1] } end private def get_total(method_name) status = method_name.to_s.gsub('total_', '').to_sym return @totals.fetch(status) { 0 } end def increment_total(status) @totals[status] += 1 self end end class Duration attr_reader :nanoseconds def initialize(nanoseconds) @nanoseconds = nanoseconds end end class UnknownDuration def tap(&block) self end def nanoseconds raise "#nanoseconds only allowed to be used in #tap block" end end end end end end cucumber-core-1.1.3/lib/cucumber/core/test/filters/0000755000175000017500000000000012515135405021560 5ustar boutilboutilcucumber-core-1.1.3/lib/cucumber/core/test/filters/tag_filter.rb0000644000175000017500000000630712515135405024233 0ustar boutilboutilrequire 'cucumber/core/filter' module Cucumber module Core module Test class TagFilter < Filter.new(:filter_expressions) def test_case(test_case) test_cases << test_case if test_case.match_tags?(filter_expressions) test_case.describe_to(receiver) end self end def done tag_limits.enforce(test_cases) receiver.done self end private def test_cases @test_cases ||= TestCases.new end def tag_limits @tag_limits ||= TagLimits.new(filter_expressions) end class TestCases attr_reader :test_cases_by_tag_name private :test_cases_by_tag_name def initialize @test_cases_by_tag_name = Hash.new { [] } end def <<(test_case) test_case.tags.each do |tag| test_cases_by_tag_name[tag.name] += [test_case] end self end def with_tag_name(tag_name) test_cases_by_tag_name[tag_name] end end class TagLimits TAG_MATCHER = /^ (?:~)? #The tag negation symbol "~". This is optional and not captured. (?\@\w+) #Captures the tag name including the "@" symbol. \: #The seperator, ":", between the tag name and the limit. (?\d+) #Caputres the limit number. $/x attr_reader :limit_list private :limit_list def initialize(filter_expressions) @limit_list = Array(filter_expressions).flat_map do |raw_expression| raw_expression.split(/\s*,\s*/) end.map do |filter_expression| TAG_MATCHER.match(filter_expression) end.compact.each_with_object({}) do |matchdata, limit_list| limit_list[matchdata[:tag_name]] = Integer(matchdata[:limit]) end end def enforce(test_cases) limit_breaches = limit_list.reduce([]) do |breaches, (tag_name, limit)| tag_count = test_cases.with_tag_name(tag_name).count if tag_count > limit tag_locations = test_cases.with_tag_name(tag_name).map(&:location) breaches << TagLimitBreach.new( tag_count, limit, tag_name, tag_locations ) end breaches end raise TagExcess.new(limit_breaches) if limit_breaches.any? self end end TagLimitBreach = Struct.new( :tag_count, :tag_limit, :tag_name, :tag_locations ) do def message "#{tag_name} occurred #{tag_count} times, but the limit was set to #{tag_limit}\n " + tag_locations.map(&:to_s).join("\n ") end alias :to_s :message end class TagExcess < StandardError def initialize(limit_breaches) super(limit_breaches.map(&:to_s).join("\n")) end end end end end end cucumber-core-1.1.3/lib/cucumber/core/test/filters/name_filter.rb0000644000175000017500000000073112515135405024373 0ustar boutilboutilrequire 'cucumber/core/filter' module Cucumber module Core module Test class NameFilter < Filter.new(:name_regexps) def test_case(test_case) if accept?(test_case) test_case.describe_to(receiver) end self end private def accept?(test_case) name_regexps.empty? || name_regexps.any? { |name_regexp| test_case.match_name?(name_regexp) } end end end end end cucumber-core-1.1.3/lib/cucumber/core/test/filters/locations_filter.rb0000644000175000017500000000153412515135405025450 0ustar boutilboutilrequire 'cucumber/core/filter' module Cucumber module Core module Test # Sorts and filters scenarios based on a list of locations class LocationsFilter < Filter.new(:locations) def test_case(test_case) test_cases << test_case self end def done sorted_test_cases.each do |test_case| test_case.describe_to receiver end receiver.done self end private def sorted_test_cases locations.map { |location| test_cases_matching(location) }.flatten end def test_cases_matching(location) test_cases.select do |test_case| test_case.match_locations?([location]) end end def test_cases @test_cases ||= [] end end end end end cucumber-core-1.1.3/lib/cucumber/core/test/filters.rb0000644000175000017500000000022712515135405022106 0ustar boutilboutilrequire 'cucumber/core/test/filters/locations_filter' require 'cucumber/core/test/filters/name_filter' require 'cucumber/core/test/filters/tag_filter' cucumber-core-1.1.3/lib/cucumber/core/test/case.rb0000644000175000017500000000756512515135405021365 0ustar boutilboutilrequire 'cucumber/core/test/result' module Cucumber module Core module Test class Case attr_reader :source, :test_steps, :around_hooks def initialize(test_steps, source, around_hooks = []) raise ArgumentError.new("test_steps should be an Array but is a #{test_steps.class}") unless test_steps.kind_of?(Array) @test_steps = test_steps @source = source @around_hooks = around_hooks end def step_count test_steps.count end def describe_to(visitor, *args) visitor.test_case(self, *args) do |child_visitor| compose_around_hooks(child_visitor, *args) do test_steps.each do |test_step| test_step.describe_to(child_visitor, *args) end end end self end def describe_source_to(visitor, *args) source.reverse.each do |node| node.describe_to(visitor, *args) end self end def with_steps(test_steps) self.class.new(test_steps, source, around_hooks) end def with_around_hooks(around_hooks) self.class.new(test_steps, source, around_hooks) end def name @name ||= NameBuilder.new(self).result end def keyword @keyword ||= NameBuilder.new(self).keyword end def tags @tags ||= TagCollector.new(self).result end require 'gherkin/tag_expression' def match_tags?(*expressions) ::Gherkin::TagExpression.new(expressions.flatten).evaluate(tags.map {|t| ::Gherkin::Formatter::Model::Tag.new(t.name, t.line) }) end def match_name?(name_regexp) source.any? { |node| node.respond_to?(:name) && node.name =~ name_regexp } end def language feature.language end def location source.last.location end def match_locations?(queried_locations) return true if source.any? { |s| s.match_locations?(queried_locations) } test_steps.any? { |node| node.match_locations? queried_locations } end def inspect "#<#{self.class}: #{location}>" end def feature source.first end private def compose_around_hooks(visitor, *args, &block) around_hooks.reverse.reduce(block) do |continue, hook| -> { hook.describe_to(visitor, *args, &continue) } end.call end class NameBuilder attr_reader :result attr_reader :keyword def initialize(test_case) test_case.describe_source_to self end def feature(*) self end def scenario(scenario) @result = scenario.name @keyword = scenario.keyword self end def scenario_outline(outline) @result = outline.name + @result @keyword = outline.keyword self end def examples_table(table) name = table.name.strip name = table.keyword if name.length == 0 @result = ", #{name}" + @result self end def examples_table_row(row) @result = " (##{row.number})" self end end class TagCollector attr_reader :result def initialize(test_case) @result = [] test_case.describe_source_to self end [:feature, :scenario, :scenario_outline, :examples_table].each do |node_name| define_method(node_name) do |node| @result = node.tags + @result self end end def examples_table_row(*) end end end end end end cucumber-core-1.1.3/lib/cucumber/core/test/around_hook.rb0000644000175000017500000000142512515135405022747 0ustar boutilboutilmodule Cucumber module Core module Test class AroundHook def initialize(&block) @block = block @timer = Timer.new end def describe_to(visitor, *args, &continue) visitor.around_hook(self, *args, &continue) end def execute(*args, &continue) @timer.start @block.call(continue) Result::Unknown.new # Around hook does not know the result of the inner test steps rescue Result::Raisable => exception exception.with_duration(@timer.duration) rescue Exception => exception failed(exception) end private def failed(exception) Result::Failed.new(@timer.duration, exception) end end end end end cucumber-core-1.1.3/lib/cucumber/core/test/action.rb0000644000175000017500000000275612515135405021724 0ustar boutilboutilrequire 'cucumber/core/test/result' require 'cucumber/core/test/timer' require 'cucumber/core/test/result' require 'cucumber/core/ast/location' module Cucumber module Core module Test class Action def initialize(&block) raise ArgumentError, "Passing a block to execute the action is mandatory." unless block @block = block @timer = Timer.new end def skip(*) skipped end def execute(*args) @timer.start @block.call(*args) passed rescue Result::Raisable => exception exception.with_duration(@timer.duration) rescue Exception => exception failed(exception) end def location Ast::Location.new(*@block.source_location) end def inspect "#<#{self.class}: #{location}>" end private def passed Result::Passed.new(@timer.duration) end def failed(exception) Result::Failed.new(@timer.duration, exception) end def skipped Result::Skipped.new end end class UnskippableAction < Action def skip(*args) execute(*args) end end class UndefinedAction def execute(*) undefined end def skip(*) undefined end private def undefined Result::Undefined.new end end end end end cucumber-core-1.1.3/lib/cucumber/core/report/0000755000175000017500000000000012515135405020444 5ustar boutilboutilcucumber-core-1.1.3/lib/cucumber/core/report/summary.rb0000644000175000017500000000101112515135405022457 0ustar boutilboutilmodule Cucumber module Core module Report class Summary attr_reader :test_cases, :test_steps def initialize @test_cases = Test::Result::Summary.new @test_steps = Test::Result::Summary.new end def after_test_case(test_case, result) result.describe_to test_cases end def after_test_step(test_step, result) result.describe_to test_steps end def method_missing(*) end end end end end cucumber-core-1.1.3/lib/cucumber/core/platform.rb0000644000175000017500000000104412515135405021301 0ustar boutilboutil# Detect the platform we're running on so we can tweak behaviour # in various places. require 'rbconfig' module Cucumber unless defined?(Cucumber::VERSION) JRUBY = defined?(JRUBY_VERSION) IRONRUBY = defined?(RUBY_ENGINE) && RUBY_ENGINE == "ironruby" WINDOWS = RbConfig::CONFIG['host_os'] =~ /mswin|mingw/ OS_X = RbConfig::CONFIG['host_os'] =~ /darwin/ WINDOWS_MRI = WINDOWS && !JRUBY && !IRONRUBY RUBY_2_0 = RUBY_VERSION =~ /^2\.0/ RUBY_1_9 = RUBY_VERSION =~ /^1\.9/ end end cucumber-core-1.1.3/lib/cucumber/core/gherkin/0000755000175000017500000000000012515135405020560 5ustar boutilboutilcucumber-core-1.1.3/lib/cucumber/core/gherkin/writer/0000755000175000017500000000000012515135405022074 5ustar boutilboutilcucumber-core-1.1.3/lib/cucumber/core/gherkin/writer/helpers.rb0000644000175000017500000001004112515135405024057 0ustar boutilboutilmodule Cucumber module Core module Gherkin module Writer module HasOptionsInitializer def self.included(base) base.extend HasDefaultKeyword end attr_reader :name, :options private :name, :options def initialize(*args) @comments = args.shift if args.first.is_a?(Array) @comments ||= [] @options = args.pop if args.last.is_a?(Hash) @options ||= {} @name = args.first end private def comments_statement @comments end def keyword options.fetch(:keyword) { self.class.keyword } end def name_statement "#{keyword}: #{name}".strip end def tag_statement tags end def tags options[:tags] end module HasDefaultKeyword def default_keyword(keyword) @keyword = keyword end def keyword @keyword end end end module AcceptsComments def comment(line) comment_lines << "# #{line}" end def comment_lines @comment_lines ||= [] end def slurp_comments comment_lines.tap { @comment_lines = nil } end end module HasElements include AcceptsComments def self.included(base) base.extend HasElementBuilders end def build(source = []) elements.inject(source + statements) { |acc, el| el.build(acc) } end private def elements @elements ||= [] end module HasElementBuilders def elements(*names) names.each { |name| element(name) } end private def element(name) define_method name do |*args, &source| factory_name = String(name).split("_").map(&:capitalize).join factory = Writer.const_get(factory_name) factory.new(slurp_comments, *args).tap do |builder| builder.instance_exec(&source) if source elements << builder end self end end end end module Indentation def self.level(number) Module.new do define_method :indent do |string, amount=nil| amount ||= number return string if string.nil? || string.empty? (' ' * amount) + string end define_method :indent_level do number end define_method :prepare_statements do |*statements| statements.flatten.compact.map { |s| indent(s) } end end end end module HasDescription private def description options.fetch(:description) { '' }.split("\n").map(&:strip) end def description_statement description.map { |s| indent(s,2) } unless description.empty? end end module HasRows def row(*cells) rows << cells end def rows @rows ||= [] end private def row_statements(indent=nil) rows.map { |row| indent(table_row(row), indent) } end def table_row(row) padded = pad(row) "| #{padded.join(' | ')} |" end def pad(row) row.map.with_index { |text, position| justify_cell(text, position) } end def column_length(column) lengths = rows.transpose.map { |r| r.map(&:length).max } lengths[column] end def justify_cell(cell, position) cell.ljust(column_length(position)) end end end end end end cucumber-core-1.1.3/lib/cucumber/core/gherkin/writer.rb0000644000175000017500000001206512515135405022425 0ustar boutilboutilrequire 'cucumber/core/gherkin/writer/helpers' require 'cucumber/core/gherkin/document' module Cucumber module Core module Gherkin module Writer NEW_LINE = '' def gherkin(uri = nil, &source) uri ||= 'features/test.feature' builder = Gherkin.new(uri, &source) builder.build end class Gherkin def initialize(uri, &source) @uri, @source = uri, source end def comment(line) comment_lines << "# #{line}" end def comment_lines @comment_lines ||= [] end def feature(*args, &source) @feature = Feature.new(comment_lines, *args).tap do |builder| builder.instance_exec(&source) if source end self end def build instance_exec(&@source) Document.new(@uri, @feature.build.join("\n")) end end class Feature include HasElements include HasOptionsInitializer include HasDescription include Indentation.level(0) default_keyword 'Feature' elements :background, :scenario, :scenario_outline def build(source = []) elements.inject(source + statements) { |acc, el| el.build(acc) + [NEW_LINE] } end private def language options[:language] end def statements prepare_statements language_statement, comments_statement, tag_statement, name_statement, description_statement, NEW_LINE end def language_statement "# language: #{language}" if language end end class Background include HasElements include HasOptionsInitializer include HasDescription include Indentation.level 2 default_keyword 'Background' elements :step private def statements prepare_statements comments_statement, tag_statement, name_statement, description_statement end end class Scenario include HasElements include HasOptionsInitializer include HasDescription include Indentation.level 2 default_keyword 'Scenario' elements :step private def statements prepare_statements comments_statement, tag_statement, name_statement, description_statement end end class ScenarioOutline include HasElements include HasOptionsInitializer include HasDescription include Indentation.level 2 default_keyword 'Scenario Outline' elements :step, :examples private def statements prepare_statements comments_statement, tag_statement, name_statement, description_statement end end class Step include HasElements include HasOptionsInitializer include Indentation.level 4 default_keyword 'Given' elements :table def doc_string(string, content_type='') elements << DocString.new(string, content_type) end private def statements prepare_statements comments_statement, name_statement end def name_statement "#{keyword} #{name}" end end class Table include Indentation.level(6) include HasRows def initialize(*) end def build(source) source + statements end private def statements row_statements end end class DocString include Indentation.level(6) attr_reader :strings, :content_type private :strings, :content_type def initialize(string, content_type) @strings = string.split("\n").map(&:strip) @content_type = content_type end def build(source) source + statements end private def statements prepare_statements doc_string_statement end def doc_string_statement [ %["""#{content_type}], strings, '"""' ] end end class Examples include HasOptionsInitializer include HasRows include HasDescription include Indentation.level(4) default_keyword 'Examples' def build(source) source + statements end private def statements prepare_statements NEW_LINE, comments_statement, tag_statement, name_statement, description_statement, row_statements(2) end end end end end end cucumber-core-1.1.3/lib/cucumber/core/gherkin/parser.rb0000644000175000017500000000243412515135405022404 0ustar boutilboutilrequire 'cucumber/core/gherkin/ast_builder' require 'gherkin/parser/parser' module Cucumber module Core module Gherkin ParseError = Class.new(StandardError) class Parser attr_reader :receiver private :receiver def initialize(receiver) @receiver = receiver end def document(document) builder = AstBuilder.new(document.uri) parser = ::Gherkin::Parser::Parser.new(builder, true, "root", false) begin parser.parse(document.body, document.uri, 0) builder.language = parser.i18n_language receiver.feature builder.result rescue *PARSER_ERRORS => e raise Core::Gherkin::ParseError.new("#{document.uri}: #{e.message}") end end def done receiver.done self end private PARSER_ERRORS = if Cucumber::JRUBY [ ::Java::GherkinLexer::LexingError ] else [ ::Gherkin::Lexer::LexingError, ::Gherkin::Parser::ParseError, ] end end end end end cucumber-core-1.1.3/lib/cucumber/core/gherkin/document.rb0000644000175000017500000000051312515135405022722 0ustar boutilboutil module Cucumber module Core module Gherkin class Document attr_reader :uri, :body def initialize(uri, body) @uri = uri @body = body end def to_s body end def ==(other) to_s == other.to_s end end end end end cucumber-core-1.1.3/lib/cucumber/core/gherkin/ast_builder.rb0000644000175000017500000002100712515135405023402 0ustar boutilboutilrequire 'cucumber/core/ast' require 'cucumber/core/platform' require 'gherkin/rubify' module Cucumber module Core module Gherkin # Builds an AST of a feature by listening to events from the # Gherkin parser. class AstBuilder def initialize(path) @path = path @feature_builder = nil end def result return Ast::NullFeature.new unless @feature_builder @feature_builder.result(language) end def language=(language) @language = language end def uri(uri) @path = uri end def feature(node) @feature_builder = FeatureBuilder.new(file, node) end def background(node) builder = BackgroundBuilder.new(file, node) @feature_builder.background_builder = builder @current = builder end def scenario(node) builder = ScenarioBuilder.new(file, node) @feature_builder.add_child builder @current = builder end def scenario_outline(node) builder = ScenarioOutlineBuilder.new(file, node) @feature_builder.add_child builder @current = builder end def examples(node) @current.add_examples file, node end def step(node) @current.add_step file, node end def eof end def syntax_error(state, event, legal_events, line) # raise "SYNTAX ERROR" end private def language @language || raise("Language has not been set") end def file @path end class Builder attr_reader :file, :node private :file, :node def initialize(file, node) @file = file @node = node end private def tags node.tags.map do |tag| Ast::Tag.new( Ast::Location.new(file, tag.line), tag.name) end end def location Ast::Location.new(file, node.line) end def comments node.comments.map do |comment| Ast::Comment.new( Ast::Location.new(file, comment.line), comment.value ) end end end class FeatureBuilder < Builder attr_accessor :background_builder private :background_builder def initialize(*) super @background_builder = nil end def result(language) background = background(language) Ast::Feature.new( node, language, location, background, comments, tags, node.keyword, node.name.lstrip, node.description.rstrip, children.map { |builder| builder.result(background, language, tags) } ) end def add_child(child) children << child end def children @children ||= [] end private def background(language) return Ast::EmptyBackground.new unless background_builder @background ||= background_builder.result(language) end end class BackgroundBuilder < Builder def result(language) Ast::Background.new( node, language, location, comments, node.keyword, node.name, node.description, steps(language) ) end def add_step(file, node) step_builders << ScenarioBuilder::StepBuilder.new(file, node) end private def steps(language) step_builders.map { |step_builder| step_builder.result(language) } end def step_builders @step_builders ||= [] end end class ScenarioBuilder < Builder def result(background, language, feature_tags) Ast::Scenario.new( node, language, location, background, comments, tags, feature_tags, node.keyword, node.name, node.description, steps(language) ) end def add_step(file, node) step_builders << StepBuilder.new(file, node) end private def steps(language) step_builders.map { |step_builder| step_builder.result(language) } end def step_builders @step_builders ||= [] end class StepBuilder < Builder def result(language) Ast::Step.new( node, language, location, node.keyword, node.name, MultilineArgument.from(node.doc_string || node.rows, location) ) end end end class ScenarioOutlineBuilder < Builder def result(background, language, feature_tags) raise ParseError.new("Missing Examples section for Scenario Outline at #{location}") if examples_builders.empty? Ast::ScenarioOutline.new( node, language, location, background, comments, tags, feature_tags, node.keyword, node.name, node.description, steps(language), examples_tables(language) ) end def add_examples(file, node) examples_builders << ExamplesTableBuilder.new(file, node) end def add_step(file, node) step_builders << StepBuilder.new(file, node) end private def steps(language) step_builders.map { |step_builder| step_builder.result(language) } end def step_builders @step_builders ||= [] end def examples_tables(language) examples_builders.map { |examples_builder| examples_builder.result(language) } end def examples_builders @examples_builders ||= [] end class ExamplesTableBuilder < Builder def result(language) Ast::ExamplesTable.new( node, location, comments, tags, node.keyword, node.name, node.description, header, example_rows(language) ) end private def header row = node.rows[0] Ast::ExamplesTable::Header.new(row.cells, location) end def example_rows(language) _, *raw_examples = *node.rows raw_examples.each_with_index.map do |row, index| header.build_row(row.cells, index + 1, location.on_line(row.line), language) end end end class StepBuilder < Builder def result(language) Ast::OutlineStep.new( node, language, location, node.keyword, node.name, MultilineArgument.from(node.doc_string || node.rows, location) ) end end end module MultilineArgument class << self include ::Gherkin::Rubify def from(argument, parent_location) return Ast::EmptyMultilineArgument.new unless argument argument = rubify(argument) case argument when ::Gherkin::Formatter::Model::DocString Ast::DocString.new(argument.value, argument.content_type, parent_location.on_line(argument.line_range)) when Array location = parent_location.on_line(argument.first.line..argument.last.line) Ast::DataTable.new(argument.map{|row| row.cells}, location) else raise ArgumentError, "Don't know how to convert #{argument.inspect} into a MultilineArgument" end end end end end end end end cucumber-core-1.1.3/lib/cucumber/core/filter.rb0000644000175000017500000000455612515135405020755 0ustar boutilboutilmodule Cucumber module Core # Filters process test cases. # # Each filter must respond to the following protocol: # # * `with_receiver(new_receiver)` # * `test_case(test_case, &describe_test_steps)` # * `done` # # The `with_receiver` method is used to assemble the filters into a chain. It should return a new instance of the # filter with the receiver attribute set to the new receiver. The receiver will also respond to the filter protocol. # # When a `test_case` message is received, the filter can choose to: # # 1. pass the test_case directly to its receiver (no-op) # 2. pass a modified copy of the test_case to its receiver # 3. not pass the test_case to its receiver at all # # Finally, the `done` message is sent. A filter should pass this message directly to its receiver. # module Filter # Utility method for quick construction of filter classes. # # @example Example usage: # # class BlankingFilter < Filter.new(:name_to_blank, :receiver) # def test_case(test_case) # if name_to_blank == test_case.name # test_case.with_steps([]).describe_to(receiver) # else # test_case.describe_to(receiver) # end # end # end # # The attribute names passed to the Filter constructor will become private attributes of # your filter class. # def self.new(*attributes, &block) attributes << :receiver result = Class.new do attr_reader(*attributes) private(*attributes) define_method(:initialize) do |*args| attributes.zip(args) do |name, value| instance_variable_set("@#{name}".to_sym, value) end end def test_case(test_case) test_case.describe_to receiver self end def done receiver.done self end define_method(:with_receiver) do |new_receiver| args = attributes.map { |name| instance_variable_get("@#{name}".to_sym) } args[-1] = new_receiver self.class.new(*args) end end if block Class.new(result, &block) else result end end end end end cucumber-core-1.1.3/lib/cucumber/core/compiler.rb0000644000175000017500000000756112515135405021301 0ustar boutilboutilrequire 'cucumber/core/test/case' require 'cucumber/core/test/step' module Cucumber module Core # Compiles the AST into test cases class Compiler attr_reader :receiver private :receiver def initialize(receiver) @receiver = receiver end def feature(feature) compiler = FeatureCompiler.new(TestCaseBuilder.new(receiver)) feature.describe_to(compiler) self end def done receiver.done self end # @private class TestCaseBuilder attr_reader :receiver private :receiver def initialize(receiver) @receiver = receiver end def on_background_step(source) background_test_steps << Test::Step.new(source) self end def on_step(source) test_steps << Test::Step.new(source) self end def on_test_case(source) Test::Case.new(test_steps, source).describe_to(receiver) @test_steps = nil self end private def background_test_steps @background_test_steps ||= [] end def test_steps @test_steps ||= background_test_steps.dup end end # @private class FeatureCompiler attr_reader :receiver private :receiver def initialize(receiver) @receiver = receiver end def feature(feature, &descend) @feature = feature descend.call(self) self end def background(background, &descend) source = [@feature, background] compiler = BackgroundCompiler.new(source, receiver) descend.call(compiler) self end def scenario(scenario, &descend) source = [@feature, scenario] scenario_compiler = ScenarioCompiler.new(source, receiver) descend.call(scenario_compiler) receiver.on_test_case(source) self end def scenario_outline(scenario_outline, &descend) source = [@feature, scenario_outline] compiler = ScenarioOutlineCompiler.new(source, receiver) descend.call(compiler) self end end # @private class ScenarioOutlineCompiler attr_reader :source, :receiver private :source, :receiver def initialize(source, receiver) @source = source @receiver = receiver end def outline_step(outline_step) outline_steps << outline_step self end def examples_table(examples_table, &descend) @examples_table = examples_table descend.call(self) self end def examples_table_row(row) steps(row).each do |step| receiver.on_step(source + [@examples_table, row, step]) end receiver.on_test_case(source + [@examples_table, row]) self end private def steps(row) outline_steps.map { |s| s.to_step(row) } end def outline_steps @outline_steps ||= [] end end # @private class ScenarioCompiler attr_reader :source, :receiver private :source, :receiver def initialize(source, receiver) @source = source @receiver = receiver end def step(step) receiver.on_step(source + [step]) self end end # @private class BackgroundCompiler attr_reader :source, :receiver private :source, :receiver def initialize(source, receiver) @source = source @receiver = receiver end def step(step) receiver.on_background_step(source + [step]) self end end end end end cucumber-core-1.1.3/lib/cucumber/core/ast/0000755000175000017500000000000012515135405017720 5ustar boutilboutilcucumber-core-1.1.3/lib/cucumber/core/ast/tag.rb0000644000175000017500000000052112515135405021016 0ustar boutilboutilmodule Cucumber module Core module Ast class Tag include HasLocation attr_reader :name def initialize(location, name) @location = location @name = name end def inspect %{#<#{self.class} "#{name}" (#{location})>} end end end end end cucumber-core-1.1.3/lib/cucumber/core/ast/step.rb0000644000175000017500000000432112515135405021220 0ustar boutilboutilrequire 'cucumber/core/ast/describes_itself' require 'cucumber/core/ast/location' module Cucumber module Core module Ast class Step include HasLocation include DescribesItself attr_reader :keyword, :name, :language, :exception, :multiline_arg, :gherkin_statement def initialize(gherkin_statement, language, location, keyword, name, multiline_arg) @gherkin_statement, @language, @location, @keyword, @name, @multiline_arg = gherkin_statement, language, location, keyword, name, multiline_arg end def to_sexp [:step, line, keyword, name, @multiline_arg.to_sexp] end def backtrace_line "#{location}:in `#{keyword}#{name}'" end def actual_keyword(previous_step_keyword = nil) if [language.keywords('and'), language.keywords('but')].flatten.uniq.include? keyword if previous_step_keyword.nil? language.keywords('given').reject{|kw| kw == '* '}[0] else previous_step_keyword end else keyword end end def inspect keyword_and_name = [keyword, name].join(": ") %{#<#{self.class} "#{keyword_and_name}" (#{location})>} end private def children [@multiline_arg] end def description_for_visitors :step end end class ExpandedOutlineStep < Step def initialize(outline_step, gherkin_statement, language, location, keyword, name, multiline_arg) @outline_step, @gherkin_statement, @language, @location, @keyword, @name, @multiline_arg = outline_step, gherkin_statement, language, location, keyword, name, multiline_arg end alias :self_match_locations? :match_locations? def match_locations?(queried_locations) self_match_locations?(queried_locations) or @outline_step.match_locations?(queried_locations) end alias :step_backtrace_line :backtrace_line def backtrace_line "#{step_backtrace_line}\n" + "#{@outline_step.location}:in `#{@outline_step.keyword}#{@outline_step.name}'" end end end end end cucumber-core-1.1.3/lib/cucumber/core/ast/scenario_outline.rb0000644000175000017500000000275612515135405023621 0ustar boutilboutilrequire 'cucumber/core/ast/names' require 'cucumber/core/ast/location' require 'cucumber/core/ast/empty_background' require 'cucumber/core/ast/describes_itself' module Cucumber module Core module Ast class ScenarioOutline include Names include HasLocation include DescribesItself MissingExamples = Class.new(StandardError) attr_reader :gherkin_statement, :language, :background, :comments, :tags, :feature_tags, :keyword, :title, :description, :steps, :examples_tables, :line private :language, :background, :comments, :feature_tags, :line def initialize(gherkin_statement, language, location, background, comments, tags, feature_tags, keyword, title, description, steps, examples_tables) @gherkin_statement = gherkin_statement @language = language @location = location @background = background @comments = comments @tags = tags @feature_tags = feature_tags @keyword = keyword @title = title @description = description @steps = steps @examples_tables = examples_tables end private def children @steps + @examples_tables end def description_for_visitors :scenario_outline end end end end end cucumber-core-1.1.3/lib/cucumber/core/ast/scenario.rb0000644000175000017500000000313312515135405022050 0ustar boutilboutilrequire 'cucumber/core/ast/describes_itself' require 'cucumber/core/ast/names' require 'cucumber/core/ast/empty_background' require 'cucumber/core/ast/location' module Cucumber module Core module Ast class Scenario include Names include HasLocation include DescribesItself attr_reader :gherkin_statement, :language, :location, :background, :comments, :tags, :feature_tags, :keyword, :title, :description, :raw_steps private :raw_steps, :description def initialize(gherkin_statement, language, location, background, comments, tags, feature_tags, keyword, title, description, raw_steps) @gherkin_statement = gherkin_statement @language = language @location = location @background = background @comments = comments @tags = tags @feature_tags = feature_tags @keyword = keyword @title = title @description = description @raw_steps = raw_steps end def children raw_steps end def to_sexp sexp = [:scenario, line, keyword, name] comment = comment.to_sexp sexp += [comment] if comment tags = tags.to_sexp sexp += tags if tags.any? sexp += step_invocations.to_sexp if step_invocations.any? sexp end private def description_for_visitors :scenario end end end end end cucumber-core-1.1.3/lib/cucumber/core/ast/outline_step.rb0000644000175000017500000000257212515135405022765 0ustar boutilboutilrequire 'cucumber/core/ast/location' require 'cucumber/core/ast/describes_itself' require 'cucumber/core/ast/step' module Cucumber module Core module Ast class OutlineStep include HasLocation include DescribesItself attr_reader :gherkin_statement, :language, :location, :keyword, :name, :multiline_arg def initialize(gherkin_statement, language, location, keyword, name, multiline_arg) @gherkin_statement, @language, @location, @keyword, @name, @multiline_arg = gherkin_statement, language, location, keyword, name, multiline_arg @language || raise("Language is required!") end def to_step(row) Ast::ExpandedOutlineStep.new(self, gherkin_statement, language, row.location, keyword, row.expand(name), replace_multiline_arg(row)) end def inspect keyword_and_name = [keyword, name].join(": ") %{#<#{self.class} "#{keyword_and_name}" (#{location})>} end private def description_for_visitors :outline_step end def children # TODO remove duplication with Step # TODO spec [@multiline_arg] end def replace_multiline_arg(example_row) return unless multiline_arg multiline_arg.map { |cell| example_row.expand(cell) } end end end end end cucumber-core-1.1.3/lib/cucumber/core/ast/names.rb0000644000175000017500000000114612515135405021352 0ustar boutilboutilmodule Cucumber module Core module Ast module Names attr_reader :description def name title end def title warn("deprecated. Use #name") @title end def legacy_conflated_name_and_description s = @title s += "\n#{@description}" if @description != "" s end def to_s @title end def inspect keyword_and_name = [keyword, name].join(": ") %{#<#{self.class} "#{keyword_and_name}" (#{location})>} end end end end end cucumber-core-1.1.3/lib/cucumber/core/ast/location.rb0000644000175000017500000000671112515135405022062 0ustar boutilboutilrequire 'forwardable' require 'cucumber/core/platform' module Cucumber module Core module Ast class Location < Struct.new(:filepath, :lines) WILDCARD = :* extend Forwardable def_delegator :lines, :include? def_delegator :lines, :line def_delegator :filepath, :same_as? def_delegator :filepath, :filename, :file def self.of_caller file, raw_line = *caller[1].split(':')[0..1] new(file, raw_line.to_i) end def initialize(filepath, raw_lines=WILDCARD) filepath || raise(ArgumentError, "file is mandatory") super(FilePath.new(filepath), Lines.new(raw_lines)) end def match?(other) other.same_as?(filepath) && other.include?(lines) end def to_s [filepath.to_s, lines.to_s].reject { |v| v == WILDCARD.to_s }.join(":") end def hash self.class.hash ^ to_s.hash end def to_str to_s end def on_line(new_line) Location.new(filepath.filename, new_line) end def inspect "<#{self.class}: #{to_s}>" end class FilePath < Struct.new(:filename) def same_as?(other) filename == other.filename end def to_s filename end end require 'set' class Lines < Struct.new(:data) protected :data attr_reader :line def initialize(raw_data) if Cucumber::JRUBY && raw_data.is_a?(::Java::GherkinFormatterModel::Range) raw_data = Range.new(raw_data.first, raw_data.last) end super Array(raw_data).to_set @line = data.first end def include?(other) return true if (data|other.data).include?(WILDCARD) other.data.subset?(data) || data.subset?(other.data) end def to_s boundary.join('..') end def inspect "<#{self.class}: #{to_s}>" end protected def boundary first_and_last(value).uniq end def at_index(idx) data.to_a[idx] end def value method :at_index end def first_and_last(something) [0, -1].map(&something) end end end module HasLocation def file_colon_line location.to_s end def file location.file end def line location.line end def location raise('Please set @location in the constructor') unless defined?(@location) @location end def match_locations?(queried_locations) return true if attributes.any? { |node| node.match_locations? queried_locations } queried_locations.any? { |queried_location| queried_location.match? location } end def attributes [tags, comments, multiline_arg].flatten end def tags # will be overriden by nodes that actually have tags [] end def comments # will be overriden by nodes that actually have comments [] end def multiline_arg # will be overriden by nodes that actually have a multiline_argument EmptyMultilineArgument.new end end end end end cucumber-core-1.1.3/lib/cucumber/core/ast/feature.rb0000644000175000017500000000352412515135405021704 0ustar boutilboutilrequire 'cucumber/core/ast/describes_itself' require 'cucumber/core/ast/names' require 'cucumber/core/ast/location' module Cucumber module Core module Ast # Represents the root node of a parsed feature. class Feature include Names include HasLocation include DescribesItself attr_reader :gherkin_statement, :language, :location, :background, :comments, :tags, :keyword, :title, :description, :feature_elements private :description def initialize(gherkin_statement, language, location, background, comments, tags, keyword, title, description, feature_elements) @gherkin_statement = gherkin_statement @language = language @location = location @background = background @comments = comments @tags = tags @keyword = keyword @title = title @description = description @feature_elements = feature_elements end def children [background] + @feature_elements end def short_name first_line = name.split(/\n/)[0] if first_line =~ /#{language.keywords('feature')}:(.*)/ $1.strip else first_line end end def to_sexp sexp = [:feature, file, name] comment = @comment.to_sexp sexp += [comment] if comment tags = @tags.to_sexp sexp += tags if tags.any? sexp += [@background.to_sexp] if @background sexp += @feature_elements.map{|fe| fe.to_sexp} sexp end private def description_for_visitors :feature end end class NullFeature def method_missing(*args, &block) self end end end end end cucumber-core-1.1.3/lib/cucumber/core/ast/examples_table.rb0000644000175000017500000000517012515135405023235 0ustar boutilboutilrequire 'cucumber/core/ast/describes_itself' require 'cucumber/core/ast/location' require 'cucumber/core/ast/names' module Cucumber module Core module Ast class ExamplesTable include Names include HasLocation include DescribesItself def initialize(gherkin_statement, location, comments, tags, keyword, title, description, header, example_rows) @gherkin_statement = gherkin_statement @location = location @comments = comments @tags = tags @keyword = keyword @title = title @description = description @header = header @example_rows = example_rows end attr_reader :gherkin_statement, :location, :comments, :tags, :keyword, :title, :description, :header, :example_rows private :title, :description, :example_rows private def description_for_visitors :examples_table end def children @example_rows end class Header include HasLocation def initialize(cells, location) @cells = cells @location = location end def values @cells end def build_row(row_cells, number, location, language) Row.new(Hash[@cells.zip(row_cells)], number, location, language) end def inspect "#<#{self.class} #{values} (#{location})>" end end class Row include DescribesItself include HasLocation attr_reader :number, :language def initialize(data, number, location, language) raise ArgumentError, data.to_s unless data.is_a?(Hash) @data = data @number = number @location = location @language = language end def ==(other) return false unless other.class == self.class other.number == number && other.location == location && other.data == data end def values @data.values end def expand(string) result = string.dup @data.each do |key, value| result.gsub!("<#{key}>", value.to_s) end result end def inspect "#<#{self.class}: #{@data.inspect} (#{location})>" end protected attr_reader :data private def description_for_visitors :examples_table_row end end end end end end cucumber-core-1.1.3/lib/cucumber/core/ast/empty_multiline_argument.rb0000644000175000017500000000075612515135405025377 0ustar boutilboutilmodule Cucumber module Core module Ast class EmptyMultilineArgument def describe_to(*) self end def data_table? false end def doc_string? false end def map(&block) self end def match_locations?(*args) false end def to_sexp [] end def inspect "#<#{self.class}>" end end end end end cucumber-core-1.1.3/lib/cucumber/core/ast/empty_background.rb0000644000175000017500000000033712515135405023605 0ustar boutilboutilmodule Cucumber module Core module Ast class EmptyBackground def describe_to(*) self end def inspect "#<#{self.class.name}>" end end end end end cucumber-core-1.1.3/lib/cucumber/core/ast/doc_string.rb0000644000175000017500000000375112515135405022406 0ustar boutilboutilrequire 'cucumber/core/ast/describes_itself' require 'delegate' module Cucumber module Core module Ast # Represents an inline argument in a step. Example: # # Given the message # """ # I like # Cucumber sandwich # """ # # The text between the pair of """ is stored inside a DocString, # which is yielded to the StepDefinition block as the last argument. # # The StepDefinition can then access the String via the #to_s method. In the # example above, that would return: "I like\nCucumber sandwich" # # Note how the indentation from the source is stripped away. # class DocString < SimpleDelegator include HasLocation include DescribesItself attr_accessor :file attr_reader :content_type, :content def initialize(string, content_type, location) @content = string @content_type = content_type @location = location super @content end def data_table? false end def doc_string? true end def map raise ArgumentError, "No block given" unless block_given? new_content = yield content self.class.new(new_content, content_type, location) end def to_step_definition_arg self end def ==(other) if other.respond_to?(:content_type) return false unless content_type == other.content_type end if other.respond_to?(:to_str) return content == other.to_str end false end def inspect [ %{#<#{self.class} (#{location})}, %{ """#{content_type}}, %{ #{@content}}, %{ """>} ].join("\n") end private def description_for_visitors :doc_string end end end end end cucumber-core-1.1.3/lib/cucumber/core/ast/describes_itself.rb0000644000175000017500000000056312515135405023562 0ustar boutilboutilmodule Cucumber module Core module Ast module DescribesItself def describe_to(visitor, *args) visitor.send(description_for_visitors, self, *args) do |child_visitor| children.each do |child| child.describe_to(child_visitor, *args) end end self end end end end end cucumber-core-1.1.3/lib/cucumber/core/ast/data_table.rb0000644000175000017500000000574312515135405022336 0ustar boutilboutilrequire 'gherkin/rubify' require 'gherkin/lexer/i18n_lexer' require 'gherkin/formatter/escaping' require 'cucumber/core/ast/describes_itself' require 'cucumber/core/ast/location' module Cucumber module Core module Ast # Step Definitions that match a plain text Step with a multiline argument table # will receive it as an instance of DataTable. A DataTable object holds the data of a # table parsed from a feature file and lets you access and manipulate the data # in different ways. # # For example: # # Given I have: # | a | b | # | c | d | # # And a matching StepDefinition: # # Given /I have:/ do |table| # data = table.raw # end # # This will store [['a', 'b'], ['c', 'd']] in the data variable. # class DataTable include DescribesItself include HasLocation include ::Gherkin::Rubify # Creates a new instance. +raw+ should be an Array of Array of String # or an Array of Hash # You don't typically create your own DataTable objects - Cucumber will do # it internally and pass them to your Step Definitions. # def initialize(raw, location) raw = ensure_array_of_array(rubify(raw)) verify_rows_are_same_length(raw) @raw = raw.freeze @location = location end attr_reader :raw def to_step_definition_arg dup end def data_table? true end def doc_string? false end # Creates a copy of this table # def dup self.class.new(raw.dup, location) end # Returns a new, transposed table. Example: # # | a | 7 | 4 | # | b | 9 | 2 | # # Gets converted into the following: # # | a | b | # | 7 | 9 | # | 4 | 2 | # def transpose self.class.new(raw.transpose, location) end def map(&block) new_raw = raw.map do |row| row.map(&block) end self.class.new(new_raw, location) end def ==(other) other.class == self.class && raw == other.raw end def inspect %{#<#{self.class} #{raw.inspect} (#{location})>} end private def verify_rows_are_same_length(raw) begin raw.transpose rescue IndexError raise ArgumentError, "Rows must all be the same length" end end def ensure_array_of_array(array) Hash === array[0] ? hashes_to_array(array) : array end def hashes_to_array(hashes) header = hashes[0].keys.sort [header] + hashes.map{|hash| header.map{|key| hash[key]}} end def description_for_visitors :data_table end end end end end cucumber-core-1.1.3/lib/cucumber/core/ast/comment.rb0000644000175000017500000000073012515135405021707 0ustar boutilboutilrequire 'cucumber/core/ast/location' module Cucumber module Core module Ast class Comment include HasLocation attr_reader :location, :value private :location, :value def initialize(location, value) @location = location @value = value end def to_s value end def inspect %{#<#{self.class} #{value} (#{location})} end end end end end cucumber-core-1.1.3/lib/cucumber/core/ast/background.rb0000644000175000017500000000207512515135405022370 0ustar boutilboutilrequire 'cucumber/core/ast/names' require 'cucumber/core/ast/location' require 'cucumber/core/ast/describes_itself' module Cucumber module Core module Ast class Background include Names include HasLocation include DescribesItself def initialize(gherkin_statement, language, location, comments, keyword, title, description, raw_steps) @gherkin_statement = gherkin_statement @language = language @location = location @comments = comments @keyword = keyword @title = title @description = description @raw_steps = raw_steps end attr_reader :language, :title, :description, :raw_steps private :language, :title, :description, :raw_steps attr_accessor :feature attr_accessor :comments, :keyword, :location attr_reader :gherkin_statement def children raw_steps end private def description_for_visitors :background end end end end end cucumber-core-1.1.3/lib/cucumber/core/ast.rb0000644000175000017500000000100612515135405020242 0ustar boutilboutilrequire 'cucumber/core/ast/comment' require 'cucumber/core/ast/tag' require 'cucumber/core/ast/feature' require 'cucumber/core/ast/empty_background' require 'cucumber/core/ast/empty_multiline_argument' require 'cucumber/core/ast/background' require 'cucumber/core/ast/scenario' require 'cucumber/core/ast/step' require 'cucumber/core/ast/doc_string' require 'cucumber/core/ast/data_table' require 'cucumber/core/ast/scenario_outline' require 'cucumber/core/ast/outline_step' require 'cucumber/core/ast/examples_table' cucumber-core-1.1.3/lib/cucumber/core.rb0000644000175000017500000000172512515135405017463 0ustar boutilboutilrequire 'cucumber/core/gherkin/parser' require 'cucumber/core/gherkin/document' require 'cucumber/core/compiler' require 'cucumber/core/test/runner' module Cucumber module Core def execute(gherkin_documents, report, filters = []) receiver = Test::Runner.new(report) compile gherkin_documents, receiver, filters self end def compile(gherkin_documents, last_receiver, filters = []) first_receiver = compose(filters, last_receiver) compiler = Compiler.new(first_receiver) parse gherkin_documents, compiler self end private def parse(gherkin_documents, compiler) parser = Core::Gherkin::Parser.new(compiler) gherkin_documents.each do |document| parser.document document end parser.done self end def compose(filters, last_receiver) filters.reverse.reduce(last_receiver) do |receiver, filter| filter.with_receiver(receiver) end end end end cucumber-core-1.1.3/cucumber-core.gemspec0000644000175000017500000000235512515135405017733 0ustar boutilboutil# -*- encoding: utf-8 -*- $LOAD_PATH.unshift File.expand_path("../lib", __FILE__) require "cucumber/core/version" Gem::Specification.new do |s| s.name = 'cucumber-core' s.version = Cucumber::Core::Version s.authors = ["Aslak Hellesøy", "Matt Wynne", "Steve Tooke", "Oleg Sukhodolsky", "Tom Brand"] s.description = 'Core library for the Cucumber BDD app' s.summary = "cucumber-core-#{s.version}" s.email = 'cukes@googlegroups.com' s.homepage = "http://cukes.info" s.platform = Gem::Platform::RUBY s.license = "MIT" s.required_ruby_version = ">= 1.9.3" s.add_dependency 'gherkin', '~> 2.12.0' s.add_development_dependency 'bundler', '>= 1.3.5' s.add_development_dependency 'rake', '>= 0.9.2' s.add_development_dependency 'rspec', '~> 3' s.add_development_dependency 'unindent', '>= 1.0' s.add_development_dependency 'kramdown', '~> 1.4.2' # For coverage reports s.add_development_dependency 'coveralls', '~> 0.7' s.rubygems_version = ">= 1.6.1" s.files = `git ls-files`.split("\n").reject {|path| path =~ /\.gitignore$/ } s.test_files = `git ls-files -- spec/*`.split("\n") s.rdoc_options = ["--charset=UTF-8"] s.require_path = "lib" end cucumber-core-1.1.3/Rakefile0000644000175000017500000000075712515135405015304 0ustar boutilboutil# encoding: utf-8 require 'rubygems' require 'bundler' Bundler::GemHelper.install_tasks $:.unshift File.expand_path("../lib", __FILE__) require "rspec/core/rake_task" RSpec::Core::RakeTask.new(:spec) do |t| t.ruby_opts = %w[-r./spec/coverage -w] t.rspec_opts = %w[--color] end require_relative 'spec/capture_warnings' include CaptureWarnings namespace :spec do task :warnings do report_warnings do Rake::Task['spec'].invoke end end end task default: ['spec:warnings'] cucumber-core-1.1.3/README.md0000644000175000017500000001063212515135405015107 0ustar boutilboutil# cucumber-core [![Build Status](https://secure.travis-ci.org/cucumber/cucumber-ruby-core.png)](http://travis-ci.org/cucumber/cucumber-ruby-core) [![Code Climate](https://codeclimate.com/github/cucumber/cucumber-ruby-core.png)](https://codeclimate.com/github/cucumber/cucumber-ruby-core) [![Coverage Status](https://coveralls.io/repos/cucumber/cucumber-ruby-core/badge.png?branch=master)](https://coveralls.io/r/cucumber/cucumber-ruby-core?branch=master) [![Dependency Status](https://gemnasium.com/cucumber/cucumber-ruby-core.png)](https://gemnasium.com/cucumber/cucumber-ruby-core) Cucumber Core is the [inner hexagon](http://alistair.cockburn.us/Hexagonal+architecture) for the [Ruby flavour of Cucumber](https://github.com/cucumber/cucumber). It contains the core domain logic to execute Cucumber features. It has no user interface, just a Ruby API. If you're interested in how Cucumber works, or in building other tools that work with Gherkin documents, you've come to the right place. ## An overview The entry-point is a single method on the module `Cucumber::Core` called [`#execute`](http://rubydoc.info/gems/cucumber-core/Cucumber/Core#execute-instance_method). Here's what it does: 1. Parses the plain-text Gherkin documents into an **AST** 2. Compiles the AST down to **test cases** 3. Passes the activated test cases through any **filters** 4. Executes the test cases, calling back to the **report** We've introduced a number of concepts here, so let's go through them in detail. ### The AST The Abstract Syntax Tree or [AST](http://rubydoc.info/gems/cucumber-core/Cucumber/Core/Ast) is an object graph that represents the Gherkin documents you've passed into the core. Things like [Feature](http://rubydoc.info/gems/cucumber-core/Cucumber/Core/Ast/Feature), [Scenario](http://rubydoc.info/gems/cucumber-core/Cucumber/Core/Ast/Scenario) and [ExamplesTable](ExamplesTable). These are immutable value objects. ### Test cases Your gherkin might contain scenarios, as well as examples from tables beneath a scenario outline. Test cases represent the general case of both of these. We compile the AST down to instances of [`Cucumber::Core::Test::Case`](http://rubydoc.info/gems/cucumber-core/Cucumber/Core/Test/Case), each containing a number of instances of [`Cucumber::Core::Test::Step`](http://rubydoc.info/gems/cucumber-core/Cucumber/Core/Test/Step). It's these that are then filtered and executed. Test cases and their test steps are also immutable value objects. ### Filters Once we have the test cases, and they've been activated by the mappings, you may want to pass them through a filter or two. Filters can be used to do things like activate, sort, replace or remove some of the test cases or their steps before they're executed. ### Report A report is how you find out what is happening during your test run. As the test cases and steps are executed, messages are sent to the report. A report needs to respond to the following methods: * `before_test_case(test_case)` * `after_test_case(test_case, result)` * `before_test_step(test_step)` * `after_test_step(test_test, result)` * `done` That's probably best illustrated with an example. ## Example Here's an example of how you might use [`Cucumber::Core#execute`](http://rubydoc.info/gems/cucumber-core/Cucumber/Core#execute-instance_method) ```ruby require 'cucumber/core' require 'cucumber/core/filter' class MyRunner include Cucumber::Core end class ActivateSteps < Cucumber::Core::Filter.new def test_case(test_case) test_steps = test_case.test_steps.map do |step| activate(step) end test_case.with_steps(test_steps).describe_to(receiver) end private def activate(step) case step.name when /fail/ step.with_action { raise Failure } when /pass/ step.with_action {} else step end end end class Report def before_test_step(test_step) end def after_test_step(test_step, result) puts "#{test_step.name} #{result}" end def before_test_case(test_case) end def after_test_case(test_case, result) end def done end end feature = Cucumber::Core::Gherkin::Document.new(__FILE__, <<-GHERKIN) Feature: Scenario: Given passing And failing And undefined GHERKIN MyRunner.new.execute([feature], Report.new, [ActivateSteps.new]) ``` If you run this little Ruby script, you should see the following output: ``` passing ✓ failing ✗ undefined ? ``` ## Copyright Copyright (c) 2013-2014 Cucumber Limited. cucumber-core-1.1.3/LICENSE0000644000175000017500000000210212515135405014626 0ustar boutilboutilCopyright (C) 2013 The Cucumber Organisation MIT License (Expat) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. cucumber-core-1.1.3/HISTORY.md0000644000175000017500000000474712515135405015325 0ustar boutilboutil## [In Git](https://github.com/cucumber/cucumber-ruby-core/compare/v1.1.3...master) ## [v1.1.3](https://github.com/cucumber/cucumber-ruby-core/compare/v1.1.2...v1.1.2) ### New Features * Added custom `inspect` methods for AST Nodes (@tooky) ## [v1.1.2](https://github.com/cucumber/cucumber-ruby-core/compare/v1.1.1...v1.1.2) ### New Features * Make Test Case names for Scenario Outlines language neutral [83](https://github.com/cucumber/cucumber-ruby-core/pull/83) (@brasmusson) * Add predicate methods for Multline arguments (@mattwynne) * Expose `Test::Case#feature` (@mattwynne) * Fail test case if around hook fails (@mattwynne, @tooky) * Expose `Test::Case#around_hooks` (@tooky) ## [v1.1.1](https://github.com/cucumber/cucumber-ruby-core/compare/v1.1.0...v1.1.1) ### New Features * Calculate actual keyword for snippets (@brasmusson) ### Bugfixes * Remove keyword from `Test::Case#name` [82](https://github.com/cucumber/cucumber-ruby-core/pull/82) (@richarda) ## [v1.1.0](https://github.com/cucumber/cucumber-ruby-core/compare/v1.0.0...v1.1.0) ### New features * LocationsFilter now sorts test cases as well as filtering them (@mattwynne) ## [v1.0.0](https://github.com/cucumber/cucumber-ruby-core/compare/v1.0.0.beta.4...v1.0.0) ### Features Removed * Removed the Mapper DSL (@mattwynne, @tooky) * Removed Cucumber.initializer (@tooky) ### New Features * Added dynamic filter class constructor (@mattwynne) ## [v1.0.0.beta.4](https://github.com/cucumber/cucumber-ruby-core/compare/v1.0.0.beta.3...v1.0.0.beta.4) ### New Features * Introduce a Duration object (#[71](https://github.com/cucumber/cucumber-ruby-core/pull/71) [@brasmusson](https://github.com/brasmusson)) * BeforeStep hooks (#[70](https://github.com/cucumber/cucumber-ruby-core/pull/70) [@almostwhitehat](https://github.com/almostwhitehat)) * Expose `Test::Case#test_steps` (@mattwynne) ### Bugfixes * Handle empty feature files (#[77](https://github.com/cucumber/cucumber-ruby-core/pull/77), [cucumber/cucumber#771](https://github.com/cucumber/cucumber/issues/771) [@brasmusson](https://github.com/brasmusson)) * Run after hooks in reverse order (#[69](https://github.com/cucumber/cucumber-ruby-core/pull/69) [@erran](https://github.com/erran)) ## [v1.0.0.beta.3](https://github.com/cucumber/cucumber-ruby-core/compare/v1.0.0.beta.2...v1.0.0.beta.3) Changes were not logged. ## [v1.0.0.beta.2](https://github.com/cucumber/cucumber-ruby-core/compare/v1.0.0.beta.1...v1.0.0.beta.2) Changes were not logged. cucumber-core-1.1.3/Gemfile0000644000175000017500000000004612515135405015121 0ustar boutilboutilsource "https://rubygems.org" gemspec cucumber-core-1.1.3/CONTRIBUTING.md0000644000175000017500000000045312515135405016061 0ustar boutilboutilRelease Process =============== * Bump the version number in `lib/cucumber/core/version.rb` * Update `HISTORY.md` is updated with the upcoming version number and entries for all changes recorded. * Now release it ``` bundle update bundle exec rake git commit -m "Release X.Y.Z" rake release ``` cucumber-core-1.1.3/.yardopts0000644000175000017500000000010312515135405015466 0ustar boutilboutil--exclude spec --markup markdown --no-private - HISTORY.md LICENSE cucumber-core-1.1.3/.travis.yml0000644000175000017500000000030312515135405015733 0ustar boutilboutilrvm: - 2.2 - 2.1 - 2.0.0 - 1.9.3 - jruby # whitelist branches: only: - master notifications: email: - cukes-devs@googlegroups.com irc: - "irc.freenode.org#cucumber" cucumber-core-1.1.3/.ruby-gemset0000644000175000017500000000001112515135405016062 0ustar boutilboutilcucumber cucumber-core-1.1.3/.rspec0000644000175000017500000000001012515135405014732 0ustar boutilboutil--color cucumber-core-1.1.3/.coveralls.yml0000644000175000017500000000003012515135405016412 0ustar boutilboutilservice_name: travis-ci