puffing-billy 2.1.0 → 2.4.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
  SHA256:
3
- metadata.gz: d6e9bb7a75fe76a33e705c89f01e30fa987d1df3f4cfee135bb53393f28a4398
4
- data.tar.gz: 47e189ad311796a8a192479fc60034d2651f1879d8e17c42afb174f6296a491f
3
+ metadata.gz: 98ce893c9461f4f8c8eedd69eee46870634f789dd03a2007370bb7b03361bc26
4
+ data.tar.gz: 35f7939ff8eb3cbccd4bf4e91ccefbddd1b4b999fddfbc86ec0ff82885ae0dad
5
5
  SHA512:
6
- metadata.gz: 21e38be3cbfe954494685117ec5828cc989b9d2997170f33bfe08e2259bfdaec52e3468e7483db6a8883f5505b06dd7b7a5c1d8ccfe81447ab866d5da94ef323
7
- data.tar.gz: 8fc3ba732c1ab60daa5599c0eb77e3c02d4a5a083b43941680e2798898ae6b11addd0f0060de6aa5cabb1c7f5cd2b598ca5d485fe6019da0e5ca12549a46037c
6
+ metadata.gz: 73d5bc111493abd8a2bf9157f9bc3b717f0d4bc2a9276a757280d6e6ad7b3e5a1acd90a30234e00bdd9245380f6d9c00aa9bb86c3ec6bd7eed46643af1b85451
7
+ data.tar.gz: a32ac78dec161016266bf1ac2d2cdedcb68247c03cea681f096dc1dd4eb73dd4d166f27173a9f8b0040df39fd168d97010960a074b8b23c49183bee6a737bd50
@@ -1,23 +1,36 @@
1
1
  language: ruby
2
- cache: bundler
3
- before_install:
4
- - gem install bundler -v '< 2'
5
- - export PHANTOMJS_VERSION='2.1.1'
6
- - export PHANTOMJS_URL='https://github.com/Medium/phantomjs'
7
- - export PHANTOMJS_URL+="/releases/download/v${PHANTOMJS_VERSION}"
8
- - export PHANTOMJS_URL+="/phantomjs-${PHANTOMJS_VERSION}-linux-x86_64.tar.bz2"
9
- - >
10
- wget -q ${PHANTOMJS_URL} &&
11
- tar xfv phantomjs-${PHANTOMJS_VERSION}-linux-x86_64.tar.bz2 \
12
- --wildcards */bin/phantomjs --strip-components=2
13
- - export PATH="`pwd`:${PATH}"
14
- before_script:
15
- - phantomjs --version
16
- - bundle --version
2
+
17
3
  rvm:
18
4
  - 2.3
19
5
  - 2.4
20
6
  - 2.5
21
7
  - 2.6
22
- script: bundle exec rspec spec
8
+
9
+ cache:
10
+ bundler: true
11
+ directories:
12
+ - $HOME/.webdrivers
13
+
14
+ addons:
15
+ chrome: stable
16
+ apt:
17
+ update: true
18
+
19
+ sudo: false
20
+ dist: xenial
21
+
22
+ rvm:
23
+ - 2.3
24
+ - 2.4
25
+ - 2.5
26
+ - 2.6
27
+
28
+ matrix:
29
+ fast_finish: true
30
+
31
+ bundler_args: --jobs 3 --retry 3
32
+
33
+ before_install:
34
+ - gem update --system
35
+ - gem install bundler
23
36
 
@@ -1,3 +1,26 @@
1
+ v2.4.0, 2020-08-26
2
+ -------------------
3
+ * Make verify_peer configurable and default it to false [#294](https://github.com/oesmith/puffing-billy/pull/294)
4
+ * Include pid in names of temporary files [#290](https://github.com/oesmith/puffing-billy/pull/290)
5
+
6
+ v2.3.1, 2020-03-19
7
+ -------------------
8
+ * Update min 'faraday' gem version required [#285](https://github.com/oesmith/puffing-billy/pull/285)
9
+
10
+ v2.3.0, 2019-12-26
11
+ -------------------
12
+ * Add `cache_whitelist` config option [#279](https://github.com/oesmith/puffing-billy/pull/279)
13
+ * Ignore certificate errors in Chrome [#280](https://github.com/oesmith/puffing-billy/pull/280)
14
+
15
+ v2.2.0, 2019-10-26
16
+ -------------------
17
+ * Before handle request method [#273](https://github.com/oesmith/puffing-billy/pull/273)
18
+ * Add Selenium headless driver [#272](https://github.com/oesmith/puffing-billy/pull/272)
19
+
20
+ v2.1.1, 2019-06-15
21
+ -------------------
22
+ * Add driver registration for chrome headless [#267](https://github.com/oesmith/puffing-billy/pull/267)
23
+
1
24
  v2.1.0, 2019-03-17
2
25
  -------------------
3
26
  * Allow stubbing all request methods [#263](https://github.com/oesmith/puffing-billy/pull/263)
data/README.md CHANGED
@@ -14,14 +14,14 @@ Billy spawns an EventMachine-based proxy server, which it uses to intercept
14
14
  requests sent by your browser. It has a simple API for configuring which
15
15
  requests need stubbing and what they should return.
16
16
 
17
- Billy lets you test against known, repeatable data. It also allows you to
17
+ Billy lets you test against known, repeatable data. It also allows you to
18
18
  test for failure cases. Does your twitter (or facebook/google/etc)
19
- integration degrade gracefully when the API starts returning 500s? Well now
19
+ integration degrade gracefully when the API starts returning 500s? Well now
20
20
  you can test it!
21
21
 
22
22
  ```ruby
23
23
  it 'should stub google' do
24
- proxy.stub('http://www.google.com/').and_return(:text => "I'm not Google!")
24
+ proxy.stub('http://www.google.com/').and_return(text: "I'm not Google!")
25
25
  visit 'http://www.google.com/'
26
26
  expect(page).to have_content("I'm not Google!")
27
27
  end
@@ -32,21 +32,25 @@ You can also record HTTP interactions and replay them later. See
32
32
 
33
33
  ## Installation
34
34
 
35
- Add this line to your application's Gemfile:
35
+ Add this line to your application's `Gemfile`:
36
36
 
37
- gem 'puffing-billy'
37
+ ```ruby
38
+ gem 'puffing-billy', group: :test
39
+ ```
38
40
 
39
41
  And then execute:
40
42
 
41
- $ bundle
43
+ ```sh
44
+ $ bundle
45
+ ```
42
46
 
43
47
  Or install it yourself as:
44
48
 
45
- $ gem install puffing-billy
46
-
47
- ## RSpec Usage
49
+ ```sh
50
+ $ gem install puffing-billy
51
+ ```
48
52
 
49
- ### Setup for Capybara
53
+ ## Setup for Capybara
50
54
 
51
55
  In your `rails_helper.rb`:
52
56
 
@@ -55,7 +59,9 @@ require 'billy/capybara/rspec'
55
59
 
56
60
  # select a driver for your chosen browser environment
57
61
  Capybara.javascript_driver = :selenium_billy # Uses Firefox
62
+ # Capybara.javascript_driver = :selenium_headless_billy # Uses Firefox in headless mode
58
63
  # Capybara.javascript_driver = :selenium_chrome_billy
64
+ # Capybara.javascript_driver = :selenium_chrome_headless_billy
59
65
  # Capybara.javascript_driver = :apparition_billy
60
66
  # Capybara.javascript_driver = :webkit_billy
61
67
  # Capybara.javascript_driver = :poltergeist_billy
@@ -66,7 +72,7 @@ Capybara.javascript_driver = :selenium_billy # Uses Firefox
66
72
  headless specs when using puffing-billy for other local rack apps.
67
73
  See [this phantomjs issue](https://github.com/ariya/phantomjs/issues/11342) for any updates.
68
74
 
69
- ### Setup for Watir
75
+ ## Setup for Watir
70
76
 
71
77
  In your `rails_helper.rb`:
72
78
 
@@ -79,87 +85,7 @@ require 'billy/watir/rspec'
79
85
  # @browser = Billy::Browsers::Watir.new = :phantomjs
80
86
  ```
81
87
 
82
- ### In your tests (Capybara/Watir)
83
-
84
- ```ruby
85
- # Stub and return text, json, jsonp (or anything else)
86
- proxy.stub('http://example.com/text/').and_return(:text => 'Foobar')
87
- proxy.stub('http://example.com/json/').and_return(:json => { :foo => 'bar' })
88
- proxy.stub('http://example.com/jsonp/').and_return(:jsonp => { :foo => 'bar' })
89
- proxy.stub('http://example.com/headers/').and_return({
90
- :headers => { 'Access-Control-Allow-Origin' => '*' },
91
- :json => { :foo => 'bar' }
92
- })
93
- proxy.stub('http://example.com/wtf/').and_return(:body => 'WTF!?', :content_type => 'text/wtf')
94
-
95
- # Stub redirections and other return codes
96
- proxy.stub('http://example.com/redirect/').and_return(:redirect_to => 'http://example.com/other')
97
- proxy.stub('http://example.com/missing/').and_return(:code => 404, :body => 'Not found')
98
-
99
- # Even stub HTTPS!
100
- proxy.stub('https://example.com:443/secure/').and_return(:text => 'secrets!!1!')
101
-
102
- # Pass a Proc (or Proc-style object) to create dynamic responses.
103
- #
104
- # The proc will be called with the following arguments:
105
- # params: Query string parameters hash, CGI::escape-style
106
- # headers: Headers hash
107
- # body: Request body string
108
- # url: The actual URL which was requested
109
- # method: The HTTP verb which was requested
110
- proxy.stub('https://example.com/proc/').and_return(Proc.new { |params, headers, body, url, method|
111
- {
112
- :code => 200,
113
- :text => "Hello, #{params['name'][0]}"
114
- }
115
- })
116
-
117
- # You can also use Puffing Billy to intercept requests and responses. Just pass
118
- # a Proc and use the pass_request method. You can manipulate the request
119
- # (headers, URL, HTTP method, etc) and also the response from the upstream
120
- # server. The scope of the delivered callable is the user scope where
121
- # it was defined. Setting method to 'all' will intercept requests regardless of
122
- # the method.
123
- proxy.stub('http://example.com/', method => 'all').and_return(Proc.new { |*args|
124
- response = Billy.pass_request(*args)
125
- response[:headers]['Content-Type'] = 'text/plain'
126
- response[:body] = 'Hello World!'
127
- response[:code] = 200
128
- response
129
- })
130
-
131
- # Stub out a POST. Don't forget to allow a CORS request and set the method to 'post'
132
- proxy.stub('http://example.com/api', :method => 'post').and_return(
133
- :headers => { 'Access-Control-Allow-Origin' => '*' },
134
- :code => 201
135
- )
136
-
137
- # Stub out an OPTIONS request. Set the headers to the values you require.
138
- proxy.stub('http://example.com/api', :method => :options).and_return(
139
- :headers => {
140
- 'Access-Control-Allow-Methods' => 'GET, PATCH, POST, PUT, OPTIONS',
141
- 'Access-Control-Allow-Headers' => 'X-Requested-With, X-Prototype-Version, Content-Type',
142
- 'Access-Control-Allow-Origin' => '*'
143
- },
144
- :code => 200
145
- )
146
- ```
147
-
148
- Stubs are reset between tests. Any requests that are not stubbed will be
149
- proxied to the remote server.
150
-
151
- If for any reason you'd need to reset stubs manually you can do it in two ways:
152
-
153
- ```ruby
154
- # reset a single stub
155
- example_stub = proxy.stub('http://example.com/text/').and_return(:text => 'Foobar')
156
- proxy.unstub example_stub
157
-
158
- # reset all stubs
159
- proxy.reset
160
- ```
161
-
162
- ## Cucumber Usage
88
+ ## Setup for Cucumber
163
89
 
164
90
  An example feature:
165
91
 
@@ -171,7 +97,7 @@ Feature: Stubbing via billy
171
97
  And a stub for google
172
98
  ```
173
99
 
174
- ### Capybara
100
+ ### Setup for Cucumber + Capybara
175
101
 
176
102
  In your `features/support/env.rb`:
177
103
 
@@ -191,7 +117,7 @@ Before('@billy') do
191
117
  end
192
118
 
193
119
  And /^a stub for google$/ do
194
- proxy.stub('http://www.google.com/').and_return(:text => "I'm not Google!")
120
+ proxy.stub('http://www.google.com/').and_return(text: "I'm not Google!")
195
121
  visit 'http://www.google.com/'
196
122
  expect(page).to have_content("I'm not Google!")
197
123
  end
@@ -202,7 +128,7 @@ It's good practice to reset the driver after each scenario, so having an
202
128
  stubs are reset after each step, so any usage of a stub should be in the
203
129
  same step that it was created in.
204
130
 
205
- ### Watir
131
+ ### Setup for Cucumber + Watir
206
132
 
207
133
  In your `features/support/env.rb`:
208
134
 
@@ -222,7 +148,7 @@ Before('@billy') do
222
148
  end
223
149
 
224
150
  And /^a stub for google$/ do
225
- proxy.stub('http://www.google.com/').and_return(:text => "I'm not Google!")
151
+ proxy.stub('http://www.google.com/').and_return(text: "I'm not Google!")
226
152
  @browser.goto 'http://www.google.com/'
227
153
  expect(@browser.text).to eq("I'm not Google!")
228
154
  end
@@ -234,6 +160,86 @@ Please see [this link](https://gist.github.com/sauy7/1b081266dd453a1b737b) for
234
160
  details and report back to [Issue #49](https://github.com/oesmith/puffing-billy/issues/49)
235
161
  if you get it fully working.
236
162
 
163
+ ## Examples
164
+
165
+ ```ruby
166
+ # Stub and return text, json, jsonp (or anything else)
167
+ proxy.stub('http://example.com/text/').and_return(text: 'Foobar')
168
+ proxy.stub('http://example.com/json/').and_return(json: { foo: 'bar' })
169
+ proxy.stub('http://example.com/jsonp/').and_return(jsonp: { foo: 'bar' })
170
+ proxy.stub('http://example.com/headers/').and_return(
171
+ headers: { 'Access-Control-Allow-Origin' => '*' },
172
+ json: { foo: 'bar' }
173
+ )
174
+ proxy.stub('http://example.com/wtf/').and_return(body: 'WTF!?', content_type: 'text/wtf')
175
+
176
+ # Stub redirections and other return codes
177
+ proxy.stub('http://example.com/redirect/').and_return(redirect_to: 'http://example.com/other')
178
+ proxy.stub('http://example.com/missing/').and_return(code: 404, body: 'Not found')
179
+
180
+ # Even stub HTTPS!
181
+ proxy.stub('https://example.com:443/secure/').and_return(text: 'secrets!!1!')
182
+
183
+ # Pass a Proc (or Proc-style object) to create dynamic responses.
184
+ #
185
+ # The proc will be called with the following arguments:
186
+ # params: Query string parameters hash, CGI::escape-style
187
+ # headers: Headers hash
188
+ # body: Request body string
189
+ # url: The actual URL which was requested
190
+ # method: The HTTP verb which was requested
191
+ proxy.stub('https://example.com/proc/').and_return(Proc.new { |params, headers, body, url, method|
192
+ {
193
+ code: 200,
194
+ text: "Hello, #{params['name'][0]}"
195
+ }
196
+ })
197
+
198
+ # You can also use Puffing Billy to intercept requests and responses. Just pass
199
+ # a Proc and use the pass_request method. You can manipulate the request
200
+ # (headers, URL, HTTP method, etc) and also the response from the upstream
201
+ # server. The scope of the delivered callable is the user scope where
202
+ # it was defined. Setting method to 'all' will intercept requests regardless of
203
+ # the method.
204
+ proxy.stub('http://example.com/', method: 'all').and_return(Proc.new { |*args|
205
+ response = Billy.pass_request(*args)
206
+ response[:headers]['Content-Type'] = 'text/plain'
207
+ response[:body] = 'Hello World!'
208
+ response[:code] = 200
209
+ response
210
+ })
211
+
212
+ # Stub out a POST. Don't forget to allow a CORS request and set the method to 'post'
213
+ proxy.stub('http://example.com/api', method: 'post').and_return(
214
+ headers: { 'Access-Control-Allow-Origin' => '*' },
215
+ code: 201
216
+ )
217
+
218
+ # Stub out an OPTIONS request. Set the headers to the values you require.
219
+ proxy.stub('http://example.com/api', method: 'options').and_return(
220
+ headers: {
221
+ 'Access-Control-Allow-Methods' => 'GET, PATCH, POST, PUT, OPTIONS',
222
+ 'Access-Control-Allow-Headers' => 'X-Requested-With, X-Prototype-Version, Content-Type',
223
+ 'Access-Control-Allow-Origin' => '*'
224
+ },
225
+ code: 200
226
+ )
227
+ ```
228
+
229
+ Stubs are reset between tests. Any requests that are not stubbed will be
230
+ proxied to the remote server.
231
+
232
+ If for any reason you'd need to reset stubs manually you can do it in two ways:
233
+
234
+ ```ruby
235
+ # reset a single stub
236
+ example_stub = proxy.stub('http://example.com/text/').and_return(text: 'Foobar')
237
+ proxy.unstub example_stub
238
+
239
+ # reset all stubs
240
+ proxy.reset
241
+ ```
242
+
237
243
  ## Caching
238
244
 
239
245
  Requests routed through the external proxy are cached.
@@ -246,7 +252,8 @@ In your `rails_helper.rb`:
246
252
 
247
253
  ```ruby
248
254
  Billy.configure do |c|
249
- c.whitelist = ['test.host', 'localhost', '127.0.0.1']
255
+ c.whitelist = ['test.host', 'localhost', '127.0.0.1'] # To replace the default whitelist, OR
256
+ c.whitelist << 'mynewhost.local' # to append a host without overriding the defaults.
250
257
  end
251
258
  ```
252
259
 
@@ -260,6 +267,8 @@ server = Capybara.current_session.server
260
267
  Billy.config.whitelist = ["#{server.host}:#{server.port}"]
261
268
  ```
262
269
 
270
+ If you would like to cache whitelisted URLs, you can define them in `c.cache_whitelist`. This is useful for scenarios where you may want to set `c.non_whitelisted_requests_disabled` to `true` to only allow whitelisted URLs to be accessed, but still allow specific URLs to be treated as if they were non-whitelisted.
271
+
263
272
  If you want to use puffing-billy like you would [VCR](https://github.com/vcr/vcr)
264
273
  you can turn on cache persistence. This way you don't have to manually mock out
265
274
  everything as requests are automatically recorded and played back. With cache
@@ -289,6 +298,7 @@ Billy.configure do |c|
289
298
  c.proxy_port = 12345 # defaults to random
290
299
  c.proxied_request_host = nil
291
300
  c.proxied_request_port = 80
301
+ c.cache_whitelist = []
292
302
  c.record_requests = true # defaults to false
293
303
  c.cache_request_body_methods = ['post', 'patch', 'put'] # defaults to ['post']
294
304
  end
@@ -434,9 +444,6 @@ internally on this request, or your test ended before it could complete successf
434
444
 
435
445
  `c.after_cache_handles_request` is used to configure a callback that can operate on the response after it has been retrieved from the cache but before it is returned. The callback receives the request and response as arguments, with a request object like: `{ method: method, url: url, headers: headers, body: body }`. An example usage would be manipulating the Access-Control-Allow-Origin header so that your test server doesn't always have to run on the same port in order to accept cached responses to CORS requests:
436
446
 
437
- `c.use_ignore_params` is used to choose whether to use the ignore_params blacklist or the allow_params whitelist. Set to `true` to use `c.ignore_params`,
438
- `false` to use `c.allow_params`
439
-
440
447
  ```
441
448
  Billy.configure do |c|
442
449
  ...
@@ -454,6 +461,18 @@ Billy.configure do |c|
454
461
  end
455
462
  ```
456
463
 
464
+ `c.use_ignore_params` is used to choose whether to use the ignore_params blacklist or the allow_params whitelist. Set to `true` to use `c.ignore_params`,
465
+ `false` to use `c.allow_params`
466
+
467
+ `c.before_handle_request` is used to modify `method`, `url`, `headers`, `body` before handle request by `stubs`, `cache` or `proxy`. Method accept 4 argumens and must return array of this arguments:
468
+
469
+ ```
470
+ c.before_handle_request = proc { |method, url, headers, body|
471
+ filtered_body = JSON.dump(filter_secret_data(JSON.load(body)))
472
+ [method, url, headers, filtered_body]
473
+ }
474
+ ```
475
+
457
476
  `c.cache_simulates_network_delays` is used to add some delay before cache returns response. When set to `true`, cached requests will wait from configured delay time before responding. This allows to catch various race conditions in asynchronous front-end requests. The default is `false`.
458
477
 
459
478
  `c.cache_simulates_network_delay_time` is used to configure time (in seconds) to wait until responding from cache. The default is `0.1`.
@@ -524,20 +543,23 @@ end
524
543
  If you want the cache for each test to be independent, i.e. have it's own directory where the cache files are stored, you can do so.
525
544
 
526
545
  ### in Cucumber
527
- use a Before tag:
546
+
547
+ use a `Before` tag:
528
548
  ```rb
529
549
  Before('@javascript') do |scenario, block|
530
550
  Billy.configure do |c|
531
551
  feature_name = scenario.feature.name.underscore
532
552
  scenario_name = scenario.name.underscore
533
553
  c.cache_path = "features/support/fixtures/req_cache/#{feature_name}/#{scenario_name}/"
534
- Dir.mkdir_p(Billy.config.cache_path) unless File.exist?(Billy.config.cache_path)
554
+ FileUtils.mkdir_p(Billy.config.cache_path) unless File.exist?(Billy.config.cache_path)
535
555
  end
536
556
  end
537
557
  ```
538
558
 
539
559
  ### in Rspec
540
- use a before(:each) block:
560
+
561
+ use a `before(:each)` block:
562
+
541
563
  ```rb
542
564
  RSpec.configure do |config|
543
565
  base_cache_path = Billy.config.cache_path
@@ -558,7 +580,6 @@ RSpec.configure do |config|
558
580
  end
559
581
  ```
560
582
 
561
-
562
583
  ## Stub requests recording
563
584
 
564
585
  If you want to record requests to stubbed URIs, set the following configuration option:
@@ -602,6 +623,7 @@ and tell it to ignore SSL certificate warnings. See
602
623
  to see how Billy's default drivers are configured.
603
624
 
604
625
  ## Working with VCR and Webmock
626
+
605
627
  If you use VCR and Webmock elsewhere in your specs, you may need to disable them
606
628
  for your specs utilizing Puffing Billy. To do so, you can configure your `rails_helper.rb`
607
629
  as shown below:
@@ -631,7 +653,7 @@ Note that this approach may cause unexpected behavior if your backend sends the
631
653
 
632
654
  ### Raising errors from stubs
633
655
 
634
- By default PuffingBilly suppress errors from stub-blocks.
656
+ By default Puffing Billy suppresses errors from stub-blocks.
635
657
  To make it raise errors instead, add this test initializers:
636
658
 
637
659
  ```ruby
@@ -718,17 +740,24 @@ the system store. So after a run of your the suite only one certificate will be
718
740
  left over. If this is not enough you can handling the cleanup again with a
719
741
  custom on-after hook.
720
742
 
721
- ## Resources
743
+ ### TLS hostname validation
722
744
 
723
- * [Bring Ruby VCR to Javascript testing with Capybara and puffing-billy](http://architects.dzone.com/articles/bring-ruby-vcr-javascript)
724
- * [Integration Testing Stripe.js With Mocked Network Requests](http://dev.contractual.ly/testing-stripe-js-with-mocked-network/)
725
- * [Clean-up unused cache files periodically with this config](https://github.com/oesmith/puffing-billy/pull/26#issuecomment-29905030)
745
+ em-http-request was modified to emit a warning if being used without the TLS
746
+ ``verify_peer`` option. Puffing Billy defaults to specifying ``verify_peer: false``
747
+ but you can now modify configuration to do peer verification. So if you've
748
+ gone to the trouble of setting up your own certificate authority and self-signed
749
+ certs you can enable it like so:
726
750
 
727
- ## FAQ
751
+ ```ruby
752
+ Billy.configure do |c|
753
+ c.verify_peer = true
754
+ end
755
+ ```
728
756
 
729
- 1. Why name it after a train?
757
+ ## Resources
730
758
 
731
- Trains are *cool*.
759
+ * [Bring Ruby VCR to Javascript testing with Capybara and puffing-billy](https://dzone.com/articles/bring-ruby-vcr-javascript)
760
+ * [Clean-up unused cache files periodically with this config](https://github.com/oesmith/puffing-billy/pull/26#issuecomment-29905030)
732
761
 
733
762
  ## Contributing
734
763
 
@@ -740,5 +769,5 @@ custom on-after hook.
740
769
 
741
770
  ## TODO
742
771
 
743
- 1. Integration for test frameworks other than rspec.
772
+ 1. Integration for test frameworks other than RSpec.
744
773
  2. Show errors from the EventMachine reactor loop in the test output.
data/Rakefile CHANGED
@@ -1,2 +1,11 @@
1
1
  #!/usr/bin/env rake
2
2
  require 'bundler/gem_tasks'
3
+
4
+ begin
5
+ require 'rspec/core/rake_task'
6
+ RSpec::Core::RakeTask.new(:spec)
7
+ rescue LoadError
8
+ end
9
+
10
+ desc 'Run all tests'
11
+ task default: [:spec]
@@ -0,0 +1,22 @@
1
+ <!doctype html>
2
+ <body>
3
+ <h1>Latest news</h1>
4
+ <div id="news"></div>
5
+
6
+ <script type='text/javascript' src='http://code.jquery.com/jquery-1.8.2.min.js'></script>
7
+ <script type='text/javascript'>
8
+ $(function () {
9
+ var url = 'https://blog.howmanyleft.co.uk/api/read/json?callback=?&type=text&num=3&filter=text';
10
+ $.getJSON(url, function (data) {
11
+ $.each(data.posts, function (idx, post) {
12
+ var title = post['regular-title'];
13
+ var href = post['url-with-slug'];
14
+ var body = post['regular-body'];
15
+ $('#news').append(
16
+ '<h3><a href="' + href + '">' + title + '</a></h3>' +
17
+ '<p>' + body + '</p>');
18
+ });
19
+ });
20
+ })
21
+ </script>
22
+ </body>
@@ -47,21 +47,50 @@ module Billy
47
47
 
48
48
  def self.register_selenium_driver
49
49
  ::Capybara.register_driver :selenium_billy do |app|
50
- profile = Selenium::WebDriver::Firefox::Profile.new
51
- profile.assume_untrusted_certificate_issuer = false
52
- profile.proxy = Selenium::WebDriver::Proxy.new(
53
- http: "#{Billy.proxy.host}:#{Billy.proxy.port}",
54
- ssl: "#{Billy.proxy.host}:#{Billy.proxy.port}")
55
- ::Capybara::Selenium::Driver.new(app, profile: profile)
50
+ options = build_selenium_options_for_firefox
51
+ capabilities = Selenium::WebDriver::Remote::Capabilities.firefox(accept_insecure_certs: true)
52
+
53
+ ::Capybara::Selenium::Driver.new(app, options: options, desired_capabilities: capabilities)
54
+ end
55
+
56
+ ::Capybara.register_driver :selenium_headless_billy do |app|
57
+ options = build_selenium_options_for_firefox.tap do |opts|
58
+ opts.add_argument '-headless'
59
+ end
60
+ capabilities = Selenium::WebDriver::Remote::Capabilities.firefox(accept_insecure_certs: true)
61
+
62
+ ::Capybara::Selenium::Driver.new(app, options: options, desired_capabilities: capabilities)
56
63
  end
57
64
 
58
65
  ::Capybara.register_driver :selenium_chrome_billy do |app|
59
66
  options = Selenium::WebDriver::Chrome::Options.new
67
+ options.add_argument('--ignore-certificate-errors')
60
68
  options.add_argument("--proxy-server=#{Billy.proxy.host}:#{Billy.proxy.port}")
61
69
 
62
70
  ::Capybara::Selenium::Driver.new(
63
- app, browser: :chrome,
64
- options: options
71
+ app,
72
+ browser: :chrome,
73
+ options: options,
74
+ clear_local_storage: true,
75
+ clear_session_storage: true
76
+ )
77
+ end
78
+
79
+ ::Capybara.register_driver :selenium_chrome_headless_billy do |app|
80
+ options = Selenium::WebDriver::Chrome::Options.new
81
+ options.headless!
82
+ options.add_argument('--enable-features=NetworkService,NetworkServiceInProcess')
83
+ options.add_argument('--ignore-certificate-errors')
84
+ options.add_argument("--proxy-server=#{Billy.proxy.host}:#{Billy.proxy.port}")
85
+ options.add_argument('--disable-gpu') if Gem.win_platform?
86
+ options.add_argument('--no-sandbox') if ENV['CI']
87
+
88
+ ::Capybara::Selenium::Driver.new(
89
+ app,
90
+ browser: :chrome,
91
+ options: options,
92
+ clear_local_storage: true,
93
+ clear_session_storage: true
65
94
  )
66
95
  end
67
96
  end
@@ -73,6 +102,17 @@ module Billy
73
102
  end
74
103
  end
75
104
  end
105
+
106
+ def self.build_selenium_options_for_firefox
107
+ profile = Selenium::WebDriver::Firefox::Profile.new.tap do |prof|
108
+ prof.assume_untrusted_certificate_issuer = false
109
+ prof.proxy = Selenium::WebDriver::Proxy.new(
110
+ http: "#{Billy.proxy.host}:#{Billy.proxy.port}",
111
+ ssl: "#{Billy.proxy.host}:#{Billy.proxy.port}")
112
+ end
113
+
114
+ Selenium::WebDriver::Firefox::Options.new(profile: profile)
115
+ end
76
116
  end
77
117
  end
78
118
  end
@@ -19,6 +19,7 @@ module Billy
19
19
  private
20
20
 
21
21
  def configure_chrome(args)
22
+ args[:headless] = true
22
23
  args[:switches] ||= []
23
24
  args[:switches] += %W[--proxy-server=#{Billy.proxy.host}:#{Billy.proxy.port}]
24
25
  args
@@ -6,12 +6,12 @@ module Billy
6
6
  DEFAULT_WHITELIST = ['127.0.0.1', 'localhost']
7
7
  RANDOM_AVAILABLE_PORT = 0 # https://github.com/eventmachine/eventmachine/wiki/FAQ#wiki-can-i-start-a-server-on-a-random-available-port
8
8
 
9
- attr_accessor :logger, :cache, :cache_request_headers, :whitelist, :path_blacklist, :ignore_params, :allow_params,
9
+ attr_accessor :logger, :cache, :cache_request_headers, :whitelist, :cache_whitelist, :path_blacklist, :ignore_params, :allow_params,
10
10
  :persist_cache, :ignore_cache_port, :non_successful_cache_disabled, :non_successful_error_level,
11
- :non_whitelisted_requests_disabled, :cache_path, :certs_path, :proxy_host, :proxy_port, :proxied_request_inactivity_timeout,
11
+ :non_whitelisted_requests_disabled, :cache_path, :certs_path, :verify_peer, :proxy_host, :proxy_port, :proxied_request_inactivity_timeout,
12
12
  :proxied_request_connect_timeout, :dynamic_jsonp, :dynamic_jsonp_keys, :dynamic_jsonp_callback_name, :merge_cached_responses_whitelist,
13
13
  :strip_query_params, :proxied_request_host, :proxied_request_port, :cache_request_body_methods, :after_cache_handles_request,
14
- :cache_simulates_network_delays, :cache_simulates_network_delay_time, :record_requests, :record_stub_requests, :use_ignore_params
14
+ :cache_simulates_network_delays, :cache_simulates_network_delay_time, :record_requests, :record_stub_requests, :use_ignore_params, :before_handle_request
15
15
 
16
16
  def initialize
17
17
  @logger = defined?(Rails) ? Rails.logger : Logger.new(STDOUT)
@@ -21,6 +21,7 @@ module Billy
21
21
  def reset
22
22
  @cache = true
23
23
  @cache_request_headers = false
24
+ @cache_whitelist = []
24
25
  @whitelist = DEFAULT_WHITELIST
25
26
  @path_blacklist = []
26
27
  @merge_cached_responses_whitelist = []
@@ -36,6 +37,7 @@ module Billy
36
37
  @non_whitelisted_requests_disabled = false
37
38
  @cache_path = File.join(Dir.tmpdir, 'puffing-billy')
38
39
  @certs_path = File.join(Dir.tmpdir, 'puffing-billy', 'certs')
40
+ @verify_peer = false
39
41
  @proxy_host = 'localhost'
40
42
  @proxy_port = RANDOM_AVAILABLE_PORT
41
43
  @proxied_request_inactivity_timeout = 10 # defaults from https://github.com/igrigorik/em-http-request/wiki/Redirects-and-Timeouts
@@ -50,6 +52,7 @@ module Billy
50
52
  @record_requests = false
51
53
  @record_stub_requests = false
52
54
  @use_ignore_params = true
55
+ @before_handle_request = nil
53
56
  end
54
57
  end
55
58
 
@@ -16,6 +16,10 @@ module Billy
16
16
  opts = { inactivity_timeout: Billy.config.proxied_request_inactivity_timeout,
17
17
  connect_timeout: Billy.config.proxied_request_connect_timeout }
18
18
 
19
+ if url =~ /^https/
20
+ opts.merge!({tls: {verify_peer: Billy.config.verify_peer}})
21
+ end
22
+
19
23
  if Billy.config.proxied_request_host && !bypass_internal_proxy?(url)
20
24
  opts.merge!({ proxy: { host: Billy.config.proxied_request_host,
21
25
  port: Billy.config.proxied_request_port }} )
@@ -110,7 +114,7 @@ module Billy
110
114
 
111
115
  url = Addressable::URI.parse(url)
112
116
  # Cache the responses if they aren't whitelisted host[:port]s but always cache blacklisted paths on any hosts
113
- cacheable_status?(status) && (!whitelisted_url?(url) || blacklisted_path?(url.path))
117
+ cacheable_status?(status) && (!whitelisted_url?(url) || blacklisted_path?(url.path) || cache_whitelisted_url?(url))
114
118
  end
115
119
 
116
120
  def whitelisted_url?(url)
@@ -123,6 +127,16 @@ module Billy
123
127
  end
124
128
  end
125
129
 
130
+ def cache_whitelisted_url?(url)
131
+ Billy.config.cache_whitelist.any? do |value|
132
+ if value.is_a?(Regexp)
133
+ url.to_s =~ value || url.omit(:port).to_s =~ value
134
+ else
135
+ value =~ /^#{url.host}(?::#{url.port})?$/
136
+ end
137
+ end
138
+ end
139
+
126
140
  def blacklisted_path?(path)
127
141
  !Billy.config.path_blacklist.index { |bl| bl.is_a?(Regexp) ? path =~ bl : path.include?(bl) }.nil?
128
142
  end
@@ -17,6 +17,10 @@ module Billy
17
17
  def handle_request(method, url, headers, body)
18
18
  request = request_log.record(method, url, headers, body)
19
19
 
20
+ if Billy.config.before_handle_request
21
+ method, url, headers, body = Billy.config.before_handle_request.call(method, url, headers, body)
22
+ end
23
+
20
24
  # Process the handlers by order of importance
21
25
  [:stubs, :cache, :proxy].each do |key|
22
26
  if (response = handlers[key].handle_request(method, url, headers, body))
@@ -27,7 +27,7 @@ module Billy
27
27
  # and ensure the location is safely created. Pass
28
28
  # back the resulting path.
29
29
  def write_file(name, contents)
30
- path = File.join(Billy.config.certs_path, name)
30
+ path = File.join(Billy.config.certs_path, "#{Process.pid}-#{name}")
31
31
  FileUtils.mkdir_p(File.dirname(path))
32
32
  File.write(path, contents)
33
33
  path
@@ -1,3 +1,3 @@
1
1
  module Billy
2
- VERSION = '2.1.0'
2
+ VERSION = '2.4.0'
3
3
  end
@@ -17,19 +17,18 @@ Gem::Specification.new do |gem|
17
17
 
18
18
  gem.add_development_dependency 'rspec'
19
19
  gem.add_development_dependency 'thin'
20
- gem.add_development_dependency 'faraday'
21
- gem.add_development_dependency 'poltergeist'
20
+ gem.add_development_dependency 'faraday', '>= 0.9.0'
22
21
  gem.add_development_dependency 'apparition'
23
- # selenium-webdriver 3.8 drops support for PhantomJS
24
- gem.add_development_dependency 'selenium-webdriver', '<= 3.7.0'
25
22
  gem.add_development_dependency 'capybara'
26
- gem.add_development_dependency 'capybara-webkit', '~> 1.0'
23
+ gem.add_development_dependency 'selenium-webdriver'
27
24
  gem.add_development_dependency 'rack'
25
+ gem.add_development_dependency 'rake'
28
26
  gem.add_development_dependency 'guard'
29
27
  gem.add_development_dependency 'rb-inotify'
30
28
  gem.add_development_dependency 'pry'
31
29
  gem.add_development_dependency 'cucumber'
32
30
  gem.add_development_dependency 'watir', '~> 6.10.0'
31
+ gem.add_development_dependency 'webdrivers'
33
32
  gem.add_runtime_dependency 'addressable', '~> 2.5'
34
33
  gem.add_runtime_dependency 'eventmachine', '~> 1.2'
35
34
  gem.add_runtime_dependency 'em-synchrony'
@@ -0,0 +1,28 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'Capybara drivers', type: :feature, js: true do
4
+ it 'allows HTTPS calls' do
5
+ proxy.stub('https://blog.howmanyleft.co.uk:443/api/read/json').and_return(
6
+ jsonp: {
7
+ posts: [
8
+ {
9
+ 'regular-title' => 'News Item 1',
10
+ 'url-with-slug' => 'http://example.com/news/1',
11
+ 'regular-body' => 'News item 1 content here'
12
+ },
13
+ {
14
+ 'regular-title' => 'News Item 2',
15
+ 'url-with-slug' => 'http://example.com/news/2',
16
+ 'regular-body' => 'News item 2 content here'
17
+ }
18
+ ]
19
+ })
20
+
21
+ visit '/tumblr_api_https.html'
22
+
23
+ expect(page).to have_link('News Item 1', href: 'http://example.com/news/1')
24
+ expect(page).to have_content('News item 1 content here')
25
+ expect(page).to have_link('News Item 2', href: 'http://example.com/news/2')
26
+ expect(page).to have_content('News item 2 content here')
27
+ end
28
+ end
@@ -175,6 +175,34 @@ describe Billy::ProxyHandler do
175
175
  request[:body])
176
176
  end
177
177
 
178
+ it 'does NOT cache the response if the host is whitelisted but not cache_whitelisted' do
179
+ uri = Addressable::URI.parse(request[:url])
180
+
181
+ expect(subject).to receive(:allowed_response_code?).and_return(true)
182
+ expect(Billy.config).to receive(:whitelist).and_return([uri.host])
183
+ expect(Billy.config).to receive(:cache_whitelist).and_return([])
184
+
185
+ expect(Billy::Cache.instance).not_to receive(:store)
186
+ subject.handle_request(request[:method],
187
+ request[:url],
188
+ request[:headers],
189
+ request[:body])
190
+ end
191
+
192
+ it 'caches the response if the host is whitelisted AND cache_whitelisted' do
193
+ uri = Addressable::URI.parse(request[:url])
194
+
195
+ expect(subject).to receive(:allowed_response_code?).and_return(true)
196
+ expect(Billy.config).to receive(:whitelist).and_return([uri.host])
197
+ expect(Billy.config).to receive(:cache_whitelist).and_return([uri.host])
198
+
199
+ expect(Billy::Cache.instance).to receive(:store)
200
+ subject.handle_request(request[:method],
201
+ request[:url],
202
+ request[:headers],
203
+ request[:body])
204
+ end
205
+
178
206
  it 'uses the timeouts defined in configuration' do
179
207
  allow(Billy.config).to receive(:proxied_request_inactivity_timeout).and_return(42)
180
208
  allow(Billy.config).to receive(:proxied_request_connect_timeout).and_return(24)
@@ -128,6 +128,28 @@ describe Billy::RequestHandler do
128
128
  allow(stub_handler).to receive(:handle_request).and_raise("Any Stub Error")
129
129
  expect(subject.handle_request(*args)).to eql(error: "Any Stub Error")
130
130
  end
131
+
132
+ context 'before_handle_request activated' do
133
+ before do
134
+ handle_request = proc { |method, url, headers, body|
135
+ [method, url, headers, "#{body}_modified"]
136
+ }
137
+ allow(Billy::config).to receive(:before_handle_request).and_return(handle_request)
138
+ end
139
+
140
+ after do
141
+ allow(Billy::config).to receive(:before_handle_request).and_call_original
142
+ end
143
+
144
+ it 'modify request before handling' do
145
+ new_args = %w(get url headers body_modified)
146
+ expect(stub_handler).to receive(:handle_request).with(*new_args)
147
+ expect(cache_handler).to receive(:handle_request).with(*new_args).and_return('bar')
148
+ expect(proxy_handler).to_not receive(:handle_request)
149
+ expect(subject.handle_request(*args)).to eql 'bar'
150
+ expect(subject.requests).to eql([{status: :complete, handler: :cache, method: 'get', url: 'url', headers: 'headers', body: 'body'}])
151
+ end
152
+ end
131
153
  end
132
154
 
133
155
  describe '#stubs' do
@@ -118,6 +118,29 @@ shared_examples_for 'a cache' do
118
118
  end
119
119
  end
120
120
 
121
+ context 'cache_whitelist GET requests' do
122
+ before do
123
+ Billy.config.whitelist = [http.host]
124
+ Billy.config.cache_whitelist = [http.host]
125
+ end
126
+
127
+ it 'should be cached' do
128
+ assert_cached_url
129
+ end
130
+
131
+ context 'with ports' do
132
+ before do
133
+ rack_app_url = URI(http.url_prefix)
134
+ Billy.config.whitelist = ["#{rack_app_url.host}:#{rack_app_url.port + 1}"]
135
+ Billy.config.cache_whitelist = Billy.config.whitelist
136
+ end
137
+
138
+ it 'should be cached' do
139
+ assert_cached_url
140
+ end
141
+ end
142
+ end
143
+
121
144
  context 'ignore_params GET requests' do
122
145
  before do
123
146
  Billy.config.ignore_params = ['/analytics']
@@ -232,7 +255,7 @@ shared_examples_for 'a cache' do
232
255
 
233
256
  it 'should raise error when disabled' do
234
257
  # TODO: Suppress stderr output: https://gist.github.com/adamstegman/926858
235
- expect { http.get('/foo') }.to raise_error(Faraday::Error::ConnectionFailed, 'end of file reached')
258
+ expect { http.get('/foo') }.to raise_error(Faraday::ConnectionFailed, 'end of file reached')
236
259
  end
237
260
  end
238
261
 
@@ -261,7 +284,7 @@ shared_examples_for 'a cache' do
261
284
  end
262
285
 
263
286
  it 'should raise error for non-successful responses when :error' do
264
- expect { http_error.get('/foo') }.to raise_error(Faraday::Error::ConnectionFailed)
287
+ expect { http_error.get('/foo') }.to raise_error(Faraday::ConnectionFailed)
265
288
  end
266
289
  end
267
290
  end
@@ -6,11 +6,17 @@ require 'billy/watir/rspec'
6
6
  require 'rack'
7
7
  require 'logger'
8
8
  require 'fileutils'
9
+ require 'webdrivers'
9
10
 
10
- browser = Billy::Browsers::Watir.new :phantomjs
11
- Capybara.app = Rack::Directory.new(File.expand_path('../../examples', __FILE__))
12
- Capybara.server = :webrick
13
- Capybara.javascript_driver = :poltergeist_billy
11
+ $stdout.puts `#{::Selenium::WebDriver::Chrome::Service.driver_path.call} --version` if ENV['CI']
12
+
13
+ browser = Billy::Browsers::Watir.new :chrome
14
+
15
+ Capybara.configure do |config|
16
+ config.app = Rack::Directory.new(File.expand_path('../../examples', __FILE__))
17
+ config.server = :webrick
18
+ config.javascript_driver = :selenium_chrome_headless_billy
19
+ end
14
20
 
15
21
  Billy.configure do |config|
16
22
  config.logger = Logger.new(File.expand_path('../../log/test.log', __FILE__))
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: puffing-billy
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.1.0
4
+ version: 2.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Olly Smith
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-03-17 00:00:00.000000000 Z
11
+ date: 2020-08-27 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rspec
@@ -44,16 +44,16 @@ dependencies:
44
44
  requirements:
45
45
  - - ">="
46
46
  - !ruby/object:Gem::Version
47
- version: '0'
47
+ version: 0.9.0
48
48
  type: :development
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
52
  - - ">="
53
53
  - !ruby/object:Gem::Version
54
- version: '0'
54
+ version: 0.9.0
55
55
  - !ruby/object:Gem::Dependency
56
- name: poltergeist
56
+ name: apparition
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
59
  - - ">="
@@ -67,7 +67,7 @@ dependencies:
67
67
  - !ruby/object:Gem::Version
68
68
  version: '0'
69
69
  - !ruby/object:Gem::Dependency
70
- name: apparition
70
+ name: capybara
71
71
  requirement: !ruby/object:Gem::Requirement
72
72
  requirements:
73
73
  - - ">="
@@ -82,20 +82,6 @@ dependencies:
82
82
  version: '0'
83
83
  - !ruby/object:Gem::Dependency
84
84
  name: selenium-webdriver
85
- requirement: !ruby/object:Gem::Requirement
86
- requirements:
87
- - - "<="
88
- - !ruby/object:Gem::Version
89
- version: 3.7.0
90
- type: :development
91
- prerelease: false
92
- version_requirements: !ruby/object:Gem::Requirement
93
- requirements:
94
- - - "<="
95
- - !ruby/object:Gem::Version
96
- version: 3.7.0
97
- - !ruby/object:Gem::Dependency
98
- name: capybara
99
85
  requirement: !ruby/object:Gem::Requirement
100
86
  requirements:
101
87
  - - ">="
@@ -109,21 +95,21 @@ dependencies:
109
95
  - !ruby/object:Gem::Version
110
96
  version: '0'
111
97
  - !ruby/object:Gem::Dependency
112
- name: capybara-webkit
98
+ name: rack
113
99
  requirement: !ruby/object:Gem::Requirement
114
100
  requirements:
115
- - - "~>"
101
+ - - ">="
116
102
  - !ruby/object:Gem::Version
117
- version: '1.0'
103
+ version: '0'
118
104
  type: :development
119
105
  prerelease: false
120
106
  version_requirements: !ruby/object:Gem::Requirement
121
107
  requirements:
122
- - - "~>"
108
+ - - ">="
123
109
  - !ruby/object:Gem::Version
124
- version: '1.0'
110
+ version: '0'
125
111
  - !ruby/object:Gem::Dependency
126
- name: rack
112
+ name: rake
127
113
  requirement: !ruby/object:Gem::Requirement
128
114
  requirements:
129
115
  - - ">="
@@ -206,6 +192,20 @@ dependencies:
206
192
  - - "~>"
207
193
  - !ruby/object:Gem::Version
208
194
  version: 6.10.0
195
+ - !ruby/object:Gem::Dependency
196
+ name: webdrivers
197
+ requirement: !ruby/object:Gem::Requirement
198
+ requirements:
199
+ - - ">="
200
+ - !ruby/object:Gem::Version
201
+ version: '0'
202
+ type: :development
203
+ prerelease: false
204
+ version_requirements: !ruby/object:Gem::Requirement
205
+ requirements:
206
+ - - ">="
207
+ - !ruby/object:Gem::Version
208
+ version: '0'
209
209
  - !ruby/object:Gem::Dependency
210
210
  name: addressable
211
211
  requirement: !ruby/object:Gem::Requirement
@@ -336,6 +336,7 @@ files:
336
336
  - examples/post_api.html
337
337
  - examples/preflight_request.html
338
338
  - examples/tumblr_api.html
339
+ - examples/tumblr_api_https.html
339
340
  - lib/billy.rb
340
341
  - lib/billy/browsers/capybara.rb
341
342
  - lib/billy/browsers/watir.rb
@@ -375,6 +376,7 @@ files:
375
376
  - spec/features/examples/post_api_spec.rb
376
377
  - spec/features/examples/preflight_request_spec.rb
377
378
  - spec/features/examples/tumblr_api_spec.rb
379
+ - spec/lib/billy/browsers/capybara_spec.rb
378
380
  - spec/lib/billy/cache_spec.rb
379
381
  - spec/lib/billy/handlers/cache_handler_spec.rb
380
382
  - spec/lib/billy/handlers/handler_spec.rb
@@ -421,6 +423,7 @@ test_files:
421
423
  - spec/features/examples/post_api_spec.rb
422
424
  - spec/features/examples/preflight_request_spec.rb
423
425
  - spec/features/examples/tumblr_api_spec.rb
426
+ - spec/lib/billy/browsers/capybara_spec.rb
424
427
  - spec/lib/billy/cache_spec.rb
425
428
  - spec/lib/billy/handlers/cache_handler_spec.rb
426
429
  - spec/lib/billy/handlers/handler_spec.rb