rainforest_ruby_runtime 0.0.2 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,5 +1,8 @@
1
1
  module RainforestRubyRuntime
2
2
  class DSL
3
+ include RSpec::Matchers
4
+ include Capybara::DSL
5
+
3
6
  def initialize(callback: )
4
7
  @callback = callback
5
8
  end
@@ -8,14 +11,25 @@ module RainforestRubyRuntime
8
11
  RainforestRubyRuntime::Test.new(id: id, title: title, callback: @callback, &block)
9
12
  end
10
13
 
14
+ def step(options, &block)
15
+ RainforestRubyRuntime::Step.new(options.merge(callback: @callback), &block).tap(&:run)
16
+ end
17
+
11
18
  def define_variable_scope(name, &block)
12
19
  scope = Variables::Scope.new(name, &block)
13
- Variables.scope_registery.register(scope)
20
+ Variables.scope_registry.register(scope)
14
21
  end
15
22
 
16
23
  def run_code(code)
17
24
  eval(code)
18
25
  end
26
+
27
+ def method_missing(name, *args, &block)
28
+ if Variables.scope_registry.has_variable?(name)
29
+ Variables.scope_registry[name]
30
+ else
31
+ super
32
+ end
33
+ end
19
34
  end
20
35
  end
21
-
@@ -1,12 +1,15 @@
1
1
  module RainforestRubyRuntime
2
2
  class Runner
3
3
  attr_reader :config_options, :logger
4
+ attr_accessor :browser
4
5
 
5
6
  FAILURE_EXCEPTIONS = [
6
7
  RSpec::Expectations::ExpectationNotMetError,
7
8
  Capybara::ElementNotFound,
8
9
  ].freeze
9
10
 
11
+ BROWSERS = %w(chrome firefox ie edge safari).freeze
12
+
10
13
  def initialize(options = {})
11
14
  @config_options = options.dup.freeze
12
15
  @step_variables = options[:step_variables]
@@ -14,64 +17,55 @@ module RainforestRubyRuntime
14
17
  @logger = options.fetch(:logger) { Logger.new(StringIO.new) }
15
18
  end
16
19
 
17
- def run(code)
18
- logger.debug "Running code:\n#{code}\nDriver: #{driver}"
19
- Capybara.default_driver = :"#{driver}"
20
+ def run(codes)
21
+ logger.debug "Running code:\n#{codes.join('\n')}\nDriver: #{driver_type}"
22
+ Capybara.default_driver = :"#{driver_type}"
20
23
  Capybara.default_max_wait_time = wait_time
21
24
 
22
- apply_config!
23
- setup_scope_registery!
25
+ setup_scope_registry!
24
26
 
25
27
  dsl = RainforestRubyRuntime::DSL.new(callback: @callback)
26
28
 
27
- test = dsl.run_code(code)
28
- if Test === test
29
- test.run
29
+ tests = codes.map { |code| dsl.run_code(code) }
30
+ if tests.all? { |test| test.is_a?(Test) }
31
+ describe = driver_klass.new(config_options).to_rspec(tests)
32
+ run_rspec(describe)
30
33
  else
31
- raise WrongReturnValueError, test
34
+ raise WrongReturnValueError, tests.reject { |test| test.is_a?(Test) }
32
35
  end
33
- test
36
+ tests
34
37
  ensure
35
38
  terminate_session!
36
39
  end
37
40
 
38
- def extract_results(code, fake_session_id: nil)
39
- stdout = stderr = nil
40
- payload = nil
41
- begin
42
- stdout, stderr = capture_output2 do
43
- run(code)
41
+ def run_rspec(describe)
42
+ if ENV['RUNTIME_ENV'] == 'test' && ENV['SHOW_OUTPUT'] != 'true'
43
+ # if we're in tests, don't mix output from here with tests output
44
+ # and don't include this describe block in the test count
45
+ describe.run
46
+ RSpec.world.example_groups.pop
47
+ else
48
+ RSpec.configure do |config|
49
+ config.color = true
50
+ config.formatter = :documentation
51
+ end
52
+ RSpec.configuration.reporter.report(RSpec.world.example_count([describe])) do |reporter|
53
+ describe.run(reporter)
44
54
  end
45
- rescue *FAILURE_EXCEPTIONS => e
46
- payload = exception_to_payload e, status: 'failed'
47
- rescue StandardError => e
48
- payload = exception_to_payload e, status: 'error'
49
- rescue SyntaxError, Exception => e
50
- payload = exception_to_payload e, status: 'fatal_error'
51
55
  end
52
-
53
- payload ||= { status: 'passed' }
54
-
55
- sid = fake_session_id || session_id
56
-
57
- payload = payload.merge({
58
- stdout: stdout,
59
- stderr: stderr,
60
- session_id: sid,
61
- driver: driver,
62
- })
63
-
64
-
65
- logger.debug("Payload")
66
- logger.debug(payload.inspect)
67
-
68
- payload
69
56
  end
70
57
 
71
- def driver
58
+ def driver_type
72
59
  ENV.fetch("CAPYBARA_DRIVER") { "selenium" }
73
60
  end
74
61
 
62
+ def driver_klass
63
+ {
64
+ 'selenium' => Drivers::Selenium,
65
+ 'sauce' => Drivers::Sauce,
66
+ }.fetch(driver_type)
67
+ end
68
+
75
69
  def current_browser
76
70
  current_driver.browser
77
71
  end
@@ -93,17 +87,6 @@ module RainforestRubyRuntime
93
87
  })
94
88
  end
95
89
 
96
- def apply_config!
97
- config = {
98
- "selenium" => Drivers::Selenium,
99
- "sauce" => Drivers::Sauce,
100
- "testingbot" => Drivers::TestingBot,
101
- "browser_stack" => Drivers::BrowserStack,
102
- }.fetch(driver)
103
-
104
- config.new(config_options).call
105
- end
106
-
107
90
  def current_driver
108
91
  Capybara.current_session.driver
109
92
  end
@@ -115,12 +98,12 @@ module RainforestRubyRuntime
115
98
  elsif current_driver.respond_to?(:quit)
116
99
  current_driver.quit
117
100
  else
118
- logger.warn "Cannot terminate session. Driver #{driver}" and return
101
+ logger.warn "Cannot terminate session. Driver #{driver_type}" and return
119
102
  end
120
103
  logger.debug "Session successfuly terminated"
121
104
  rescue Selenium::WebDriver::Error::WebDriverError => e
122
105
  # Ignore
123
- logger.warn "Exception while terminating session. Driver #{driver}. Class: #{e.class}"
106
+ logger.warn "Exception while terminating session. Driver #{driver_type}. Class: #{e.class}"
124
107
  logger.warn "#{e.message}\n#{e.backtrace.join("\n")}"
125
108
  end
126
109
 
@@ -128,23 +111,12 @@ module RainforestRubyRuntime
128
111
  ENV.fetch("CAPYBARA_WAIT_TIME", 20).to_i
129
112
  end
130
113
 
131
- def capture_output2
132
- previous_stdout, $stdout = $stdout, StringIO.new
133
- previous_stderr, $stderr = $stderr, StringIO.new
134
- yield
135
- [$stdout.string, $stderr.string]
136
- ensure
137
- # Restore the previous value of stdout (typically equal to STDERR).
138
- $stdout = previous_stdout
139
- $stderr = previous_stderr
140
- end
141
-
142
- def setup_scope_registery!
114
+ def setup_scope_registry!
143
115
  # TODO this should not be set globally, but passed in the DSL
144
116
  if @step_variables.nil?
145
- Variables.scope_registery = Variables::Registery.new
117
+ Variables.scope_registry = Variables::Registry.new
146
118
  else
147
- Variables.scope_registery = Variables::StaticVariableRegistery.new(@step_variables)
119
+ Variables.scope_registry = Variables::StaticVariableRegistry.new(@step_variables)
148
120
  end
149
121
  end
150
122
  end
@@ -10,28 +10,11 @@ module RainforestRubyRuntime
10
10
  @callback = callback
11
11
  end
12
12
 
13
- def step(options, &block)
14
- step = Step.new(options.merge(callback: @callback), &block)
15
- @steps << step
16
- step.run
17
- step
18
- end
19
-
20
13
  def run
21
- extend RSpec::Matchers
22
- extend Capybara::DSL
23
14
  @callback.before_test(self)
24
- instance_eval &@block
15
+ @block.call
25
16
  @callback.after_test(self)
26
17
  end
27
-
28
- def method_missing(name, *args, &block)
29
- if Variables.scope_registery.has_variable?(name)
30
- Variables.scope_registery[name]
31
- else
32
- super
33
- end
34
- end
35
18
  end
36
19
 
37
20
  class Step
@@ -1,6 +1,6 @@
1
1
  module RainforestRubyRuntime
2
2
  module Variables
3
- class Registery
3
+ class Registry
4
4
  def initialize(variables = {})
5
5
  @variables = {}
6
6
  end
@@ -22,7 +22,7 @@ module RainforestRubyRuntime
22
22
  attr_reader :variables
23
23
  end
24
24
 
25
- class StaticVariableRegistery
25
+ class StaticVariableRegistry
26
26
  def initialize(variables)
27
27
  @variables = variables.inject({}) do |variables, (name, var_and_values)|
28
28
  scope = Scope.new(name)
@@ -47,12 +47,12 @@ module RainforestRubyRuntime
47
47
  end
48
48
  end
49
49
 
50
- def self.scope_registery
51
- @scope_registery ||= Registery.new
50
+ def self.scope_registry
51
+ @scope_registry ||= Registry.new
52
52
  end
53
53
 
54
- def self.scope_registery=(registery)
55
- @scope_registery = registery
54
+ def self.scope_registry=(registry)
55
+ @scope_registry = registry
56
56
  end
57
57
  end
58
58
  end
@@ -5,7 +5,7 @@ module RainforestRubyRuntime
5
5
 
6
6
  def initialize(*, &block)
7
7
  super
8
- @registry = Registery.new
8
+ @registry = Registry.new
9
9
  instance_eval &block if block_given?
10
10
  end
11
11
 
@@ -1,3 +1,3 @@
1
1
  module RainforestRubyRuntime
2
- VERSION = "0.0.2"
2
+ VERSION = "0.1.0"
3
3
  end
@@ -13,19 +13,30 @@ Gem::Specification.new do |spec|
13
13
  spec.homepage = "https://www.rainforestqa.com/"
14
14
  spec.license = "MIT"
15
15
 
16
- spec.files = `git ls-files -z`.split("\x0")
16
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f =~ %r(^vendor/sauce/) }
17
17
  spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
18
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
19
  spec.require_paths = ["lib"]
20
20
 
21
- spec.add_development_dependency "bundler", "~> 1.14"
22
- spec.add_development_dependency "rake"
21
+ spec.post_install_message = <<-EOS
22
+ Thanks for installing!
23
+ If you plan to run scripts against Sauce Labs, please run
24
+ `rainforest_test --prepare-sauce`
25
+ and set the SAUCE_USERNAME, SAUCE_ACCESS_KEY, and CAPYBARA_DRIVER
26
+ environment variables.
27
+ EOS
28
+
29
+ spec.add_development_dependency "pry"
30
+ spec.add_development_dependency "byebug"
31
+ spec.add_development_dependency "awesome_print"
32
+ spec.add_development_dependency "rspec_junit_formatter"
23
33
  spec.add_dependency "sauce", "~> 3.7"
24
34
  spec.add_dependency "sauce-connect", "~> 3.6"
25
- spec.add_dependency "rspec-expectations", "~> 3.0"
35
+ spec.add_dependency "rspec", "3.5.0"
26
36
  spec.add_dependency "capybara", "~> 2.7.1"
27
37
  spec.add_dependency "selenium-client", "~> 1.2.0"
28
38
  spec.add_dependency "selenium-webdriver", "~> 3.3"
29
39
  spec.add_dependency "net-http-persistent", "~> 3.0"
30
- spec.add_dependency "testingbot", "~> 0.1.7"
40
+ spec.add_dependency "bundler", "~> 1.16"
41
+ spec.add_dependency "rake", "~> 12.3"
31
42
  end
@@ -1,2 +1,2 @@
1
- test(id: 1, title: "") do
1
+ test(id: 1, title: "empty") do
2
2
  end
@@ -0,0 +1,89 @@
1
+ require 'open3'
2
+ require_relative './spec_helper'
3
+
4
+ module RainforestRubyRuntime
5
+ describe 'rainforest_test', show_output: true do
6
+ subject do
7
+ if browsers
8
+ Open3.capture2e("ruby bin/rainforest_test #{browsers} sample_tests/empty.rb")
9
+ else
10
+ Open3.capture2e('ruby bin/rainforest_test sample_tests/empty.rb')
11
+ end
12
+ end
13
+
14
+ context 'with no browsers' do
15
+ let(:browsers) { nil }
16
+
17
+ it 'defaults to chrome' do
18
+ output, status = subject
19
+
20
+ expect(status).to be_success
21
+ expect(output).to match(/chrome/)
22
+ end
23
+ end
24
+
25
+ context 'with a browser' do
26
+ context 'from the whitelist' do
27
+ let(:browser) { Runner::BROWSERS.sample }
28
+ let(:browsers) { "--browsers #{browser}" }
29
+
30
+ it 'passes it to Runner' do
31
+ output, status = subject
32
+
33
+ expect(status).to be_success
34
+ expect(output).to match(browser)
35
+ end
36
+ end
37
+
38
+ context 'that is unknown' do
39
+ let(:browsers) { "--browsers opera" }
40
+
41
+ it 'exits with status 1' do
42
+ output, status = subject
43
+
44
+ expect(status).to_not be_success
45
+ expect(output).to match(/invalid option: #{browsers}/)
46
+ end
47
+ end
48
+ end
49
+
50
+ context 'with multiple browsers' do
51
+ context 'from the whitelist' do
52
+ let(:browser_values) { Runner::BROWSERS.sample(2) }
53
+ let(:browsers) { "--browsers #{browser_values.join(',')}" }
54
+
55
+ it 'passes them to Runner' do
56
+ output, status = subject
57
+
58
+ expect(status).to be_success
59
+ browser_values.each { |b| expect(output).to match(b) }
60
+ end
61
+ end
62
+
63
+ context 'that is unknown' do
64
+ let(:browsers) { "--browsers #{Runner::BROWSERS.sample},opera" }
65
+
66
+ it 'exits with status 1' do
67
+ output, status = subject
68
+
69
+ expect(status).to_not be_success
70
+ expect(output).to match(/invalid option: --browsers opera/)
71
+ end
72
+ end
73
+ end
74
+
75
+ context 'with multiple files' do
76
+ subject do
77
+ Open3.capture2e('ruby bin/rainforest_test sample_tests/empty.rb sample_tests/simple.rb')
78
+ end
79
+
80
+ it 'should work' do
81
+ output, status = subject
82
+
83
+ expect(status).to be_success
84
+ expect(output).to match(/\[1\] empty/)
85
+ expect(output).to match(/\[1\] My Sample Test/)
86
+ end
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,132 @@
1
+ require_relative './spec_helper'
2
+
3
+ module RainforestRubyRuntime
4
+ describe Runner do
5
+ let(:codes) { [read_sample('empty')] }
6
+
7
+ subject { Runner.new(browsers: %i(chrome)) }
8
+
9
+ before do
10
+ allow(subject).to receive(:driver).and_return('selenium')
11
+ end
12
+
13
+ describe "#session_id" do
14
+ before { allow(subject).to receive(:current_driver) { OpenStruct.new(browser: OpenStruct.new(session_id: 'session')) } }
15
+
16
+ it "returns a session id" do
17
+ expect(subject.session_id).to eq('session')
18
+ end
19
+ end
20
+
21
+ describe "#run" do
22
+ describe "with a limited browser set" do
23
+ before do
24
+ allow(subject).to receive(:driver_type).and_return('sauce')
25
+ expect_any_instance_of(Drivers::Sauce).to receive(:to_rspec) do |driver|
26
+ driver.send(:apply_config)
27
+ double(run: nil)
28
+ end
29
+ end
30
+
31
+ it "applies the correct configuration" do
32
+ subject.run(codes)
33
+ expect(Sauce.get_config.browsers).to eq([['Windows 7', 'Chrome', 'latest']])
34
+ end
35
+ end
36
+
37
+ describe "for a rainforest test" do
38
+ let(:codes) { [read_sample('simple')] }
39
+
40
+ it "runs the content of the step" do
41
+ tests = subject.run(codes)
42
+ expect(tests).to all be_instance_of(Test)
43
+ expect($simple_test_was).to eq(:run)
44
+ end
45
+ end
46
+
47
+ describe "for another type of test" do
48
+ it "raises an exception" do
49
+ expect { subject.run(['']) }.to raise_error(WrongReturnValueError)
50
+ end
51
+ end
52
+
53
+ describe "with custom step variables" do
54
+ let(:codes) { [read_sample('step_variables')] }
55
+
56
+ it "makes the variable accessible in the test" do
57
+ subject.run(codes)
58
+ expect($step_variable_1_was).to_not be_nil
59
+ expect($step_variable_2_was).to match(/time is: .*/)
60
+ expect($step_variable_3_was).to eq('1')
61
+ end
62
+ end
63
+ end
64
+
65
+ describe "callbacks" do
66
+ let(:callback) do
67
+ Class.new do
68
+ attr_reader :before_steps, :before_tests, :after_steps, :after_tests
69
+
70
+ def initialize
71
+ @before_steps = []
72
+ @after_steps = []
73
+ @after_tests = []
74
+ @before_tests = []
75
+ end
76
+
77
+ def before_step(step)
78
+ @before_steps << step
79
+ end
80
+
81
+ def after_step(step)
82
+ @after_steps << step
83
+ end
84
+
85
+ def after_test(test)
86
+ @after_tests << test
87
+ end
88
+
89
+ def before_test(test)
90
+ @before_tests << test
91
+ end
92
+ end.new
93
+ end
94
+
95
+ let(:codes) { [read_sample('two_steps')] }
96
+
97
+ subject { Runner.new(browsers: %i(chrome), callback: callback) }
98
+
99
+ it "calls the right method on the callback object" do
100
+ subject.run(codes)
101
+ expect(callback.before_tests.size).to eq(1)
102
+ expect(callback.after_tests.size).to eq(1)
103
+ expect(callback.before_steps.size).to eq(2)
104
+ expect(callback.after_steps.size).to eq(2)
105
+
106
+ expect(callback.before_steps.map(&:id)).to eq([1, 2])
107
+ expect(callback.after_steps.map(&:id)).to eq([1, 2])
108
+
109
+ expect(callback.before_steps.map(&:action)).to eq(["action 1", "action 2"])
110
+ end
111
+
112
+ describe "a partially define callback object" do
113
+ let(:callback) do
114
+ Class.new do
115
+ end.new
116
+ end
117
+
118
+ it "should not raise a method missing exception" do
119
+ subject.run(codes)
120
+ end
121
+ end
122
+ end
123
+
124
+ def read_sample(name)
125
+ File.read(File.expand_path("../sample_tests/#{name}.rb", __dir__))
126
+ end
127
+
128
+ def format_step(code)
129
+ read_sample("one_step").gsub("#REPLACE", code)
130
+ end
131
+ end
132
+ end