capybarbecue 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 8fa420c19a4cf57eb1a6fb4d9122e1a01f7355cf
4
- data.tar.gz: f3456b9851b38b602da77a2a542bfb78d71cf602
3
+ metadata.gz: a2678761d8bc81927ea82cb9bca880c1a8818043
4
+ data.tar.gz: 6d41973f08aa3b7f8bd69cb0bae7fe88db3e77c8
5
5
  SHA512:
6
- metadata.gz: c50a76bc67c480cddab96a0aa8a55459b6522ec022e6961ae369d7dc7c14953394b08c37b9501be7fc34437b44da186a6b1efc72f725014e7da1ab4db5fd065a
7
- data.tar.gz: 329541d5aabcf0be4d796721aca4156fc52971d64c94f6b6113a0cf4cf09dbd463dc12c482487c18020e865f18773e89f2d38b9e22c556e088d8fd7a4ae14371
6
+ metadata.gz: ef670b9982bc9a534a8b36b1f5edcf05c99e0a22d334bb8e88af8d6505ffee4dd0d169f156a7bf8ce29b214bcbff6771a74e1bc7c26f2518a1af50ead9f58b89
7
+ data.tar.gz: 63a06f7afeffa2d0b9d66f15d62dd0767efc01842d15c73a8483c06ff912cad74af0e2f7aa9773a9aa138ebad0230b021d03ca615635e75471c5a0b944758718
@@ -1,20 +1,21 @@
1
+ The MIT License (MIT)
2
+
1
3
  Copyright (c) 2013 Andrew DiMichele
2
4
 
3
- Permission is hereby granted, free of charge, to any person obtaining
4
- a copy of this software and associated documentation files (the
5
- "Software"), to deal in the Software without restriction, including
6
- without limitation the rights to use, copy, modify, merge, publish,
7
- distribute, sublicense, and/or sell copies of the Software, and to
8
- permit persons to whom the Software is furnished to do so, subject to
9
- the following conditions:
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
10
11
 
11
- The above copyright notice and this permission notice shall be
12
- included in all copies or substantial portions of the Software.
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
13
14
 
14
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
- NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
- LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
- OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
- WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
@@ -0,0 +1,89 @@
1
+ # Capybarbecue
2
+
3
+ _Capybara the way you like it... skinned, butchered, and cooked to perfection._
4
+
5
+ ## What is Capybarbecue?
6
+
7
+ *Capybarbecue* makes adds reliability and flexibility to your Capybara test suite by silently changing the way
8
+ Capybara works. It gives you a shared database connection and allows you to safely write tests that make use of it
9
+ while eliminating race conditions.
10
+
11
+ For example, the following Capybara test only works with a shared database connection and without *Capybarbecue* it
12
+ will randomly fail every now and then due to the race condition that your typical shared database connection
13
+ introduces:
14
+
15
+ ```ruby
16
+ user = User.first
17
+ visit edit_user_path(user)
18
+ fill_in 'first_name', with: 'Andrew' # Edit the user's name
19
+ click_on 'Submit' # Triggers POST request
20
+ expect(user.reload.first_name).to eq 'Andrew' # This line will cause you some headaches
21
+ ```
22
+
23
+ Now in principle, this isn't the "right" way to perform automated acceptance testing since you would only simulate
24
+ actual user behavior which can only occur through a browser, but let's face it: this paradigm doesn't work for all
25
+ testing use cases and writing tests like the one above is sometimes just too damn convenient.
26
+
27
+ ## Setup
28
+
29
+ This is so easy... you're gonna love it.
30
+
31
+ To install *Capybarbecue*, add this line to your `Gemfile` and then `bundle install`
32
+
33
+ ```ruby
34
+ gem 'capybarbecue'
35
+ ```
36
+
37
+ Then at the bottom of your test set up file (e.g. RSpec's `spec_helper.rb` or Cucumber's `env.rb`) after you've done
38
+ your Capybara setup, add this line:
39
+
40
+ ```ruby
41
+ Capybarbecue.activate!
42
+ ```
43
+
44
+ That's it. Just continue to write your Capybara tests as you normally would. Welcome to the BBQ!
45
+
46
+ ## Beware Javascript
47
+
48
+ AJAX and other javascript interactions may cause you problems since Capybara has no way to know when your javascript has
49
+ finished executing. To prevent issues with this, make sure you check the state of the page to ensure that your
50
+ javascript has finished running.
51
+
52
+ For example, let's say we have a button with an `onclick` handler that simply visits a new page. Here's the
53
+ _wrong_ way to test:
54
+
55
+ ```ruby
56
+ visit '/page/1'
57
+ click_on 'Go to /page/2' # Triggers an `onclick` handler
58
+ click_on 'Go to /page/3' # OOPS! No guarantee that /page/2 has been loaded yet!
59
+ ```
60
+
61
+ Here's the _right_ way:
62
+
63
+ ```ruby
64
+ visit '/page/1'
65
+ click_on 'Go to /page/2' # Triggers an `onclick` handler
66
+ page.should have_content 'Page 2' # GREAT! Capybara will actually wait until this is true (or fail after some timeout)
67
+ click_on 'Go to /page/3' # We know we're on the right page and that this button exists!
68
+ ```
69
+
70
+ Note that this only works with Capybara matchers. For example, the following will _not_ work:
71
+
72
+ ```ruby
73
+ visit '/page/1'
74
+ click_on 'Go to /page/2' # Triggers an `onclick` handler
75
+ current_path.should start_with '/page/2' # OOPS! The new page may not have loaded yet!
76
+ click_on 'Go to /page/3' # We may not even get here
77
+ ```
78
+
79
+ ## How it works
80
+
81
+ *Capybarbecue* works by running your tests and handling Rack requests in the same thread. When you make a call to the
82
+ Capybara API, your call is delegated to another thread while the main thread is used to handle web requests, which are
83
+ themselves queued by third thread which acts as the web server. Once the Capybara matcher returns, control is returned
84
+ to your test.
85
+
86
+ ## Copyright
87
+
88
+ Copyright (c) 2013 Andrew DiMichele. See LICENSE.txt for further details.
89
+
@@ -10,16 +10,23 @@ module Capybarbecue
10
10
  return if activated?
11
11
  require 'capybarbecue/async_call'
12
12
  require 'capybarbecue/async_delegate_class'
13
- require 'capybarbecue/rack_runner'
13
+ require 'capybarbecue/async_executer'
14
14
  require 'capybarbecue/server'
15
15
 
16
16
  (class << Capybara; self end).instance_eval do
17
17
  alias_method :original_session, :current_session
18
18
  define_method :current_session do
19
- Capybarbecue::AsyncDelegateClass.new(original_session) do
19
+ bbq_lookup[original_session] ||= Capybarbecue::AsyncDelegateClass.new(original_session, bbq_executer) do
20
20
  Capybara.app.handle_requests
21
21
  end
22
22
  end
23
+ # TODO: Consider nesting these under an object that's easy to remove
24
+ define_method :bbq_lookup do
25
+ @bbq_lookup ||= {}
26
+ end
27
+ define_method :bbq_executer do
28
+ @bbq_executer ||= Capybarbecue::AsyncExecuter.new
29
+ end
23
30
  end
24
31
 
25
32
  Capybara.send(:session_pool).clear
@@ -36,6 +43,9 @@ module Capybarbecue
36
43
  (class << Capybara; self end).instance_eval do
37
44
  alias_method :current_session, :original_session
38
45
  remove_method :original_session
46
+ remove_method :bbq_lookup
47
+ remove_method :bbq_executer
48
+ # TODO: Remove instance variables as well
39
49
  end
40
50
  Capybara.send(:session_pool).clear
41
51
  Capybara.app = Capybara.app.app
@@ -1,8 +1,7 @@
1
1
  require 'thread'
2
2
 
3
- class AsyncCall # Asynchronously calls a method
4
- DEFAULT_TIMEOUT = 5.0
5
- attr_reader :thread
3
+ class AsyncCall
4
+ DEFAULT_TIMEOUT = 30
6
5
  attr_reader :ready
7
6
  attr_reader :to_s
8
7
  alias :ready? :ready
@@ -12,12 +11,13 @@ class AsyncCall # Asynchronously calls a method
12
11
  @ready = false
13
12
  @response = nil
14
13
  @exception = nil
15
- @thread = Thread.new do
16
- begin
17
- respond_with obj.send(method, *args, &block)
18
- rescue Exception => e
19
- respond_with_exception e
20
- end
14
+ end
15
+
16
+ def run # Should be called from the executer
17
+ begin
18
+ respond_with @obj.send(@method, *@args, &@block)
19
+ rescue Exception => e
20
+ respond_with_exception e
21
21
  end
22
22
  end
23
23
 
@@ -29,7 +29,7 @@ class AsyncCall # Asynchronously calls a method
29
29
  # It feels dangerous not to sleep here... keep a pulse on this (sleep causes performance problems)
30
30
  Thread.pass
31
31
  end
32
- kill! and raise Timeout::Error.new('Timeout expired before response received') unless ready?
32
+ raise Timeout::Error.new('Timeout expired before response received') unless ready?
33
33
  if @exception.present?
34
34
  # Add the backtrace from this thread to make it useful
35
35
  backtrace = @exception.backtrace + Kernel.caller
@@ -39,10 +39,6 @@ class AsyncCall # Asynchronously calls a method
39
39
  @response
40
40
  end
41
41
 
42
- def kill!
43
- @thread.kill
44
- end
45
-
46
42
  def to_s
47
43
  s = "#{@obj.class.name}##{@method}(#{@args.map{ |a| a.class.name }.join(', ')})"
48
44
  s += ' { ... }' if @block.present?
@@ -2,16 +2,23 @@ module Capybarbecue
2
2
  class AsyncDelegateClass
3
3
  attr_reader :instance
4
4
 
5
- def initialize(instance, &wait_proc)
5
+ def initialize(instance, executer, &wait_proc)
6
6
  @instance = instance
7
+ @executer = executer
7
8
  @wait_proc = wait_proc # Called repeatedly while waiting
8
9
  end
9
10
 
10
11
  def method_missing(method, *args, &block)
11
12
  call = AsyncCall.new(@instance, method, *args, &block)
13
+ @executer.execute(call)
12
14
  wrap_response(call.wait_for_response(&@wait_proc))
13
15
  end
14
16
 
17
+ # This is our hint that we are an AsyncDelegateClass
18
+ def __async_delegate__
19
+ true
20
+ end
21
+
15
22
  private
16
23
 
17
24
  def respond_to_missing?(method, include_all=false)
@@ -21,10 +28,19 @@ module Capybarbecue
21
28
  # Wrap anything that looks like Capybara
22
29
  def wrap_response(value)
23
30
  if value.class.name.include?('Capybara')
24
- AsyncDelegateClass.new(value, &@wait_proc)
31
+ AsyncDelegateClass.new(value, @executer, &@wait_proc)
25
32
  else
26
33
  value
27
34
  end
28
35
  end
36
+
37
+ ## For instance methods of Object, delegate these to the object we've wrapped
38
+ super_methods = superclass.instance_methods - (instance_methods - superclass.instance_methods)
39
+ super_methods -= [:methods, :__send__, :__id__, :respond_to?] # Don't override these bad boys
40
+ super_methods.each do |method|
41
+ define_method method do |*args, &block|
42
+ method_missing(method, *args, &block)
43
+ end
44
+ end
29
45
  end
30
46
  end
@@ -0,0 +1,35 @@
1
+ require 'thread'
2
+
3
+ module Capybarbecue
4
+ class AsyncExecuter
5
+ attr_reader :thread
6
+
7
+ def initialize
8
+ @queue = Queue.new
9
+ @thread = Thread.new do
10
+ while true
11
+ # This #run method should handle its own exceptions
12
+ @queue.deq.run
13
+ end
14
+ end
15
+ end
16
+
17
+ def execute(async_call)
18
+ if in_executer?
19
+ async_call.run
20
+ else
21
+ @queue.enq(async_call)
22
+ end
23
+ end
24
+
25
+ def kill!
26
+ @thread.kill
27
+ end
28
+
29
+ private
30
+
31
+ def in_executer?
32
+ Thread.current == @thread
33
+ end
34
+ end
35
+ end
@@ -25,6 +25,9 @@ module Capybarbecue
25
25
  request.response = @app.call(request.env)
26
26
  rescue Exception => e
27
27
  request.exception = e
28
+ ensure
29
+ body = request.response.try(:last)
30
+ body.close if body.respond_to? :close
28
31
  end
29
32
  end
30
33
  end
@@ -1,3 +1,3 @@
1
1
  module Capybarbecue
2
- VERSION = '0.1.0'
2
+ VERSION = '0.2.0'
3
3
  end
@@ -6,22 +6,15 @@ describe AsyncCall do
6
6
  let(:args) { [] }
7
7
  let(:block) { nil }
8
8
  subject{ AsyncCall.new(obj, method, *args, &block) }
9
- after{ subject.kill! }
10
- describe '#thread' do
11
- it 'should be a thread' do
12
- expect(subject.thread).to be_a Thread
13
- end
14
- end
15
9
  describe '#ready' do
16
10
  let(:method){ :test }
17
11
  before do
18
12
  stub(obj).test do
19
- sleep 0.1
20
13
  end
21
14
  end
22
15
  it 'should be true once the response is ready' do
23
16
  expect(subject).to_not be_ready
24
- sleep 0.11
17
+ subject.run
25
18
  expect(subject).to be_ready
26
19
  end
27
20
  end
@@ -29,28 +22,21 @@ describe AsyncCall do
29
22
  let(:obj){ Object }
30
23
  let(:method){ :name }
31
24
  it 'returns the value' do
25
+ subject.run
32
26
  expect(subject.wait_for_response).to eq 'Object'
33
27
  end
34
28
  context 'with a block' do
35
- before do
36
- stub(obj).name { sleep 0.3 }
37
- end
38
29
  it 'calls the block repeatedly while waiting' do
39
30
  mock(obj).foo.at_least(2)
40
- subject.wait_for_response { obj.foo }
31
+ begin
32
+ subject.wait_for_response(0.5) { obj.foo }
33
+ rescue Timeout::Error
34
+ end
41
35
  end
42
36
  end
43
37
  context 'when the timeout expires' do
44
- let(:method){ :test }
45
- before do
46
- stub(obj).test do
47
- sleep 1
48
- end
49
- end
50
- it 'raises an error and kills the thread' do
38
+ it 'raises an error' do
51
39
  expect{ subject.wait_for_response(0.01) }.to raise_error Timeout::Error
52
- sleep 0.1
53
- expect(subject.thread).to_not be_alive
54
40
  end
55
41
  end
56
42
  context 'when the call raises an exception' do
@@ -61,21 +47,11 @@ describe AsyncCall do
61
47
  end
62
48
  end
63
49
  it 'also raises the exception' do
50
+ subject.run
64
51
  expect{ subject.wait_for_response }.to raise_error ZeroDivisionError
65
52
  end
66
53
  end
67
54
  end
68
- describe '#kill!' do
69
- let(:method){ :test }
70
- before do
71
- stub(obj).test do
72
- sleep 1
73
- end
74
- end
75
- it 'kills the thread' do
76
- expect{ subject.kill!; sleep 0.01 }.to change{ subject.thread.stop? }.from(false).to(true)
77
- end
78
- end
79
55
  describe '#to_s' do
80
56
  let(:obj) { Object.new }
81
57
  let(:method) { :foo }
@@ -2,7 +2,8 @@ require File.expand_path(File.dirname(__FILE__) + '../../spec_helper')
2
2
 
3
3
  describe Capybarbecue::AsyncDelegateClass do
4
4
  let(:obj){ Object.new }
5
- subject{ Capybarbecue::AsyncDelegateClass.new(obj) }
5
+ let(:executer){ Capybarbecue::AsyncExecuter.new }
6
+ subject{ Capybarbecue::AsyncDelegateClass.new(obj, executer) }
6
7
 
7
8
  it 'calls the method on the session asynchronously' do
8
9
  mock(obj).foo{ 'cats' }
@@ -19,18 +20,19 @@ describe Capybarbecue::AsyncDelegateClass do
19
20
  context 'when the object returned is a Capybara object' do
20
21
  before { mock(obj).foo{ Capybara::Driver::Base.new } }
21
22
  it 'wraps the return value in AsyncDelegateClass' do
22
- expect(subject.foo).to be_an_instance_of Capybarbecue::AsyncDelegateClass
23
+ expect(subject.foo).to respond_to :__async_delegate__
23
24
  end
24
25
  context 'when a block is given' do
25
26
  subject do
26
- Capybarbecue::AsyncDelegateClass.new(obj) do
27
+ Capybarbecue::AsyncDelegateClass.new(obj, executer) do
27
28
  obj.wait_func
28
29
  end
29
30
  end
30
31
  it 'preserves the wait_proc' do
31
- mock(obj).wait_func.twice
32
+ stub(obj).wait_func
32
33
  resp = subject.foo
33
34
  stub(resp.instance).foo
35
+ mock(obj).wait_func
34
36
  resp.foo
35
37
  end
36
38
  end
@@ -49,9 +51,25 @@ describe Capybarbecue::AsyncDelegateClass do
49
51
  end
50
52
  end
51
53
 
54
+ describe 'instance_methods of object' do
55
+ let(:obj){ Hash.new }
56
+ it 'delegates these to the wrapped object' do
57
+ expect(subject).to be_a Hash
58
+ expect(subject).to be_a_kind_of Hash
59
+ expect(subject).to be_an_instance_of Hash
60
+ end
61
+ end
62
+
63
+ describe '#__async_delegate__' do
64
+ it 'is defined and returns true' do
65
+ expect(subject).to respond_to :__async_delegate__
66
+ expect(subject.__async_delegate__).to be_true
67
+ end
68
+ end
69
+
52
70
  context 'when a block is given' do
53
71
  subject do
54
- Capybarbecue::AsyncDelegateClass.new(obj) do
72
+ Capybarbecue::AsyncDelegateClass.new(obj, executer) do
55
73
  obj.wait_func
56
74
  end
57
75
  end
@@ -0,0 +1,39 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
+
3
+ describe Capybarbecue::AsyncExecuter do
4
+ subject { Capybarbecue::AsyncExecuter.new }
5
+ describe '#thread' do
6
+ it 'should be alive' do
7
+ subject
8
+ sleep 0.01
9
+ expect(subject.thread).to be_alive
10
+ end
11
+ end
12
+ describe '#execute' do
13
+ let(:async_call){ AsyncCall.new(Object, :name) }
14
+ it 'eventually executes the call' do
15
+ subject.execute(async_call)
16
+ expect(async_call.wait_for_response(0.1)).to eq 'Object'
17
+ end
18
+ context 'with nested calls' do
19
+ let(:obj) do
20
+ Object.new.tap do |o|
21
+ stub(o).foo do
22
+ subject.execute(async_call)
23
+ async_call.wait_for_response(0.1)
24
+ end
25
+ end
26
+ end
27
+ let(:first_async_call){ AsyncCall.new(obj, :foo) }
28
+ it 'executes both calls without deadlocking' do
29
+ subject.execute(first_async_call)
30
+ expect(async_call.wait_for_response(0.1)).to eq 'Object'
31
+ end
32
+ end
33
+ end
34
+ describe '#kill!' do
35
+ it 'kills the thread' do
36
+ expect{ subject.kill!; sleep 0.01 }.to change{ subject.thread.stop? }.from(false).to(true)
37
+ end
38
+ end
39
+ end
@@ -1,4 +1,5 @@
1
1
  require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
+ require 'rack'
2
3
 
3
4
  describe Capybarbecue::Server do
4
5
  let(:app) { Object.new }
@@ -18,6 +19,17 @@ describe Capybarbecue::Server do
18
19
  subject.call(request2)
19
20
  subject.handle_requests
20
21
  end
22
+ context 'when Rack returns a Rack::BodyProxy object' do
23
+ let(:body){ Rack::BodyProxy.new('body') }
24
+ before do
25
+ stub(app).call { [200, {}, body] }
26
+ subject.call(nil)
27
+ end
28
+ it 'closes the BodyProxy' do
29
+ mock(body).close
30
+ subject.handle_requests
31
+ end
32
+ end
21
33
  end
22
34
  describe '#call' do
23
35
  context 'when another thread handles the request' do
@@ -21,7 +21,7 @@ describe Capybarbecue do
21
21
  after{ Capybarbecue.deactivate! }
22
22
  it 'redefines Capybara#current_session' do
23
23
  subject
24
- expect(Capybara.current_session).to be_an_instance_of Capybarbecue::AsyncDelegateClass
24
+ expect(Capybara.current_session).to respond_to :__async_delegate__
25
25
  expect(Capybara.current_session.instance).to be Capybara.original_session
26
26
  end
27
27
  it 'saves old Capybara#current_session Capybara#original_session' do
@@ -111,6 +111,9 @@ describe 'With a real life app', :type => :feature, :capybara_feature => true do
111
111
  within 'div#div1' do
112
112
  expect(page).to have_css 'div.divclass2'
113
113
  end
114
+ within find('div#div1') do
115
+ expect(page).to have_css 'div.divclass2'
116
+ end
114
117
  within_fieldset 'fieldset1' do
115
118
  expect(page).to have_field 'file_field'
116
119
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: capybarbecue
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andrew DiMichele
@@ -80,20 +80,6 @@ dependencies:
80
80
  - - '>='
81
81
  - !ruby/object:Gem::Version
82
82
  version: '0'
83
- - !ruby/object:Gem::Dependency
84
- name: rdoc
85
- requirement: !ruby/object:Gem::Requirement
86
- requirements:
87
- - - '>='
88
- - !ruby/object:Gem::Version
89
- version: '0'
90
- type: :development
91
- prerelease: false
92
- version_requirements: !ruby/object:Gem::Requirement
93
- requirements:
94
- - - '>='
95
- - !ruby/object:Gem::Version
96
- version: '0'
97
83
  - !ruby/object:Gem::Dependency
98
84
  name: bundler
99
85
  requirement: !ruby/object:Gem::Requirement
@@ -108,20 +94,6 @@ dependencies:
108
94
  - - '>='
109
95
  - !ruby/object:Gem::Version
110
96
  version: '0'
111
- - !ruby/object:Gem::Dependency
112
- name: jeweler
113
- requirement: !ruby/object:Gem::Requirement
114
- requirements:
115
- - - '>='
116
- - !ruby/object:Gem::Version
117
- version: '0'
118
- type: :development
119
- prerelease: false
120
- version_requirements: !ruby/object:Gem::Requirement
121
- requirements:
122
- - - '>='
123
- - !ruby/object:Gem::Version
124
- version: '0'
125
97
  - !ruby/object:Gem::Dependency
126
98
  name: poltergeist
127
99
  requirement: !ruby/object:Gem::Requirement
@@ -155,33 +127,25 @@ description: Makes fundamental changes to Capybara's threading architecture so y
155
127
  email: backflip@gmail.com
156
128
  executables: []
157
129
  extensions: []
158
- extra_rdoc_files:
159
- - LICENSE.txt
160
- - README.rdoc
130
+ extra_rdoc_files: []
161
131
  files:
162
- - .document
163
- - .rspec
164
- - Gemfile
165
- - Gemfile.lock
166
- - LICENSE.txt
167
- - NOTES.txt
168
- - README.rdoc
169
- - Rakefile
170
- - lib/capybarbecue.rb
171
132
  - lib/capybarbecue/async_call.rb
172
133
  - lib/capybarbecue/async_delegate_class.rb
173
- - lib/capybarbecue/rack_runner.rb
134
+ - lib/capybarbecue/async_executer.rb
174
135
  - lib/capybarbecue/server.rb
175
136
  - lib/capybarbecue/version.rb
137
+ - lib/capybarbecue.rb
176
138
  - spec/capybarbecue/async_call_spec.rb
177
139
  - spec/capybarbecue/async_delegate_class_spec.rb
178
- - spec/capybarbecue/rack_runner_spec.rb
140
+ - spec/capybarbecue/async_executer_spec.rb
179
141
  - spec/capybarbecue/server_spec.rb
180
142
  - spec/capybarbecue_spec.rb
181
143
  - spec/integration/element_spec.rb
182
144
  - spec/integration/session_spec.rb
183
145
  - spec/spec_helper.rb
184
146
  - spec/support/test_rack_app.rb
147
+ - README.md
148
+ - LICENSE.txt
185
149
  homepage: http://github.com/adimichele/capybarbecue
186
150
  licenses:
187
151
  - MIT
@@ -194,7 +158,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
194
158
  requirements:
195
159
  - - '>='
196
160
  - !ruby/object:Gem::Version
197
- version: '0'
161
+ version: 1.9.3
198
162
  required_rubygems_version: !ruby/object:Gem::Requirement
199
163
  requirements:
200
164
  - - '>='
data/.document DELETED
@@ -1,5 +0,0 @@
1
- lib/**/*.rb
2
- bin/*
3
- -
4
- features/**/*.feature
5
- LICENSE.txt
data/.rspec DELETED
@@ -1 +0,0 @@
1
- --color
data/Gemfile DELETED
@@ -1,15 +0,0 @@
1
- source "http://rubygems.org"
2
-
3
- group :development do
4
- gem "rspec"
5
- gem "rr", require: false
6
- gem "yard"
7
- gem "rdoc"
8
- gem "bundler"
9
- gem "jeweler"
10
- gem "poltergeist"
11
- gem "launchy"
12
- end
13
-
14
- gem "activesupport"
15
- gem "capybara", "~>2.1.0"
@@ -1,78 +0,0 @@
1
- GEM
2
- remote: http://rubygems.org/
3
- specs:
4
- activesupport (4.0.0)
5
- i18n (~> 0.6, >= 0.6.4)
6
- minitest (~> 4.2)
7
- multi_json (~> 1.3)
8
- thread_safe (~> 0.1)
9
- tzinfo (~> 0.3.37)
10
- addressable (2.3.5)
11
- atomic (1.1.10)
12
- capybara (2.1.0)
13
- mime-types (>= 1.16)
14
- nokogiri (>= 1.3.3)
15
- rack (>= 1.0.0)
16
- rack-test (>= 0.5.4)
17
- xpath (~> 2.0)
18
- diff-lcs (1.2.4)
19
- eventmachine (1.0.3)
20
- faye-websocket (0.4.7)
21
- eventmachine (>= 0.12.0)
22
- git (1.2.5)
23
- http_parser.rb (0.5.3)
24
- i18n (0.6.4)
25
- jeweler (1.8.4)
26
- bundler (~> 1.0)
27
- git (>= 1.2.5)
28
- rake
29
- rdoc
30
- json (1.8.0)
31
- launchy (2.3.0)
32
- addressable (~> 2.3)
33
- mime-types (1.23)
34
- mini_portile (0.5.1)
35
- minitest (4.7.5)
36
- multi_json (1.7.7)
37
- nokogiri (1.6.0)
38
- mini_portile (~> 0.5.0)
39
- poltergeist (1.3.0)
40
- capybara (~> 2.1.0)
41
- faye-websocket (>= 0.4.4, < 0.5.0)
42
- http_parser.rb (~> 0.5.3)
43
- rack (1.5.2)
44
- rack-test (0.6.2)
45
- rack (>= 1.0)
46
- rake (10.1.0)
47
- rdoc (4.0.1)
48
- json (~> 1.4)
49
- rr (1.1.1)
50
- rspec (2.14.1)
51
- rspec-core (~> 2.14.0)
52
- rspec-expectations (~> 2.14.0)
53
- rspec-mocks (~> 2.14.0)
54
- rspec-core (2.14.4)
55
- rspec-expectations (2.14.0)
56
- diff-lcs (>= 1.1.3, < 2.0)
57
- rspec-mocks (2.14.2)
58
- thread_safe (0.1.2)
59
- atomic
60
- tzinfo (0.3.37)
61
- xpath (2.0.0)
62
- nokogiri (~> 1.3)
63
- yard (0.8.7)
64
-
65
- PLATFORMS
66
- ruby
67
-
68
- DEPENDENCIES
69
- activesupport
70
- bundler
71
- capybara (~> 2.1.0)
72
- jeweler
73
- launchy
74
- poltergeist
75
- rdoc
76
- rr
77
- rspec
78
- yard
data/NOTES.txt DELETED
@@ -1,37 +0,0 @@
1
- = Rails Thread =
2
- Put a proxy class in front of Capybara::Driver::Base and Capybara::Driver::Node
3
- Capybara:Session#driver is the instance of driver
4
- Probably need to create a new driver which is a proxy for another driver
5
- Proxy class puts method_missing args on MQ to be sent to Driver thread
6
- Needs to yield to Driver thread and then execute the RackRunner
7
- RackRunner reads rack requests from a MQ, calling the rack endpoint with those; returns when done
8
- This should probably use the rack-test gem? This probably isn't what we want since this simulates a browser
9
-
10
- = Driver Thread =
11
- Reads RPCs off a MQ, calls the commands in the appropriate class, and puts the response on another MQ
12
- Part of the new driver passed to Capybara
13
- Should be able to use the Capybara driver of your choice
14
- How is a Node passed back to the Rails thread? Maybe it's OK for Node queries to go straight through to the webdriver?
15
- These could trigger web requests (like a click) that would not be processed... maybe wrap in the Barbecue driver
16
- so that the RackRunner is triggered appropriately? That still causes a problem when clicking a link since the
17
- webdriver might block... need to think about this some more
18
-
19
- = Capybara::Server =
20
- Rewrite this class entirely
21
- Should spawn a simple Rack webserver that just take the #call Args and puts them on the MQ
22
- Needs to block until MQ response is received
23
- Webserver could be Puma or EventMachine
24
-
25
-
26
- Notes:
27
- Queue class provided by 'thread' library
28
-
29
- ----- Latest Notes -----
30
- * Interface with Session instead
31
- * Don't try to mimic interfaces - just ferry calls over
32
- * Add field to the base node class so we can tell it's a node
33
- * If return value is a Node then wrap it
34
- * If return value is enumerable, wrap its Node values via #map
35
-
36
- ---- TODO ----
37
- * Raise errors during timeouts if Time is frozen
@@ -1,19 +0,0 @@
1
- = capybarbecue
2
-
3
- Description goes here.
4
-
5
- == Contributing to capybarbecue
6
-
7
- * Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet.
8
- * Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it.
9
- * Fork the project.
10
- * Start a feature/bugfix branch.
11
- * Commit and push until you are happy with your contribution.
12
- * Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
13
- * Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
14
-
15
- == Copyright
16
-
17
- Copyright (c) 2013 Andrew DiMichele. See LICENSE.txt for
18
- further details.
19
-
data/Rakefile DELETED
@@ -1,44 +0,0 @@
1
- # encoding: utf-8
2
-
3
- require 'rubygems'
4
- require 'bundler'
5
- begin
6
- Bundler.setup(:default, :development)
7
- rescue Bundler::BundlerError => e
8
- $stderr.puts e.message
9
- $stderr.puts "Run `bundle install` to install missing gems"
10
- exit e.status_code
11
- end
12
- require 'rake'
13
-
14
- require 'jeweler'
15
- require './lib/capybarbecue/version'
16
- Jeweler::Tasks.new do |gem|
17
- # gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
18
- gem.name = "capybarbecue"
19
- gem.homepage = "http://github.com/adimichele/capybarbecue"
20
- gem.license = "MIT"
21
- gem.summary = %Q{Makes your Capybara test suite work better}
22
- gem.description = %Q{Makes fundamental changes to Capybara's threading architecture so you can write stable tests with a shared database connection.}
23
- gem.email = "backflip@gmail.com"
24
- gem.authors = ["Andrew DiMichele"]
25
- gem.version = Capybarbecue::VERSION
26
- # dependencies defined in Gemfile
27
- end
28
- Jeweler::RubygemsDotOrgTasks.new
29
-
30
- require 'rspec/core'
31
- require 'rspec/core/rake_task'
32
- RSpec::Core::RakeTask.new(:spec) do |spec|
33
- spec.pattern = FileList['spec/**/*_spec.rb']
34
- end
35
-
36
- RSpec::Core::RakeTask.new(:rcov) do |spec|
37
- spec.pattern = 'spec/**/*_spec.rb'
38
- spec.rcov = true
39
- end
40
-
41
- task :default => :spec
42
-
43
- require 'yard'
44
- YARD::Rake::YardocTask.new
@@ -1,7 +0,0 @@
1
- module Capybarbecue
2
- class RackRunner
3
- # Rack runner portion of main thread
4
- # Reads rack requests of a message queue, runs them, and puts the result on another MQ
5
- # Should have some sort of delay or signaling system to eliminate race conditions when waiting for requests
6
- end
7
- end
@@ -1,4 +0,0 @@
1
- require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
-
3
- describe Capybarbecue::RackRunner do
4
- end