testeranto.rubeno 0.1.4

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 5ab099109b1366b88ab8e8a7a5fbf951ade16dfd50b34f0020fcc892c545f745
4
+ data.tar.gz: 6b3cbdcf603218bbbb778b42117065aac703fc94c91da7dc522ce4647e9e4871
5
+ SHA512:
6
+ metadata.gz: b7aa0c61d50e39e90ec39eb4ed1e923cfffdd4a3a5168408f0185d8363869fe4cc15a00ef8590cf4b670c9066b71dd1993812ae38afb4e14afde99ce0161eb1e
7
+ data.tar.gz: 572789fa5f684ffdb6df784c6c21ed42097bcf351f82f23ee6bee2aa4b0eb29644220a2f83197f36a9e7496feae7a030ca7c7e5196f62ed8e8a9eebc0ed505cf
data/README.md ADDED
@@ -0,0 +1,122 @@
1
+ # Rubeno
2
+
3
+ The Ruby implementation of Testeranto.
4
+
5
+ ## Deployment to rubygems
6
+ 1) update the version in rubeno.gemspec
7
+ 2) `gem build rubeno.gemspec`
8
+
9
+ ## Overview
10
+
11
+ Rubeno is a Ruby implementation of the Testeranto BDD testing framework. It follows the same patterns as the other language implementations (TypeScript, Python, Go) to provide a consistent testing experience across multiple programming languages.
12
+
13
+ ## Structure
14
+
15
+ The implementation consists of:
16
+
17
+ 1. **Base Classes**: `BaseSuite`, `BaseGiven`, `BaseWhen`, `BaseThen` - Core BDD components
18
+ 2. **Main Class**: `Rubeno` - Orchestrates test execution
19
+ 3. **Test Adapter**: `SimpleTestAdapter` - Default adapter implementation
20
+ 4. **Process Manager**: `PM_Ruby` - Handles communication
21
+ 5. **Types**: Type definitions for the framework
22
+
23
+ ## Usage
24
+
25
+ ### Basic Example
26
+
27
+ ```ruby
28
+ require 'rubeno'
29
+
30
+ # Define test implementation
31
+ test_implementation = Rubeno::ITestImplementation.new(
32
+ suites: { 'Default' => ->(name, givens) { ... } },
33
+ givens: { 'basic' => ->(initial_values) { ... } },
34
+ whens: { 'press' => ->(button) { ->(store) { ... } } },
35
+ thens: { 'displayIs' => ->(expected) { ->(store) { ... } } }
36
+ )
37
+
38
+ # Define test specification
39
+ test_specification = ->(suites, givens, whens, thens) do
40
+ [
41
+ suites.Default('Test Suite', {
42
+ 'test1' => givens.basic([], [whens.press('1')], [thens.displayIs('1')], nil)
43
+ })
44
+ ]
45
+ end
46
+
47
+ # Create test instance
48
+ test_instance = Rubeno.Rubeno(
49
+ nil,
50
+ test_specification,
51
+ test_implementation,
52
+ Rubeno::SimpleTestAdapter.new,
53
+ Rubeno::ITTestResourceRequest.new(ports: 1)
54
+ )
55
+
56
+ # Set as default and run
57
+ Rubeno.set_default_instance(test_instance)
58
+ Rubeno.main
59
+ ```
60
+
61
+ ### Running Tests
62
+
63
+ To run tests with Rubeno:
64
+
65
+ ```bash
66
+ ruby example/Calculator-test.rb '{"name":"test","fs":".","ports":[]}'
67
+ ```
68
+
69
+ ## Integration with Testeranto
70
+
71
+ Rubeno follows the same patterns as other Testeranto implementations:
72
+
73
+ 1. **Test Resource Configuration**: Passed as a JSON string argument
74
+ 2. **Results Output**: Writes to `testeranto/reports/example/ruby.Calculator.test.ts.json`
75
+ 3. **WebSocket Communication**: Supports communication via WebSocket (when configured)
76
+ 4. **Artifact Generation**: Supports test artifacts and reporting
77
+
78
+ ## Docker Support
79
+
80
+ Rubeno includes a Dockerfile for running tests in containers:
81
+
82
+ ```dockerfile
83
+ FROM ruby:3.2-alpine
84
+ WORKDIR /workspace
85
+ # ... (see testeranto/runtimes/ruby/ruby.Dockerfile)
86
+ ```
87
+
88
+ ## Extending
89
+
90
+ To create custom adapters, implement the `ITestAdapter` module:
91
+
92
+ ```ruby
93
+ class CustomAdapter
94
+ include Rubeno::ITestAdapter
95
+
96
+ def before_all(input_val, tr, pm)
97
+ # Custom setup
98
+ input_val
99
+ end
100
+
101
+ # ... implement other methods
102
+ end
103
+ ```
104
+
105
+ ## Dependencies
106
+
107
+ - Ruby 2.7+ (for pattern matching and other modern features)
108
+ - JSON (standard library)
109
+
110
+ ## Future Enhancements
111
+
112
+ 1. **WebSocket Support**: Full WebSocket communication for real-time test reporting
113
+ 2. **Advanced Adapters**: More sophisticated adapter implementations
114
+ 3. **Plugin System**: Extensible plugin architecture
115
+ 4. **Performance Optimizations**: Improved test execution performance
116
+
117
+ ## See Also
118
+
119
+ - [Tiposkripto](../tiposkripto/) - TypeScript/JavaScript implementation
120
+ - [Pitono](../pitono/) - Python implementation
121
+ - [Golingvu](../golingvu/) - Go implementation
122
+
data/Rakefile ADDED
@@ -0,0 +1,8 @@
1
+ require 'rake/testtask'
2
+
3
+ Rake::TestTask.new do |t|
4
+ t.libs << 'test'
5
+ end
6
+
7
+ desc "Run tests"
8
+ task :default => :test
data/bin/rubeno ADDED
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'rubeno'
4
+
5
+ Rubeno.main
data/lib/base_given.rb ADDED
@@ -0,0 +1,137 @@
1
+ module Rubeno
2
+ class BaseGiven
3
+ attr_accessor :features, :whens, :thens, :error, :fail, :store, :recommended_fs_path,
4
+ :given_cb, :initial_values, :key, :failed, :artifacts, :status, :fails
5
+
6
+ def initialize(features, whens, thens, given_cb, initial_values, adapter = nil)
7
+ @features = features
8
+ @whens = whens
9
+ @thens = thens
10
+ @given_cb = given_cb
11
+ @initial_values = initial_values
12
+ @adapter = adapter
13
+ @artifacts = []
14
+ @fails = 0
15
+ end
16
+
17
+ def add_artifact(path)
18
+ normalized_path = path.gsub('\\', '/')
19
+ @artifacts << normalized_path
20
+ end
21
+
22
+ def before_all(store)
23
+ store
24
+ end
25
+
26
+ def to_obj
27
+ {
28
+ key: @key,
29
+ whens: (@whens || []).map { |w| w.respond_to?(:to_obj) ? w.to_obj : {} },
30
+ thens: (@thens || []).map { |t| t.respond_to?(:to_obj) ? t.to_obj : {} },
31
+ error: @error ? [@error, @error.backtrace] : nil,
32
+ failed: @failed,
33
+ features: @features || [],
34
+ artifacts: @artifacts,
35
+ status: @status
36
+ }
37
+ end
38
+
39
+ def given_that(subject, test_resource_configuration, artifactory, given_cb, initial_values, pm)
40
+ # Use the adapter's before_each method if available
41
+ if @adapter
42
+ @adapter.before_each(subject, given_cb, test_resource_configuration, initial_values, pm)
43
+ else
44
+ # Fallback to just returning the subject
45
+ subject
46
+ end
47
+ end
48
+
49
+ def after_each(store, key, artifactory, pm)
50
+ store
51
+ end
52
+
53
+ def give(subject, key, test_resource_configuration, tester, artifactory, t_log, pm, suite_ndx)
54
+ @key = key
55
+ @fails = 0
56
+ @failed = false
57
+ @error = nil
58
+
59
+ given_artifactory = ->(f_path, value) do
60
+ artifactory.call("given-#{key}/#{f_path}", value)
61
+ end
62
+
63
+ begin
64
+ # Call the given callback to get initial subject
65
+ initial_subject = @given_cb.call(@initial_values)
66
+ @store = given_that(
67
+ initial_subject,
68
+ test_resource_configuration,
69
+ given_artifactory,
70
+ @given_cb,
71
+ @initial_values,
72
+ pm
73
+ )
74
+ @status = true
75
+ rescue => e
76
+ @status = false
77
+ @failed = true
78
+ @fails += 1
79
+ @error = e
80
+ return @store
81
+ end
82
+
83
+ begin
84
+ # Process whens
85
+ @whens.each_with_index do |when_step, when_ndx|
86
+ begin
87
+ @store = when_step.test(
88
+ @store,
89
+ test_resource_configuration,
90
+ t_log,
91
+ pm,
92
+ "suite-#{suite_ndx}/given-#{key}/when/#{when_ndx}"
93
+ )
94
+ rescue => e
95
+ @failed = true
96
+ @fails += 1
97
+ @error = e
98
+ end
99
+ end
100
+
101
+ # Process thens
102
+ @thens.each_with_index do |then_step, then_ndx|
103
+ begin
104
+ result = then_step.test(
105
+ @store,
106
+ test_resource_configuration,
107
+ t_log,
108
+ pm,
109
+ "suite-#{suite_ndx}/given-#{key}/then-#{then_ndx}"
110
+ )
111
+ unless tester.call(result)
112
+ @failed = true
113
+ @fails += 1
114
+ end
115
+ rescue => e
116
+ @failed = true
117
+ @fails += 1
118
+ @error = e
119
+ end
120
+ end
121
+ rescue => e
122
+ @error = e
123
+ @failed = true
124
+ @fails += 1
125
+ ensure
126
+ begin
127
+ after_each(@store, @key, given_artifactory, pm)
128
+ rescue => e
129
+ @failed = true
130
+ @fails += 1
131
+ end
132
+ end
133
+
134
+ @store
135
+ end
136
+ end
137
+ end
data/lib/base_suite.rb ADDED
@@ -0,0 +1,94 @@
1
+ module Rubeno
2
+ class BaseSuite
3
+ attr_accessor :name, :givens, :store, :test_resource_configuration, :index, :failed, :fails, :artifacts
4
+
5
+ def initialize(name, givens = {})
6
+ @name = name
7
+ @givens = givens
8
+ @artifacts = []
9
+ @failed = false
10
+ @fails = 0
11
+ end
12
+
13
+ def add_artifact(path)
14
+ normalized_path = path.gsub('\\', '/')
15
+ @artifacts << normalized_path
16
+ end
17
+
18
+ def features
19
+ features = []
20
+ seen = {}
21
+ @givens.each_value do |given|
22
+ given.features.each do |feature|
23
+ unless seen[feature]
24
+ features << feature
25
+ seen[feature] = true
26
+ end
27
+ end
28
+ end
29
+ features
30
+ end
31
+
32
+ def to_obj
33
+ {
34
+ name: @name,
35
+ givens: @givens.values.map(&:to_obj),
36
+ fails: @fails,
37
+ failed: @failed,
38
+ features: features,
39
+ artifacts: @artifacts
40
+ }
41
+ end
42
+
43
+ def setup(s, artifactory, tr, pm)
44
+ s
45
+ end
46
+
47
+ def assert_that(t)
48
+ !!t
49
+ end
50
+
51
+ def after_all(store, artifactory, pm)
52
+ store
53
+ end
54
+
55
+ def run(input_val, test_resource_configuration, artifactory, t_log, pm)
56
+ @test_resource_configuration = test_resource_configuration
57
+
58
+ subject = setup(input_val, artifactory, test_resource_configuration, pm)
59
+
60
+ @fails = 0
61
+ @failed = false
62
+
63
+ @givens.each do |g_key, g|
64
+ begin
65
+ @store = g.give(
66
+ subject,
67
+ g_key,
68
+ test_resource_configuration,
69
+ method(:assert_that),
70
+ artifactory,
71
+ t_log,
72
+ pm,
73
+ @index
74
+ )
75
+ @fails += g.fails if g.fails && g.fails > 0
76
+ rescue => e
77
+ @failed = true
78
+ @fails += 1
79
+ puts "Error in given #{g_key}: #{e.message}"
80
+ end
81
+ end
82
+
83
+ @failed = true if @fails > 0
84
+
85
+ begin
86
+ after_all(@store, artifactory, pm)
87
+ rescue => e
88
+ puts "Error in after_all: #{e.message}"
89
+ end
90
+
91
+ self
92
+ end
93
+ end
94
+ end
data/lib/base_then.rb ADDED
@@ -0,0 +1,44 @@
1
+ module Rubeno
2
+ class BaseThen
3
+ attr_accessor :name, :then_cb, :error, :artifacts, :status
4
+
5
+ def initialize(name, then_cb)
6
+ @name = name
7
+ @then_cb = then_cb
8
+ @error = false
9
+ @artifacts = []
10
+ end
11
+
12
+ def add_artifact(path)
13
+ normalized_path = path.gsub('\\', '/')
14
+ @artifacts << normalized_path
15
+ end
16
+
17
+ def but_then(store, then_cb, test_resource_configuration, pm)
18
+ # Call the then_cb directly
19
+ then_cb.call(store)
20
+ end
21
+
22
+ def to_obj
23
+ {
24
+ name: @name,
25
+ error: @error,
26
+ artifacts: @artifacts,
27
+ status: @status
28
+ }
29
+ end
30
+
31
+ def test(store, test_resource_configuration, t_log, pm, filepath)
32
+ begin
33
+ # Call the then_cb with the store to get a boolean result
34
+ result = @then_cb.call(store)
35
+ @status = true
36
+ result
37
+ rescue => e
38
+ @status = false
39
+ @error = true
40
+ raise e
41
+ end
42
+ end
43
+ end
44
+ end
data/lib/base_when.rb ADDED
@@ -0,0 +1,47 @@
1
+ module Rubeno
2
+ class BaseWhen
3
+ attr_accessor :name, :when_cb, :error, :artifacts, :status
4
+
5
+ def initialize(name, when_cb)
6
+ @name = name
7
+ @when_cb = when_cb
8
+ @artifacts = []
9
+ end
10
+
11
+ def add_artifact(path)
12
+ normalized_path = path.gsub('\\', '/')
13
+ @artifacts << normalized_path
14
+ end
15
+
16
+ def and_when(store, when_cb, test_resource_configuration, pm)
17
+ # Call the when_cb directly
18
+ when_cb.call(store)
19
+ end
20
+
21
+ def to_obj
22
+ error_str = nil
23
+ if @error
24
+ error_str = "#{@error.class}: #{@error.message}"
25
+ end
26
+ {
27
+ name: @name,
28
+ status: @status,
29
+ error: error_str,
30
+ artifacts: @artifacts
31
+ }
32
+ end
33
+
34
+ def test(store, test_resource_configuration, t_log, pm, filepath)
35
+ begin
36
+ # Call the when_cb with the store
37
+ result = @when_cb.call(store)
38
+ @status = true
39
+ result
40
+ rescue => e
41
+ @status = false
42
+ @error = e
43
+ raise e
44
+ end
45
+ end
46
+ end
47
+ end
data/lib/pm/ruby.rb ADDED
@@ -0,0 +1,156 @@
1
+ module Rubeno
2
+ class PM_Ruby
3
+ def initialize(t, websocket_port)
4
+ @test_resource_configuration = t
5
+ @websocket_port = websocket_port
6
+ @connected = false
7
+ end
8
+
9
+ def start
10
+ raise "DEPRECATED"
11
+ end
12
+
13
+ def send(command, *args)
14
+ # For now, return default values to allow tests to run
15
+ case command
16
+ when "pages"
17
+ []
18
+ when "newPage"
19
+ "mock-page"
20
+ when "page"
21
+ "mock-page"
22
+ when "existsSync"
23
+ false
24
+ when "writeFileSync"
25
+ true
26
+ when "createWriteStream"
27
+ "mock-stream"
28
+ when "write"
29
+ true
30
+ when "end"
31
+ true
32
+ when "customclose"
33
+ nil
34
+ else
35
+ nil
36
+ end
37
+ end
38
+
39
+ def pages
40
+ send("pages")
41
+ end
42
+
43
+ def wait_for_selector(p, s)
44
+ send("waitForSelector", p, s)
45
+ end
46
+
47
+ def close_page(p)
48
+ send("closePage", p)
49
+ end
50
+
51
+ def goto(page, url)
52
+ send("goto", page, url)
53
+ end
54
+
55
+ def new_page
56
+ send("newPage")
57
+ end
58
+
59
+ def call(selector, page)
60
+ send("$", selector, page)
61
+ end
62
+
63
+ def is_disabled(selector)
64
+ send("isDisabled", selector)
65
+ end
66
+
67
+ def get_attribute(selector, attribute, p)
68
+ send("getAttribute", selector, attribute, p)
69
+ end
70
+
71
+ def get_inner_html(selector, p)
72
+ send("getInnerHtml", selector, p)
73
+ end
74
+
75
+ def focus_on(selector)
76
+ send("focusOn", selector)
77
+ end
78
+
79
+ def type_into(selector)
80
+ send("typeInto", selector)
81
+ end
82
+
83
+ def page
84
+ send("page")
85
+ end
86
+
87
+ def click(selector)
88
+ send("click", selector)
89
+ end
90
+
91
+ def screencast(opts, page)
92
+ adjusted_opts = opts.dup
93
+ if adjusted_opts['path']
94
+ adjusted_opts['path'] = "#{@test_resource_configuration.fs}/#{opts['path']}"
95
+ end
96
+ send("screencast", adjusted_opts, page, @test_resource_configuration.name)
97
+ end
98
+
99
+ def screencast_stop(p)
100
+ send("screencastStop", p)
101
+ end
102
+
103
+ def custom_screenshot(x)
104
+ opts = x[0]
105
+ page = x[1] if x.length > 1
106
+
107
+ adjusted_opts = opts.dup
108
+ if adjusted_opts['path']
109
+ adjusted_opts['path'] = "#{@test_resource_configuration.fs}/#{opts['path']}"
110
+ end
111
+
112
+ if page
113
+ send("customScreenShot", adjusted_opts, @test_resource_configuration.name, page)
114
+ else
115
+ send("customScreenShot", adjusted_opts, @test_resource_configuration.name)
116
+ end
117
+ end
118
+
119
+ def exists_sync(dest_folder)
120
+ path = "#{@test_resource_configuration.fs}/#{dest_folder}"
121
+ send("existsSync", path)
122
+ end
123
+
124
+ def mkdir_sync
125
+ path = "#{@test_resource_configuration.fs}/"
126
+ send("mkdirSync", path)
127
+ end
128
+
129
+ def write(uid, contents)
130
+ send("write", uid, contents)
131
+ end
132
+
133
+ def write_file_sync(filepath, contents)
134
+ full_path = "#{@test_resource_configuration.fs}/#{filepath}"
135
+ begin
136
+ result = send("writeFileSync", full_path, contents, @test_resource_configuration.name)
137
+ result
138
+ rescue => e
139
+ raise e
140
+ end
141
+ end
142
+
143
+ def create_write_stream(filepath)
144
+ full_path = "#{@test_resource_configuration.fs}/#{filepath}"
145
+ send("createWriteStream", full_path, @test_resource_configuration.name)
146
+ end
147
+
148
+ def end(uid)
149
+ send("end", uid)
150
+ end
151
+
152
+ def custom_close
153
+ send("customclose", @test_resource_configuration.fs, @test_resource_configuration.name)
154
+ end
155
+ end
156
+ end
data/lib/rubeno.rb ADDED
@@ -0,0 +1,379 @@
1
+ require 'json'
2
+ require 'base_suite'
3
+ require 'base_given'
4
+ require 'base_when'
5
+ require 'base_then'
6
+ require 'simple_adapter'
7
+ require 'pm/ruby'
8
+ require 'types'
9
+
10
+ module Rubeno
11
+ class Rubeno
12
+ attr_accessor :test_resource_requirement, :artifacts, :test_jobs, :test_specification,
13
+ :suites_overrides, :given_overrides, :when_overrides, :then_overrides,
14
+ :puppet_master, :specs, :total_tests, :assert_this, :test_adapter,
15
+ :test_subject
16
+
17
+ def initialize(input_val, test_specification, test_implementation, test_resource_requirement, test_adapter, uber_catcher = nil)
18
+ @test_resource_requirement = test_resource_requirement
19
+ @artifacts = []
20
+ @test_jobs = []
21
+ @test_specification = test_specification
22
+ @suites_overrides = {}
23
+ @given_overrides = {}
24
+ @when_overrides = {}
25
+ @then_overrides = {}
26
+ @test_subject = input_val
27
+ @test_adapter = test_adapter
28
+
29
+ # Initialize classy implementations
30
+ initialize_classy_implementations(test_implementation)
31
+
32
+ # Generate specs
33
+ @specs = test_specification.call(
34
+ suites_wrapper,
35
+ given_wrapper,
36
+ when_wrapper,
37
+ then_wrapper
38
+ )
39
+
40
+ # Initialize test jobs
41
+ initialize_test_jobs
42
+ end
43
+
44
+ def suites_wrapper
45
+ # Return an object that responds to method calls for suite types
46
+ wrapper = Object.new
47
+ @suites_overrides.each do |suite_name, suite_proc|
48
+ wrapper.define_singleton_method(suite_name) do |description, givens_dict|
49
+ {
50
+ 'name' => description,
51
+ 'givens' => givens_dict
52
+ }
53
+ end
54
+ end
55
+ wrapper
56
+ end
57
+
58
+ def given_wrapper
59
+ wrapper = Object.new
60
+ @given_overrides.each do |given_name, given_proc|
61
+ wrapper.define_singleton_method(given_name) do |features, whens, thens, initial_values = nil|
62
+ given_proc.call(features, whens, thens, initial_values)
63
+ end
64
+ end
65
+ wrapper
66
+ end
67
+
68
+ def when_wrapper
69
+ wrapper = Object.new
70
+ @when_overrides.each do |when_name, when_proc|
71
+ wrapper.define_singleton_method(when_name) do |*args|
72
+ when_proc.call(*args)
73
+ end
74
+ end
75
+ wrapper
76
+ end
77
+
78
+ def then_wrapper
79
+ wrapper = Object.new
80
+ @then_overrides.each do |then_name, then_proc|
81
+ wrapper.define_singleton_method(then_name) do |*args|
82
+ then_proc.call(*args)
83
+ end
84
+ end
85
+ wrapper
86
+ end
87
+
88
+ def initialize_classy_implementations(test_implementation)
89
+ # Create classy suites
90
+ test_implementation.suites.each do |key, suite_data|
91
+ @suites_overrides[key] = ->(description, givens_dict) do
92
+ {
93
+ 'name' => description,
94
+ 'givens' => givens_dict
95
+ }
96
+ end
97
+ end
98
+
99
+ # Create classy givens
100
+ test_implementation.givens.each do |key, given_cb|
101
+ @given_overrides[key] = ->(features, whens, thens, initial_values = nil) do
102
+ BaseGiven.new(features, whens, thens, given_cb, initial_values, @test_adapter)
103
+ end
104
+ end
105
+
106
+ # Create classy whens
107
+ test_implementation.whens.each do |key, when_cb_proc|
108
+ @when_overrides[key] = ->(*args) do
109
+ when_cb = when_cb_proc.call(*args)
110
+ BaseWhen.new(key, when_cb)
111
+ end
112
+ end
113
+
114
+ # Create classy thens
115
+ test_implementation.thens.each do |key, then_cb_proc|
116
+ @then_overrides[key] = ->(*args) do
117
+ then_cb = then_cb_proc.call(*args)
118
+ BaseThen.new(key, then_cb)
119
+ end
120
+ end
121
+ end
122
+
123
+ def initialize_test_jobs
124
+ @test_jobs = []
125
+ @specs.each_with_index do |suite_spec, i|
126
+ # Create a BaseSuite instance
127
+ suite = BaseSuite.new(suite_spec['name'], suite_spec['givens'])
128
+ suite.index = i
129
+
130
+ # Create a test job
131
+ test_job = {
132
+ suite: suite,
133
+ receiveTestResourceConfig: ->(pm, test_resource_config) { run_test_job(suite, pm, test_resource_config) },
134
+ to_obj: -> { suite.to_obj }
135
+ }
136
+
137
+ @test_jobs << test_job
138
+ end
139
+ end
140
+
141
+ def run_test_job(suite, pm, test_resource_config)
142
+ t_log = ->(*args) { puts args.join(' ') }
143
+
144
+ # Run the suite
145
+ suite_done = suite.run(
146
+ @test_subject,
147
+ test_resource_config, # Use the actual test resource configuration
148
+ ->(f_path, value) { nil }, # Simple artifactory
149
+ t_log,
150
+ pm
151
+ )
152
+
153
+ # Create result object
154
+ result = Object.new
155
+ result.instance_variable_set(:@fails, suite_done.fails)
156
+ result.instance_variable_set(:@artifacts, suite_done.artifacts)
157
+ result.instance_variable_set(:@features, suite_done.features)
158
+
159
+ def result.fails; @fails; end
160
+ def result.artifacts; @artifacts; end
161
+ def result.features; @features; end
162
+
163
+ result
164
+ rescue => e
165
+ puts "Error in test job: #{e.message}"
166
+ puts e.backtrace
167
+
168
+ result = Object.new
169
+ result.instance_variable_set(:@fails, 1)
170
+ result.instance_variable_set(:@artifacts, [])
171
+ result.instance_variable_set(:@features, [])
172
+
173
+ def result.fails; @fails; end
174
+ def result.artifacts; @artifacts; end
175
+ def result.features; @features; end
176
+
177
+ result
178
+ end
179
+
180
+ def receiveTestResourceConfig(partialTestResource, websocket_port = 'ipcfile')
181
+ # Parse the test resource configuration
182
+ test_resource_config = parse_test_resource_config(partialTestResource)
183
+
184
+ pm = PM_Ruby.new(test_resource_config, websocket_port)
185
+
186
+ # Run all test jobs
187
+ total_fails = 0
188
+ all_features = []
189
+ all_artifacts = []
190
+ suite_results = []
191
+
192
+ @test_jobs.each do |job|
193
+ begin
194
+ # Update the job's receiveTestResourceConfig to pass test_resource_config
195
+ result = run_test_job(job[:suite], pm, test_resource_config)
196
+ total_fails += result.fails
197
+ all_features.concat(result.features)
198
+ all_artifacts.concat(result.artifacts)
199
+ suite_results << job[:to_obj].call
200
+ rescue => e
201
+ puts "Error running test job: #{e.message}"
202
+ total_fails += 1
203
+ end
204
+ end
205
+
206
+ # Write tests.json
207
+ write_tests_json(suite_results, total_fails, all_features.uniq, all_artifacts)
208
+
209
+ # Return result
210
+ IFinalResults.new(
211
+ failed: total_fails > 0,
212
+ fails: total_fails,
213
+ artifacts: all_artifacts,
214
+ features: all_features.uniq
215
+ )
216
+ end
217
+
218
+ private
219
+
220
+ def parse_test_resource_config(partialTestResource)
221
+ begin
222
+ config = JSON.parse(partialTestResource)
223
+ # Create a hash that can be used as test resource configuration
224
+ # The PM_Ruby expects certain methods to be available
225
+ config_hash = {
226
+ 'name' => config['name'] || 'default',
227
+ 'fs' => config['fs'] || '.',
228
+ 'ports' => config['ports'] || [],
229
+ 'timeout' => config['timeout'] || 30000,
230
+ 'retries' => config['retries'] || 0,
231
+ 'environment' => config['environment'] || {}
232
+ }
233
+ # Create an object that responds to the needed methods
234
+ test_resource = Object.new
235
+ test_resource.define_singleton_method(:name) { config_hash['name'] }
236
+ test_resource.define_singleton_method(:fs) { config_hash['fs'] }
237
+ test_resource.define_singleton_method(:ports) { config_hash['ports'] }
238
+ test_resource.define_singleton_method(:timeout) { config_hash['timeout'] }
239
+ test_resource.define_singleton_method(:retries) { config_hash['retries'] }
240
+ test_resource.define_singleton_method(:environment) { config_hash['environment'] }
241
+ test_resource
242
+ rescue JSON::ParserError
243
+ # If not valid JSON, create a default config
244
+ test_resource = Object.new
245
+ test_resource.define_singleton_method(:name) { 'default' }
246
+ test_resource.define_singleton_method(:fs) { '.' }
247
+ test_resource.define_singleton_method(:ports) { [] }
248
+ test_resource.define_singleton_method(:timeout) { 30000 }
249
+ test_resource.define_singleton_method(:retries) { 0 }
250
+ test_resource.define_singleton_method(:environment) { {} }
251
+ test_resource
252
+ end
253
+ end
254
+
255
+ def write_tests_json(suite_results, total_fails, features, artifacts)
256
+ # Flatten all givens from all suites
257
+ all_givens = []
258
+ suite_results.each do |suite|
259
+ if suite[:givens]
260
+ all_givens.concat(suite[:givens])
261
+ end
262
+ end
263
+
264
+ tests_data = {
265
+ 'name' => @specs.first ? @specs.first['name'] : 'Unnamed Test',
266
+ 'givens' => all_givens,
267
+ 'fails' => total_fails,
268
+ 'failed' => total_fails > 0,
269
+ 'features' => features,
270
+ 'artifacts' => artifacts
271
+ }
272
+
273
+ # Create directory if it doesn't exist
274
+ dir_path = 'testeranto/reports/'
275
+ FileUtils.mkdir_p(dir_path) unless Dir.exist?(dir_path)
276
+
277
+ # Write to file
278
+ tests_json_path = "#{dir_path}/ruby.Calculator.test.ts.json"
279
+ File.write(tests_json_path, JSON.pretty_generate(tests_data))
280
+ puts "tests.json written to: #{tests_json_path}"
281
+ end
282
+ end
283
+
284
+ # Main function
285
+ def self.main
286
+ puts "Rubeno Ruby implementation"
287
+
288
+ # Check command line arguments
289
+ if ARGV.length < 1
290
+ puts "No test arguments provided - exiting"
291
+ exit 0
292
+ end
293
+
294
+ partialTestResource = ARGV[0]
295
+ websocket_port = ARGV[1] || 'ipcfile'
296
+
297
+ # We need a default instance to run
298
+ # In a real implementation, this would be set elsewhere
299
+ if $default_rubeno_instance.nil?
300
+ puts "WARNING: No default Rubeno instance has been configured"
301
+ puts "Creating a minimal default instance for testing..."
302
+
303
+ # Create a minimal test implementation
304
+ minimal_implementation = ITestImplementation.new(
305
+ suites: { 'Default' => { description: "Default test suite" } },
306
+ givens: {
307
+ 'Default' => ->(initial_values) do
308
+ # Return a simple object
309
+ Object.new
310
+ end
311
+ },
312
+ whens: {
313
+ 'noop' => ->() do
314
+ ->(store) do
315
+ store
316
+ end
317
+ end
318
+ },
319
+ thens: {
320
+ 'alwaysTrue' => ->() do
321
+ ->(store) do
322
+ true
323
+ end
324
+ end
325
+ }
326
+ )
327
+
328
+ # Create a minimal test specification
329
+ minimal_specification = ->(suites, givens, whens, thens) do
330
+ # The wrappers are objects with methods, not hashes
331
+ # So we need to use them correctly
332
+ [
333
+ suites.Default('Minimal Test Suite', {
334
+ 'test1' => givens.Default(
335
+ ['minimal test'],
336
+ [],
337
+ [thens.alwaysTrue()],
338
+ nil
339
+ )
340
+ })
341
+ ]
342
+ end
343
+
344
+ # Create and set a default instance
345
+ $default_rubeno_instance = Rubeno.new(
346
+ nil,
347
+ minimal_specification,
348
+ minimal_implementation,
349
+ ITTestResourceRequest.new(ports: 0),
350
+ SimpleTestAdapter.new
351
+ )
352
+
353
+ puts "Minimal default instance created"
354
+ end
355
+
356
+ result = $default_rubeno_instance.receiveTestResourceConfig(partialTestResource, websocket_port)
357
+ exit result.fails
358
+ end
359
+
360
+ # Store the default instance
361
+ $default_rubeno_instance = nil
362
+
363
+ def self.set_default_instance(instance)
364
+ $default_rubeno_instance = instance
365
+ end
366
+
367
+ # Helper function to create a Rubeno instance
368
+ def self.Rubeno(input_val = nil, test_specification = nil, test_implementation = nil, test_adapter = nil, test_resource_requirement = nil, uber_catcher = nil)
369
+ instance = Rubeno.new(
370
+ input_val,
371
+ test_specification,
372
+ test_implementation,
373
+ test_resource_requirement || ITTestResourceRequest.new,
374
+ test_adapter || SimpleTestAdapter.new,
375
+ uber_catcher
376
+ )
377
+ instance
378
+ end
379
+ end
@@ -0,0 +1,43 @@
1
+ require 'types'
2
+
3
+ module Rubeno
4
+ class SimpleTestAdapter
5
+ include ITestAdapter
6
+
7
+ def before_all(input_val, tr, pm)
8
+ input_val
9
+ end
10
+
11
+ def after_all(store, pm)
12
+ store
13
+ end
14
+
15
+ def before_each(subject, initializer, test_resource, initial_values, pm)
16
+ subject
17
+ end
18
+
19
+ def after_each(store, key, pm)
20
+ store
21
+ end
22
+
23
+ def and_when(store, when_cb, test_resource, pm)
24
+ if when_cb.respond_to?(:call)
25
+ when_cb.call(store)
26
+ else
27
+ store
28
+ end
29
+ end
30
+
31
+ def but_then(store, then_cb, test_resource, pm)
32
+ if then_cb.respond_to?(:call)
33
+ then_cb.call(store)
34
+ else
35
+ store
36
+ end
37
+ end
38
+
39
+ def assert_this(t)
40
+ !!t
41
+ end
42
+ end
43
+ end
data/lib/types.rb ADDED
@@ -0,0 +1,85 @@
1
+ module Rubeno
2
+ # Type definitions for Rubeno
3
+
4
+ # Test resource configuration
5
+ class ITTestResourceConfiguration
6
+ attr_accessor :name, :fs, :ports, :browser_ws_endpoint, :timeout, :retries, :environment
7
+
8
+ def initialize(name:, fs:, ports:, browser_ws_endpoint: nil, timeout: nil, retries: nil, environment: {})
9
+ @name = name
10
+ @fs = fs
11
+ @ports = ports
12
+ @browser_ws_endpoint = browser_ws_endpoint
13
+ @timeout = timeout
14
+ @retries = retries
15
+ @environment = environment
16
+ end
17
+ end
18
+
19
+ # Test adapter interface
20
+ module ITestAdapter
21
+ def before_all(input_val, tr, pm)
22
+ input_val
23
+ end
24
+
25
+ def after_all(store, pm)
26
+ store
27
+ end
28
+
29
+ def before_each(subject, initializer, test_resource, initial_values, pm)
30
+ subject
31
+ end
32
+
33
+ def after_each(store, key, pm)
34
+ store
35
+ end
36
+
37
+ def and_when(store, when_cb, test_resource, pm)
38
+ store
39
+ end
40
+
41
+ def but_then(store, then_cb, test_resource, pm)
42
+ store
43
+ end
44
+
45
+ def assert_this(t)
46
+ !!t
47
+ end
48
+ end
49
+
50
+ # Test specification function type
51
+ ITestSpecification = Proc
52
+
53
+ # Test implementation structure
54
+ class ITestImplementation
55
+ attr_accessor :suites, :givens, :whens, :thens
56
+
57
+ def initialize(suites:, givens:, whens:, thens:)
58
+ @suites = suites
59
+ @givens = givens
60
+ @whens = whens
61
+ @thens = thens
62
+ end
63
+ end
64
+
65
+ # Test resource request
66
+ class ITTestResourceRequest
67
+ attr_accessor :ports
68
+
69
+ def initialize(ports: 0)
70
+ @ports = ports
71
+ end
72
+ end
73
+
74
+ # Final results
75
+ class IFinalResults
76
+ attr_accessor :failed, :fails, :artifacts, :features
77
+
78
+ def initialize(failed:, fails:, artifacts:, features:)
79
+ @failed = failed
80
+ @fails = fails
81
+ @artifacts = artifacts
82
+ @features = features
83
+ end
84
+ end
85
+ end
data/rubeno.rb ADDED
@@ -0,0 +1,3 @@
1
+ # This is the main entry point for the Rubeno gem
2
+ # It loads the actual implementation
3
+ require 'rubeno'
metadata ADDED
@@ -0,0 +1,54 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: testeranto.rubeno
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.4
5
+ platform: ruby
6
+ authors:
7
+ - Adam Wong
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2010-04-03 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: The Ruby implementation of Testeranto
14
+ email: adamwong246@gmail.com
15
+ executables:
16
+ - rubeno
17
+ extensions: []
18
+ extra_rdoc_files: []
19
+ files:
20
+ - README.md
21
+ - Rakefile
22
+ - bin/rubeno
23
+ - lib/base_given.rb
24
+ - lib/base_suite.rb
25
+ - lib/base_then.rb
26
+ - lib/base_when.rb
27
+ - lib/pm/ruby.rb
28
+ - lib/rubeno.rb
29
+ - lib/simple_adapter.rb
30
+ - lib/types.rb
31
+ - rubeno.rb
32
+ homepage: http://rubygems.org/gems/rubeno
33
+ licenses: []
34
+ metadata: {}
35
+ post_install_message:
36
+ rdoc_options: []
37
+ require_paths:
38
+ - lib
39
+ required_ruby_version: !ruby/object:Gem::Requirement
40
+ requirements:
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ version: '0'
44
+ required_rubygems_version: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - ">="
47
+ - !ruby/object:Gem::Version
48
+ version: '0'
49
+ requirements: []
50
+ rubygems_version: 3.4.1
51
+ signing_key:
52
+ specification_version: 3
53
+ summary: The Ruby implementation of Testeranto
54
+ test_files: []