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.
- checksums.yaml +4 -4
- data/.circleci/config.yml +35 -0
- data/.gitignore +1 -0
- data/.ruby-version +1 -1
- data/CHANGELOG.md +10 -0
- data/Gemfile +1 -6
- data/README.md +142 -2
- data/Rakefile +26 -5
- data/bin/rainforest_test +47 -2
- data/lib/rainforest_ruby_runtime.rb +4 -5
- data/lib/rainforest_ruby_runtime/drivers/sauce.rb +22 -11
- data/lib/rainforest_ruby_runtime/drivers/selenium.rb +34 -4
- data/lib/rainforest_ruby_runtime/dsl.rb +16 -2
- data/lib/rainforest_ruby_runtime/runner.rb +39 -67
- data/lib/rainforest_ruby_runtime/test.rb +1 -18
- data/lib/rainforest_ruby_runtime/variables/{registery.rb → registry.rb} +6 -6
- data/lib/rainforest_ruby_runtime/variables/scope.rb +1 -1
- data/lib/rainforest_ruby_runtime/version.rb +1 -1
- data/rainforest_ruby_runtime.gemspec +16 -5
- data/sample_tests/empty.rb +1 -1
- data/spec/rainforest_test_spec.rb +89 -0
- data/spec/runner_spec.rb +132 -0
- data/spec/spec_helper.rb +20 -0
- metadata +74 -26
- data/.travis.yml +0 -12
- data/lib/rainforest_ruby_runtime/drivers/browser_stack.rb +0 -42
- data/lib/rainforest_ruby_runtime/drivers/testing_bot.rb +0 -34
- data/test/runner_test.rb +0 -212
- data/test/test_helper.rb +0 -8
@@ -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.
|
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(
|
18
|
-
logger.debug "Running code:\n#{
|
19
|
-
Capybara.default_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
|
-
|
23
|
-
setup_scope_registery!
|
25
|
+
setup_scope_registry!
|
24
26
|
|
25
27
|
dsl = RainforestRubyRuntime::DSL.new(callback: @callback)
|
26
28
|
|
27
|
-
|
28
|
-
if
|
29
|
-
|
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
|
-
|
36
|
+
tests
|
34
37
|
ensure
|
35
38
|
terminate_session!
|
36
39
|
end
|
37
40
|
|
38
|
-
def
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
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
|
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 #{
|
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 #{
|
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
|
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.
|
117
|
+
Variables.scope_registry = Variables::Registry.new
|
146
118
|
else
|
147
|
-
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
|
-
|
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
|
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
|
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.
|
51
|
-
@
|
50
|
+
def self.scope_registry
|
51
|
+
@scope_registry ||= Registry.new
|
52
52
|
end
|
53
53
|
|
54
|
-
def self.
|
55
|
-
@
|
54
|
+
def self.scope_registry=(registry)
|
55
|
+
@scope_registry = registry
|
56
56
|
end
|
57
57
|
end
|
58
58
|
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.
|
22
|
-
|
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
|
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 "
|
40
|
+
spec.add_dependency "bundler", "~> 1.16"
|
41
|
+
spec.add_dependency "rake", "~> 12.3"
|
31
42
|
end
|
data/sample_tests/empty.rb
CHANGED
@@ -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
|
data/spec/runner_spec.rb
ADDED
@@ -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
|