happybara 1.0.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 +7 -0
- data/lib/happybara.rb +9 -0
- data/lib/happybara/agent.rb +117 -0
- data/lib/happybara/callbacks.rb +49 -0
- data/lib/happybara/executor.rb +133 -0
- data/lib/happybara/registry.rb +29 -0
- data/lib/happybara/serializer.rb +39 -0
- data/lib/happybara/synchronizer.rb +94 -0
- data/lib/happybara/version.rb +3 -0
- data/spec/lib/happybara/agent_spec.rb +7 -0
- data/spec/lib/happybara/serializer_spec.rb +19 -0
- data/spec/spec_helper.rb +17 -0
- data/spec/support/bootstrap.rb +38 -0
- data/spec/support/log/test.log +0 -0
- metadata +100 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: f07111257fd0128b45efe622094b7cbadb1904e2
|
4
|
+
data.tar.gz: d9dd81e7fe06f279ae1590f17ae5513ba686e0c7
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: ceae202a3842453f47586a01e7a5818e26f3b96e7da9a226d162edee0153f8a6ef7a6eab40c4216fc8c20dafbc1d4edfeb90e52f2ca47ee0c75d712cfdac8db1
|
7
|
+
data.tar.gz: 84b24d10b0b2af0a9a4f1583b99d4cefe71e7ff17067c6464078487266f376994a97d8fccc76479e0653ced15288b68335d0593b0f1ff9d9199d0856d8587c2f
|
data/lib/happybara.rb
ADDED
@@ -0,0 +1,9 @@
|
|
1
|
+
require_relative './happybara/agent'
|
2
|
+
require_relative './happybara/callbacks'
|
3
|
+
require_relative './happybara/executor'
|
4
|
+
require_relative './happybara/serializer'
|
5
|
+
require_relative './happybara/synchronizer'
|
6
|
+
require_relative './happybara/version'
|
7
|
+
|
8
|
+
module Happybara
|
9
|
+
end
|
@@ -0,0 +1,117 @@
|
|
1
|
+
require 'colorize'
|
2
|
+
require 'singleton'
|
3
|
+
require_relative './callbacks'
|
4
|
+
require_relative './serializer'
|
5
|
+
require_relative './synchronizer'
|
6
|
+
require_relative './registry'
|
7
|
+
|
8
|
+
module Happybara
|
9
|
+
class Agent
|
10
|
+
include Callbacks
|
11
|
+
|
12
|
+
attr_accessor :grace_timeout
|
13
|
+
|
14
|
+
# Create a new Agent instance and configure it.
|
15
|
+
#
|
16
|
+
# @yield [Happybara::Agent] instance
|
17
|
+
# The new agent instance. The block will evaluated in this instance's
|
18
|
+
# scope so you can call methods like #on and #before() globally.
|
19
|
+
#
|
20
|
+
# @return [Happybara::Agent]
|
21
|
+
def self.create(&block)
|
22
|
+
Happybara::Agent.new.tap do |instance|
|
23
|
+
instance.instance_exec do
|
24
|
+
block[self]
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def initialize(serializer: Serializer.new)
|
30
|
+
@serializer = serializer
|
31
|
+
@reflections = {}
|
32
|
+
@mutex = Synchronizer.new({
|
33
|
+
on_abrupt_termination: ->() {
|
34
|
+
run_callbacks(:abrupt_termination)
|
35
|
+
run_after_example
|
36
|
+
},
|
37
|
+
|
38
|
+
on_forced_release: ->() {
|
39
|
+
run_callbacks(:forced_release)
|
40
|
+
run_after_example
|
41
|
+
},
|
42
|
+
|
43
|
+
on_race_condition: ->() {
|
44
|
+
run_callbacks(:race_condition)
|
45
|
+
}
|
46
|
+
})
|
47
|
+
end
|
48
|
+
|
49
|
+
# Bind the Agent to a websocket to start a testing session.
|
50
|
+
#
|
51
|
+
# @param [Websocket] socket
|
52
|
+
# @param [Object] actor
|
53
|
+
# An instance of an object that will be receiving the RPC calls. Any
|
54
|
+
# `eval` RPCs will be evaluated in this object's context. If you're
|
55
|
+
# using Tubesock and hijacking a Rails controller, you can pass the
|
56
|
+
# controller itself as an actor, then you also have access to
|
57
|
+
# ApplicationController methods and helpers as RPCs.
|
58
|
+
#
|
59
|
+
# @return [NilClass]
|
60
|
+
def bind(socket, actor)
|
61
|
+
@mutex.lock(socket: socket, timeout: grace_timeout) do
|
62
|
+
executor = Executor.new(actor: actor, socket: socket, serializer: @serializer)
|
63
|
+
executor.run_before_example = method(:run_before_example)
|
64
|
+
executor.run_after_example = method(:run_after_example)
|
65
|
+
executor.run_around_command = method(:run_around_command)
|
66
|
+
|
67
|
+
socket.onopen do
|
68
|
+
puts "Happybara: client connected.".green
|
69
|
+
end
|
70
|
+
|
71
|
+
socket.onmessage do |payload|
|
72
|
+
executor.handle_message(payload)
|
73
|
+
end
|
74
|
+
|
75
|
+
socket.onclose do
|
76
|
+
puts "Happybara: client disconnected.".green
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
nil
|
81
|
+
end
|
82
|
+
|
83
|
+
def on_forced_release(&callback)
|
84
|
+
on(:forced_release, &callback)
|
85
|
+
end
|
86
|
+
|
87
|
+
def on_abrupt_termination(&callback)
|
88
|
+
on(:abrupt_termination, &callback)
|
89
|
+
end
|
90
|
+
|
91
|
+
def on_race_condition(&callback)
|
92
|
+
on(:race_condition, &callback)
|
93
|
+
end
|
94
|
+
|
95
|
+
private
|
96
|
+
|
97
|
+
def run_before_example
|
98
|
+
run_callbacks(:before_each)
|
99
|
+
end
|
100
|
+
|
101
|
+
def run_after_example
|
102
|
+
run_callbacks(:after_each)
|
103
|
+
@mutex.release
|
104
|
+
end
|
105
|
+
|
106
|
+
def run_around_command(message, &block)
|
107
|
+
# TODO: support nested procs.. ?
|
108
|
+
callback = callbacks[:around_command] && callbacks[:around_command].first
|
109
|
+
|
110
|
+
if callback
|
111
|
+
callback.call(message, block)
|
112
|
+
else
|
113
|
+
block.call
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
module Happybara
|
2
|
+
module Callbacks
|
3
|
+
def before(stage, &callback)
|
4
|
+
case stage.to_sym
|
5
|
+
when :each
|
6
|
+
on(:before_each, &callback)
|
7
|
+
when :all
|
8
|
+
Rails.configuration.after_initialize(&callback)
|
9
|
+
else
|
10
|
+
fail "Unknown :before stage '#{stage}'"
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def after(stage, &callback)
|
15
|
+
case stage.to_sym
|
16
|
+
when :each
|
17
|
+
on(:after_each, &callback)
|
18
|
+
else
|
19
|
+
fail "Unknown :after stage '#{stage}'"
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def around(stage, &callback)
|
24
|
+
case stage.to_sym
|
25
|
+
when :command
|
26
|
+
on(:around_command, &callback)
|
27
|
+
else
|
28
|
+
fail "Unknown :around stage '#{stage}'"
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def on(event, &handler)
|
33
|
+
callbacks[event.to_sym] ||= []
|
34
|
+
callbacks[event.to_sym] << handler
|
35
|
+
end
|
36
|
+
|
37
|
+
protected
|
38
|
+
|
39
|
+
def callbacks
|
40
|
+
@callbacks ||= {}
|
41
|
+
end
|
42
|
+
|
43
|
+
def run_callbacks(event)
|
44
|
+
callbacks.fetch(event).each do |callback|
|
45
|
+
callback.call
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,133 @@
|
|
1
|
+
module Happybara
|
2
|
+
class Executor
|
3
|
+
attr_accessor :run_before_example, :run_after_example, :run_around_command
|
4
|
+
|
5
|
+
def initialize(actor:, socket:, serializer:)
|
6
|
+
@actor = actor
|
7
|
+
@socket = socket
|
8
|
+
@serializer = serializer
|
9
|
+
@registry = Registry.new
|
10
|
+
end
|
11
|
+
|
12
|
+
def handle_message(payload)
|
13
|
+
message = @serializer.deserialize payload
|
14
|
+
respond = method(:respond_to_message).curry[message['id']]
|
15
|
+
|
16
|
+
case message['type']
|
17
|
+
when 'SETUP'
|
18
|
+
handle_setup(message, &respond)
|
19
|
+
when 'RPC'
|
20
|
+
handle_rpc(message, &respond)
|
21
|
+
when 'EVAL'
|
22
|
+
handle_eval(message, &respond)
|
23
|
+
when 'QUERY'
|
24
|
+
handle_query(message, &respond)
|
25
|
+
when 'REFLECT'
|
26
|
+
handle_reflect(message, &respond)
|
27
|
+
when 'TEARDOWN'
|
28
|
+
handle_teardown(message, &respond)
|
29
|
+
else
|
30
|
+
fail "unknown message type #{message['type']}"
|
31
|
+
end
|
32
|
+
rescue StandardError => e
|
33
|
+
puts "#{'=' * 80}\nHandler error!\n#{'-' * 80}"
|
34
|
+
puts e.message
|
35
|
+
puts e.backtrace
|
36
|
+
|
37
|
+
respond['error', { details: e.message, backtrace: e.backtrace }]
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
def respond_to_message(message_id, type, data)
|
43
|
+
@socket.send_data({
|
44
|
+
id: message_id,
|
45
|
+
type: type,
|
46
|
+
data: data
|
47
|
+
}.to_json)
|
48
|
+
end
|
49
|
+
|
50
|
+
def handle_setup(message, &respond)
|
51
|
+
run_before_example[]
|
52
|
+
|
53
|
+
respond['SETUP_RESPONSE', @serializer.serialize_value(true)]
|
54
|
+
end
|
55
|
+
|
56
|
+
def handle_query(message, &respond)
|
57
|
+
@registry.open!(message['data']['klass_name'])
|
58
|
+
|
59
|
+
klass = message['data']['klass_name'].constantize
|
60
|
+
klass.public_instance_methods(true)
|
61
|
+
.sort
|
62
|
+
.map(&:to_s)
|
63
|
+
.reject { |s| s[0] =~ /[\W|_]/ }
|
64
|
+
.tap do |visible_instance_methods|
|
65
|
+
respond['QUERY_RESPONSE', @serializer.serialize({
|
66
|
+
instance_methods: visible_instance_methods
|
67
|
+
})]
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def handle_rpc(message, &respond)
|
72
|
+
method_name = message['data']['procedure']
|
73
|
+
method_args = message['data']['payload'] || {}
|
74
|
+
method_opts = message['data']['options'] || {}
|
75
|
+
|
76
|
+
fail "Unknown RPC #{method_name}" unless @actor.respond_to?(method_name)
|
77
|
+
|
78
|
+
result = around_command(message) do
|
79
|
+
@actor.send(message['data']['procedure'], **method_args.symbolize_keys)
|
80
|
+
end
|
81
|
+
|
82
|
+
@registry << result
|
83
|
+
|
84
|
+
respond['RPC_RESPONSE', @serializer.serialize_value(result)]
|
85
|
+
end
|
86
|
+
|
87
|
+
def handle_eval(message, &respond)
|
88
|
+
string = message['data']['string']
|
89
|
+
options = message['data']['options'] || {}
|
90
|
+
|
91
|
+
around_command(message) do
|
92
|
+
result = @actor.instance_eval { eval(string) }
|
93
|
+
@registry << result
|
94
|
+
respond['EVAL_RESPONSE', @serializer.serialize_value(result)]
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
def handle_reflect(message, &respond)
|
99
|
+
object_id = message['data']['object_id']
|
100
|
+
klass_name = message['data']['klass_name']
|
101
|
+
method_name = message['data']['method']
|
102
|
+
method_args = message['data']['arguments']
|
103
|
+
method_opts = message['data']['options'] || {}
|
104
|
+
|
105
|
+
ref = resolve_reference(message['data'])
|
106
|
+
|
107
|
+
fail "Object #{object_id} of type #{klass_name} could not be reflected" if ref.nil?
|
108
|
+
|
109
|
+
rc = around_command(message) do
|
110
|
+
ref.send(method_name, *method_args)
|
111
|
+
end
|
112
|
+
|
113
|
+
@registry << rc
|
114
|
+
|
115
|
+
respond['REFLECT_RESPONSE', @serializer.serialize_value(rc)]
|
116
|
+
end
|
117
|
+
|
118
|
+
def handle_teardown(message, &respond)
|
119
|
+
@registry.release!
|
120
|
+
run_after_example[]
|
121
|
+
|
122
|
+
respond['TEARDOWN_RESPONSE', @serializer.serialize_value(true)]
|
123
|
+
end
|
124
|
+
|
125
|
+
def around_command(message, &block)
|
126
|
+
run_around_command[message['data'], &block]
|
127
|
+
end
|
128
|
+
|
129
|
+
def resolve_reference(descriptor)
|
130
|
+
@registry[descriptor['object_id']]
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module Happybara
|
2
|
+
class Registry
|
3
|
+
def initialize()
|
4
|
+
@reflections = {}
|
5
|
+
end
|
6
|
+
|
7
|
+
def open!(klass_name)
|
8
|
+
@reflections[klass_name.to_s] = {}
|
9
|
+
end
|
10
|
+
|
11
|
+
def <<(object)
|
12
|
+
self.tap do
|
13
|
+
klass_name = object.class.name.to_s
|
14
|
+
|
15
|
+
if @reflections[klass_name]
|
16
|
+
@reflections[klass_name][object.object_id] = object
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def [](object_id)
|
22
|
+
ObjectSpace._id2ref(object_id) || nil
|
23
|
+
end
|
24
|
+
|
25
|
+
def release!()
|
26
|
+
@reflections.clear
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module Happybara
|
2
|
+
class Serializer
|
3
|
+
def serialize(result)
|
4
|
+
result.as_json
|
5
|
+
end
|
6
|
+
|
7
|
+
def serialize_value(result)
|
8
|
+
{
|
9
|
+
klass: result.class.name,
|
10
|
+
object_id: result.object_id,
|
11
|
+
value: result.as_json
|
12
|
+
}
|
13
|
+
end
|
14
|
+
|
15
|
+
def serialize_reference(ref)
|
16
|
+
{
|
17
|
+
klass: ref.class.name,
|
18
|
+
object_id: ref.object_id,
|
19
|
+
}
|
20
|
+
end
|
21
|
+
|
22
|
+
def deserialize(payload)
|
23
|
+
JSON.load(payload, ->(datum) {
|
24
|
+
case datum
|
25
|
+
when Hash
|
26
|
+
datum.each_pair do |key, value|
|
27
|
+
if value.is_a?(Hash) && value['$$ref'] == true && value['$$object_id']
|
28
|
+
datum[key] = ObjectSpace._id2ref(value['$$object_id']) || nil
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
datum
|
33
|
+
else
|
34
|
+
datum
|
35
|
+
end
|
36
|
+
})
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,94 @@
|
|
1
|
+
module Happybara
|
2
|
+
class Synchronizer
|
3
|
+
def initialize(
|
4
|
+
on_forced_release:,
|
5
|
+
on_abrupt_termination:,
|
6
|
+
on_race_condition: nil
|
7
|
+
)
|
8
|
+
@on_forced_release = on_forced_release || ->() {}
|
9
|
+
@on_abrupt_termination = on_abrupt_termination || ->() {}
|
10
|
+
@on_race_condition = on_race_condition || ->() {}
|
11
|
+
@mutex = Mutex.new
|
12
|
+
end
|
13
|
+
|
14
|
+
def lock(socket:, timeout: 5.0, frequency: 0.1, &block)
|
15
|
+
socket.onopen do
|
16
|
+
acquire!(timeout: timeout, frequency: frequency, &block)
|
17
|
+
end
|
18
|
+
|
19
|
+
socket.onclose do
|
20
|
+
@on_abrupt_termination[] if @mutex.owned? && @mutex.locked?
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def release
|
25
|
+
@mutex.unlock
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def acquire!(timeout:, frequency:, &block)
|
31
|
+
if @mutex.locked? && !@mutex.owned?
|
32
|
+
fail "
|
33
|
+
The Happybara agent is being accessed by multiple threads. This
|
34
|
+
mode of operation is not currently supported.
|
35
|
+
|
36
|
+
(This most likely indicates that you have multiple client runners
|
37
|
+
communicating with this Rails server concurrently.)
|
38
|
+
"
|
39
|
+
elsif @mutex.locked?
|
40
|
+
@on_race_condition[]
|
41
|
+
|
42
|
+
begin
|
43
|
+
Timer.new(frequency: frequency, timeout: timeout)
|
44
|
+
.until { vacant? }
|
45
|
+
.otherwise { @on_forced_release[] }
|
46
|
+
.start!
|
47
|
+
rescue TimeoutError
|
48
|
+
@on_forced_release[]
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
@mutex.lock
|
53
|
+
|
54
|
+
yield
|
55
|
+
end
|
56
|
+
|
57
|
+
def vacant?
|
58
|
+
!@mutex.locked?
|
59
|
+
end
|
60
|
+
|
61
|
+
class Timer
|
62
|
+
def initialize(frequency:, timeout:)
|
63
|
+
@frequency = frequency
|
64
|
+
@timeout = timeout
|
65
|
+
@until = nil
|
66
|
+
@otherwise = nil
|
67
|
+
end
|
68
|
+
|
69
|
+
def until(&block)
|
70
|
+
@until = block
|
71
|
+
end
|
72
|
+
|
73
|
+
def otherwise(&block)
|
74
|
+
@otherwise = block
|
75
|
+
end
|
76
|
+
|
77
|
+
def start!
|
78
|
+
elapsed = 0.0
|
79
|
+
|
80
|
+
while !@until[]
|
81
|
+
elapsed += frequency
|
82
|
+
|
83
|
+
if elapsed < timeout
|
84
|
+
sleep frequency
|
85
|
+
elsif @otherwise
|
86
|
+
@otherwise[]
|
87
|
+
else
|
88
|
+
raise TimeoutError.new
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
require 'spec_helper'
|
3
|
+
|
4
|
+
describe Happybara::Serializer do
|
5
|
+
describe '#deserialize' do
|
6
|
+
it 'resolves references' do
|
7
|
+
some_object = Object.new
|
8
|
+
|
9
|
+
result = subject.deserialize({
|
10
|
+
foo: {
|
11
|
+
'$$ref': true,
|
12
|
+
'$$object_id': some_object.object_id
|
13
|
+
}
|
14
|
+
}.to_json)
|
15
|
+
|
16
|
+
expect(result['foo']).to be(some_object)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
ENV['RAILS_ENV'] ||= "test"
|
2
|
+
|
3
|
+
require 'pry'
|
4
|
+
require_relative '../lib/happybara'
|
5
|
+
require_relative './support/bootstrap'
|
6
|
+
|
7
|
+
RSpec.configure do |config|
|
8
|
+
config.expect_with :rspec do |expectations|
|
9
|
+
expectations.include_chain_clauses_in_custom_matcher_descriptions = true
|
10
|
+
end
|
11
|
+
|
12
|
+
config.mock_with :rspec do |mocks|
|
13
|
+
mocks.verify_partial_doubles = true
|
14
|
+
end
|
15
|
+
|
16
|
+
config.shared_context_metadata_behavior = :apply_to_host_groups
|
17
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# This dance is necessary to subvert railtie from resolving get_smart's Rails
|
2
|
+
# application as the "Rails.application"; we really only need a dummy
|
3
|
+
# application that can host some routes, and if this gem was sourced elsewhere
|
4
|
+
# we could just define such an application directly but since it's sourced
|
5
|
+
# within get_smart, railtie will resolve the root `config.ru` and cause sadness.
|
6
|
+
#
|
7
|
+
# Be happy, unless you're upgrading Rails and this gets broken. :-)
|
8
|
+
|
9
|
+
require 'rails/application'
|
10
|
+
require 'rails/configuration'
|
11
|
+
require 'rails/railtie'
|
12
|
+
require 'active_support'
|
13
|
+
|
14
|
+
module Rails
|
15
|
+
class << self
|
16
|
+
attr_writer :app_class, :application
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
module Happybara
|
21
|
+
class TestApplication < ::Rails::Application
|
22
|
+
class << self
|
23
|
+
def find_root(*)
|
24
|
+
Pathname.new File.realpath File.expand_path(File.dirname(__FILE__))
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
require 'rails'
|
31
|
+
require 'action_view'
|
32
|
+
require 'action_controller'
|
33
|
+
|
34
|
+
Happybara::TestApplication.configure do
|
35
|
+
config.eager_load = ENV['COVERAGE'] != '1'
|
36
|
+
end
|
37
|
+
|
38
|
+
Happybara::TestApplication.initialize!
|
File without changes
|
metadata
ADDED
@@ -0,0 +1,100 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: happybara
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Ahmad Amireh
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2017-04-16 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rails
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '5'
|
20
|
+
- - "<"
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: '6'
|
23
|
+
type: :runtime
|
24
|
+
prerelease: false
|
25
|
+
version_requirements: !ruby/object:Gem::Requirement
|
26
|
+
requirements:
|
27
|
+
- - ">="
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '5'
|
30
|
+
- - "<"
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '6'
|
33
|
+
- !ruby/object:Gem::Dependency
|
34
|
+
name: rspec
|
35
|
+
requirement: !ruby/object:Gem::Requirement
|
36
|
+
requirements:
|
37
|
+
- - "~>"
|
38
|
+
- !ruby/object:Gem::Version
|
39
|
+
version: '3.5'
|
40
|
+
type: :development
|
41
|
+
prerelease: false
|
42
|
+
version_requirements: !ruby/object:Gem::Requirement
|
43
|
+
requirements:
|
44
|
+
- - "~>"
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: '3.5'
|
47
|
+
description: |
|
48
|
+
Happybara is an integration testing platform providing a webserver in Ruby that
|
49
|
+
accepts connections over a websocket where test clients can dispatch RPCs to
|
50
|
+
Ruby from a different language like JavaScript.
|
51
|
+
email:
|
52
|
+
- ahmad@amireh.net
|
53
|
+
executables: []
|
54
|
+
extensions: []
|
55
|
+
extra_rdoc_files: []
|
56
|
+
files:
|
57
|
+
- lib/happybara.rb
|
58
|
+
- lib/happybara/agent.rb
|
59
|
+
- lib/happybara/callbacks.rb
|
60
|
+
- lib/happybara/executor.rb
|
61
|
+
- lib/happybara/registry.rb
|
62
|
+
- lib/happybara/serializer.rb
|
63
|
+
- lib/happybara/synchronizer.rb
|
64
|
+
- lib/happybara/version.rb
|
65
|
+
- spec/lib/happybara/agent_spec.rb
|
66
|
+
- spec/lib/happybara/serializer_spec.rb
|
67
|
+
- spec/spec_helper.rb
|
68
|
+
- spec/support/bootstrap.rb
|
69
|
+
- spec/support/log/test.log
|
70
|
+
homepage: https://github.com/amireh/happybara
|
71
|
+
licenses:
|
72
|
+
- BSD-3
|
73
|
+
metadata: {}
|
74
|
+
post_install_message:
|
75
|
+
rdoc_options: []
|
76
|
+
require_paths:
|
77
|
+
- lib
|
78
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
84
|
+
requirements:
|
85
|
+
- - ">="
|
86
|
+
- !ruby/object:Gem::Version
|
87
|
+
version: '0'
|
88
|
+
requirements: []
|
89
|
+
rubyforge_project:
|
90
|
+
rubygems_version: 2.4.5.1
|
91
|
+
signing_key:
|
92
|
+
specification_version: 4
|
93
|
+
summary: An integration test server using Websockets.
|
94
|
+
test_files:
|
95
|
+
- spec/lib/happybara/agent_spec.rb
|
96
|
+
- spec/lib/happybara/serializer_spec.rb
|
97
|
+
- spec/spec_helper.rb
|
98
|
+
- spec/support/bootstrap.rb
|
99
|
+
- spec/support/log/test.log
|
100
|
+
has_rdoc:
|