|
- require 'rspec/expectations'
- require 'rspec/mocks'
- require 'rake'
- require 'cucumber/rake/task'
-
- class MockKernel
- attr_reader :exit_status
-
- def exit(status)
- @exit_status = status
-
- status unless status.zero?
- end
- end
-
- class CommandLine
- include ::RSpec::Mocks::ExampleMethods
-
- def initialize
- ::RSpec::Mocks.setup
-
- @stdout = StringIO.new
- @stderr = StringIO.new
- @kernel = MockKernel.new
- end
-
- def stderr
- @stderr.string
- end
-
- def stdout
- @stdout.string
- end
-
- def all_output
- [stdout, stderr].reject(&:empty?).join("\n")
- end
-
- def exit_status
- @kernel.exit_status || 0
- end
-
- def capture_stdout
- capture_stream($stdout, @stdout)
- capture_stream($stderr, @stderr)
-
- yield
- end
-
- def destroy_mocks
- ::RSpec::Mocks.verify
- ensure
- ::RSpec::Mocks.teardown
- end
-
- private
-
- def capture_stream(stream, redirect)
- allow(stream)
- .to receive(:puts)
- .and_wrap_original do |_, *args|
- redirect.puts(*args)
- end
-
- allow(stream)
- .to receive(:print)
- .and_wrap_original do |_, *args|
- redirect.print(*args)
- end
-
- allow(stream)
- .to receive(:flush)
- .and_wrap_original do |_, *args|
- redirect.flush(*args)
- end
- end
- end
-
- class CucumberCommand < CommandLine
- attr_reader :args
-
- def execute(args)
- @args = args
- argument_list = make_arg_list(args)
-
- Cucumber::Cli::Main.new(
- argument_list,
- @stdout,
- @stderr,
- @kernel
- ).execute!
- end
-
- private
-
- def make_arg_list(args)
- index = -1
- args.split(/'|"/).map do |chunk|
- index += 1
- index.even? ? chunk.split(' ') : chunk
- end.flatten
- end
- end
-
- class RubyCommand < CommandLine
- def execute(file)
- capture_stdout { require file }
- rescue RuntimeError
- # no-op: this is raised when Cucumber fails
- rescue SystemExit
- # No-op: well, we are supposed to exit the rake task
- rescue StandardError
- @kernel.exit(1)
- end
- end
-
- class RakeCommand < CommandLine
- def execute(task)
- allow_any_instance_of(Cucumber::Rake::Task)
- .to receive(:fork)
- .and_return(false)
-
- allow(Cucumber::Cli::Main)
- .to receive(:execute)
- .and_wrap_original do |_, *args|
- Cucumber::Cli::Main.new(
- args[0],
- @stdout,
- @stderr,
- @kernel
- ).execute!
- end
-
- Rake.with_application do |rake|
- rake.load_rakefile
- capture_stdout { rake[task.strip].invoke }
- end
- rescue RuntimeError
- # no-op: this is raised when Cucumber fails
- rescue SystemExit
- # No-op: well, we are supposed to exit the rake task
- end
- end
-
- module CLIWorld
- def execute_cucumber(args)
- execute_command(CucumberCommand, args)
- end
-
- def execute_extra_cucumber(args)
- execute_extra_command(CucumberCommand, args)
- end
-
- def execute_ruby(filename)
- execute_command(RubyCommand, "#{Dir.pwd}/#{filename}")
- end
-
- def execute_rake(task)
- execute_command(RakeCommand, task)
- end
-
- def execute_command(cls, args)
- @command_line = cls.new
- @command_line.execute(args)
- end
-
- def execute_extra_command(cls, args)
- @extra_commands = []
- @extra_commands << cls.new
- @extra_commands.last.execute(args)
- end
-
- def command_line
- @command_line
- end
-
- def last_extra_command
- @extra_commands&.last
- end
- end
-
- World(CLIWorld)
-
- RSpec::Matchers.define :have_succeded do
- match do |cli|
- @actual = cli.exit_status
- @expected = '0 exit code'
- @actual.zero?
- end
- end
-
- RSpec::Matchers.define :have_failed do
- match do |cli|
- @actual = cli.exit_status
- @expected = 'non-0 exit code'
- @actual.positive?
- end
- end
-
- RSpec::Matchers.define :have_exited_with do |expected|
- match do |cli|
- @actual = cli.exit_status
-
- if expected.is_a?(ExecutionStatus)
- expected.validates?(@actual)
- else
- @actual == expected
- end
- end
- end
|