percy-capybara 0.3.0 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1 +1 @@
1
- <html><body>Hello World!</body><head></head></html>
1
+ <html><head></head><body>Hello World!</body></html>
@@ -28,5 +28,18 @@ RSpec.describe Percy::Capybara::Client do
28
28
  expect(Percy::Capybara::Client.new.enabled?).to be_truthy
29
29
  end
30
30
  end
31
+ describe '#initialize_loader' do
32
+ let(:capybara_client) { Percy::Capybara::Client.new }
33
+
34
+ it 'returns a NativeLoader if no sprockets config' do
35
+ expect(capybara_client.initialize_loader.class).to eq(Percy::Capybara::Loaders::NativeLoader)
36
+ end
37
+ it 'returns a SprocketsLoader if sprockets is configured' do
38
+ capybara_client.sprockets_environment = double('sprockets_environment')
39
+ capybara_client.sprockets_options = double('sprockets_options')
40
+ loader = capybara_client.initialize_loader
41
+ expect(loader.class).to eq(Percy::Capybara::Loaders::SprocketsLoader)
42
+ end
43
+ end
31
44
  end
32
45
 
@@ -0,0 +1,52 @@
1
+ RSpec.describe Percy::Capybara::Loaders::BaseLoader do
2
+ let(:loader) { described_class.new }
3
+
4
+ describe '#root_html_resource', type: :feature, js: true do
5
+ it 'includes the root DOM HTML' do
6
+ visit '/'
7
+
8
+ loader = described_class.new(page: page)
9
+ resource = loader.root_html_resource
10
+
11
+ expect(resource.is_root).to be_truthy
12
+ expect(resource.mimetype).to eq('text/html')
13
+ expect(resource.resource_url).to match('/')
14
+ expect(resource.content).to include('Hello World!')
15
+ expect(resource.sha).to eq(Digest::SHA256.hexdigest(resource.content))
16
+ end
17
+ end
18
+ describe '#build_resources' do
19
+ it 'raises a NotImplementedError' do
20
+ expect { loader.build_resources }.to raise_error(NotImplementedError)
21
+ end
22
+ end
23
+ describe '#snapshot_resources' do
24
+ it 'raises a NotImplementedError' do
25
+ expect { loader.snapshot_resources }.to raise_error(NotImplementedError)
26
+ end
27
+ end
28
+ describe '#current_path' do
29
+ it 'returns the current path of the page, stripping the domain if it exists' do
30
+ page_double = double('page')
31
+
32
+ expect(page_double).to receive(:current_url).and_return('/')
33
+ loader = described_class.new(page: page_double)
34
+ expect(loader.current_path).to eq('/')
35
+
36
+ expect(page_double).to receive(:current_url).and_return('/test')
37
+ loader = described_class.new(page: page_double)
38
+ expect(loader.current_path).to eq('/test')
39
+
40
+ expect(page_double).to receive(:current_url).and_return('/test/a')
41
+ loader = described_class.new(page: page_double)
42
+ expect(loader.current_path).to eq('/test/a')
43
+
44
+ # Rack::Test returns a full example.com URL, so we want to make sure it is stripped:
45
+ expect(page_double).to receive(:current_url).and_return('http://www.example.com/')
46
+ loader = described_class.new(page: page_double)
47
+ expect(loader.current_path).to eq('/')
48
+ end
49
+ end
50
+ end
51
+
52
+
@@ -0,0 +1,224 @@
1
+ RSpec.describe Percy::Capybara::Loaders::NativeLoader do
2
+ let(:loader) { described_class.new(page: nil) }
3
+
4
+ describe '#build_resources' do
5
+ it 'returns an empty list' do
6
+ expect(loader.build_resources).to eq([])
7
+ end
8
+ end
9
+ describe '#snapshot_resources', type: :feature, js: true do
10
+ it 'returns the root HTML' do
11
+ visit '/'
12
+ loader = described_class.new(page: page)
13
+ expect(loader.snapshot_resources.collect(&:resource_url)).to match_array(['/'])
14
+ end
15
+ it 'returns the root HTML and CSS resources' do
16
+ visit '/test-css.html'
17
+ loader = described_class.new(page: page)
18
+ resource_urls = loader.snapshot_resources.collect(&:resource_url).map do |url|
19
+ url.gsub(/localhost:\d+/, 'localhost')
20
+ end
21
+ expect(resource_urls).to match_array([
22
+ "/test-css.html",
23
+ "http://localhost/css/base.css",
24
+ "http://localhost/css/imports.css",
25
+ "http://localhost/css/level0-imports.css",
26
+ "http://localhost/css/level1-imports.css",
27
+ "http://localhost/css/level2-imports.css",
28
+ "http://localhost/css/simple-imports.css",
29
+ ])
30
+ end
31
+ it 'returns the root HTML and image resources' do
32
+ visit '/test-images.html'
33
+ loader = described_class.new(page: page)
34
+ resource_urls = loader.snapshot_resources.collect(&:resource_url).map do |url|
35
+ url.gsub(/localhost:\d+/, 'localhost')
36
+ end
37
+ expect(resource_urls).to match_array([
38
+ "/test-images.html",
39
+ "http://localhost/images/img-relative.png",
40
+ "http://localhost/images/img-relative-to-root.png",
41
+ "http://localhost/images/percy.svg",
42
+ "http://localhost/images/srcset-base.png",
43
+ "http://localhost/images/srcset-first.png",
44
+ "http://localhost/images/srcset-second.png",
45
+ "http://localhost/images/bg-relative.png",
46
+ "http://localhost/images/bg-relative-to-root.png",
47
+ "http://localhost/images/bg-stacked.png"
48
+ ])
49
+ end
50
+ end
51
+ describe '#_should_include_url?' do
52
+ it 'returns true for valid, local URLs' do
53
+ expect(loader._should_include_url?('http://localhost/')).to eq(true)
54
+ expect(loader._should_include_url?('http://localhost:123/')).to eq(true)
55
+ expect(loader._should_include_url?('http://localhost/foo')).to eq(true)
56
+ expect(loader._should_include_url?('http://localhost:123/foo')).to eq(true)
57
+ expect(loader._should_include_url?('http://localhost/foo/test.html')).to eq(true)
58
+ expect(loader._should_include_url?('http://127.0.0.1/')).to eq(true)
59
+ expect(loader._should_include_url?('http://127.0.0.1:123/')).to eq(true)
60
+ expect(loader._should_include_url?('http://127.0.0.1/foo')).to eq(true)
61
+ expect(loader._should_include_url?('http://127.0.0.1:123/foo')).to eq(true)
62
+ expect(loader._should_include_url?('http://127.0.0.1/foo/test.html')).to eq(true)
63
+ expect(loader._should_include_url?('http://0.0.0.0/foo/test.html')).to eq(true)
64
+ # Also works for paths:
65
+ expect(loader._should_include_url?('/')).to eq(true)
66
+ expect(loader._should_include_url?('/foo')).to eq(true)
67
+ expect(loader._should_include_url?('/foo/test.png')).to eq(true)
68
+ end
69
+ it 'returns false for invalid URLs' do
70
+ expect(loader._should_include_url?('')).to eq(false)
71
+ expect(loader._should_include_url?('http://local host/foo')).to eq(false)
72
+ expect(loader._should_include_url?('bad-url/')).to eq(false)
73
+ expect(loader._should_include_url?('bad-url/foo/test.html')).to eq(false)
74
+ end
75
+ it 'returns false for data URLs' do
76
+ expect(loader._should_include_url?('data:image/gif;base64,R0')).to eq(false)
77
+ end
78
+ it 'returns false for remote URLs' do
79
+ expect(loader._should_include_url?('http://foo/')).to eq(false)
80
+ expect(loader._should_include_url?('http://example.com/')).to eq(false)
81
+ expect(loader._should_include_url?('http://example.com/foo')).to eq(false)
82
+ expect(loader._should_include_url?('https://example.com/foo')).to eq(false)
83
+ end
84
+ end
85
+ describe '#_get_css_resources', type: :feature, js: true do
86
+ it 'includes all linked and imported stylesheets' do
87
+ visit '/test-css.html'
88
+
89
+ loader = described_class.new(page: page)
90
+ resources = loader.send(:_get_css_resources)
91
+
92
+ resource = find_resource(resources, '/css/base.css')
93
+
94
+ expect(resource.content).to include('.colored-by-base { color: red; }')
95
+ expect(resource.sha).to eq(Digest::SHA256.hexdigest(resource.content))
96
+
97
+ resource = find_resource(resources, '/css/simple-imports.css')
98
+ expect(resource.content).to include("@import url('imports.css');")
99
+ expect(resource.sha).to eq(Digest::SHA256.hexdigest(resource.content))
100
+
101
+ resource = find_resource(resources, '/css/imports.css')
102
+ expect(resource.content).to include('.colored-by-imports { color: red; }')
103
+ expect(resource.sha).to eq(Digest::SHA256.hexdigest(resource.content))
104
+
105
+ resource = find_resource(resources, '/css/level0-imports.css')
106
+ expect(resource.content).to include("@import url('level1-imports.css')")
107
+ expect(resource.content).to include('.colored-by-level0-imports { color: red; }')
108
+ expect(resource.sha).to eq(Digest::SHA256.hexdigest(resource.content))
109
+
110
+ resource = find_resource(resources, '/css/level1-imports.css')
111
+ expect(resource.content).to include("@import url('level2-imports.css')")
112
+ expect(resource.content).to include('.colored-by-level1-imports { color: red; }')
113
+ expect(resource.sha).to eq(Digest::SHA256.hexdigest(resource.content))
114
+
115
+ resource = find_resource(resources, '/css/level2-imports.css')
116
+ expect(resource.content).to include(".colored-by-level2-imports { color: red; }")
117
+ expect(resource.sha).to eq(Digest::SHA256.hexdigest(resource.content))
118
+
119
+ expect(resources.length).to eq(6)
120
+ expect(resources.collect(&:mimetype).uniq).to eq(['text/css'])
121
+ expect(resources.collect(&:is_root).uniq).to match_array([nil])
122
+ end
123
+ end
124
+ describe '#_get_image_resources', type: :feature, js: true do
125
+ it 'includes all images' do
126
+ visit '/test-images.html'
127
+
128
+ loader = described_class.new(page: page)
129
+ resources = loader.send(:_get_image_resources)
130
+
131
+ # The order of these is just for convenience, they match the order in test-images.html.
132
+
133
+ resource = find_resource(resources, '/images/img-relative.png')
134
+ path = File.expand_path('../../client/testdata/images/img-relative.png', __FILE__)
135
+ content = File.read(path)
136
+ expect(resource.mimetype).to eq('image/png')
137
+ expected_sha = Digest::SHA256.hexdigest(content)
138
+ expect(Digest::SHA256.hexdigest(resource.content)).to eq(expected_sha)
139
+ expect(resource.sha).to eq(expected_sha)
140
+
141
+ resource = find_resource(resources, '/images/img-relative-to-root.png')
142
+ path = File.expand_path('../../client/testdata/images/img-relative-to-root.png', __FILE__)
143
+ content = File.read(path)
144
+ expect(resource.mimetype).to eq('image/png')
145
+ expected_sha = Digest::SHA256.hexdigest(content)
146
+ expect(Digest::SHA256.hexdigest(resource.content)).to eq(expected_sha)
147
+ expect(resource.sha).to eq(expected_sha)
148
+
149
+ resource = find_resource(resources, '/images/percy.svg')
150
+ path = File.expand_path('../../client/testdata/images/percy.svg', __FILE__)
151
+ content = File.read(path)
152
+ # In Ruby 1.9.3 the SVG mimetype is not registered so our mini ruby webserver doesn't serve
153
+ # the correct content type. Allow either to work here so we can test older Rubies fully.
154
+ expect(resource.mimetype).to match(/image\/svg\+xml|application\/octet-stream/)
155
+ expected_sha = Digest::SHA256.hexdigest(content)
156
+ expect(Digest::SHA256.hexdigest(resource.content)).to eq(expected_sha)
157
+ expect(resource.sha).to eq(expected_sha)
158
+
159
+ resource = find_resource(resources, '/images/bg-relative.png')
160
+ path = File.expand_path('../../client/testdata/images/bg-relative.png', __FILE__)
161
+ content = File.read(path)
162
+ expect(resource.mimetype).to eq('image/png')
163
+ expected_sha = Digest::SHA256.hexdigest(content)
164
+ expect(Digest::SHA256.hexdigest(resource.content)).to eq(expected_sha)
165
+ expect(resource.sha).to eq(expected_sha)
166
+
167
+ resource = find_resource(resources, '/images/bg-relative-to-root.png')
168
+ path = File.expand_path('../../client/testdata/images/bg-relative-to-root.png', __FILE__)
169
+ content = File.read(path)
170
+ expect(resource.mimetype).to eq('image/png')
171
+ expected_sha = Digest::SHA256.hexdigest(content)
172
+ expect(Digest::SHA256.hexdigest(resource.content)).to eq(expected_sha)
173
+ expect(resource.sha).to eq(expected_sha)
174
+
175
+ resource = find_resource(resources, '/images/bg-stacked.png')
176
+ path = File.expand_path('../../client/testdata/images/bg-stacked.png', __FILE__)
177
+ content = File.read(path)
178
+ expect(resource.mimetype).to eq('image/png')
179
+ expected_sha = Digest::SHA256.hexdigest(content)
180
+ expect(Digest::SHA256.hexdigest(resource.content)).to eq(expected_sha)
181
+ expect(resource.sha).to eq(expected_sha)
182
+
183
+ resource = find_resource(resources, '/images/srcset-base.png')
184
+ path = File.expand_path('../../client/testdata/images/srcset-base.png', __FILE__)
185
+ content = File.read(path)
186
+ expect(resource.mimetype).to eq('image/png')
187
+ expected_sha = Digest::SHA256.hexdigest(content)
188
+ expect(Digest::SHA256.hexdigest(resource.content)).to eq(expected_sha)
189
+ expect(resource.sha).to eq(expected_sha)
190
+
191
+ resource = find_resource(resources, '/images/srcset-first.png')
192
+ path = File.expand_path('../../client/testdata/images/srcset-first.png', __FILE__)
193
+ content = File.read(path)
194
+ expect(resource.mimetype).to eq('image/png')
195
+ expected_sha = Digest::SHA256.hexdigest(content)
196
+ expect(Digest::SHA256.hexdigest(resource.content)).to eq(expected_sha)
197
+ expect(resource.sha).to eq(expected_sha)
198
+
199
+ resource = find_resource(resources, '/images/srcset-second.png')
200
+ path = File.expand_path('../../client/testdata/images/srcset-second.png', __FILE__)
201
+ content = File.read(path)
202
+ expect(resource.mimetype).to eq('image/png')
203
+ expected_sha = Digest::SHA256.hexdigest(content)
204
+ expect(Digest::SHA256.hexdigest(resource.content)).to eq(expected_sha)
205
+ expect(resource.sha).to eq(expected_sha)
206
+
207
+ resource_urls = resources.collect(&:resource_url).map do |url|
208
+ url.gsub(/localhost:\d+/, 'localhost')
209
+ end
210
+ expect(resource_urls).to match_array([
211
+ "http://localhost/images/img-relative.png",
212
+ "http://localhost/images/img-relative-to-root.png",
213
+ "http://localhost/images/percy.svg",
214
+ "http://localhost/images/srcset-base.png",
215
+ "http://localhost/images/srcset-first.png",
216
+ "http://localhost/images/srcset-second.png",
217
+ "http://localhost/images/bg-relative.png",
218
+ "http://localhost/images/bg-relative-to-root.png",
219
+ "http://localhost/images/bg-stacked.png"
220
+ ])
221
+ expect(resources.collect(&:is_root).uniq).to match_array([nil])
222
+ end
223
+ end
224
+ end
@@ -0,0 +1,58 @@
1
+ require 'sprockets'
2
+
3
+ class SimpleRackApp
4
+ def self.call(env)
5
+ [200, {}, 'Hello World']
6
+ end
7
+ end
8
+
9
+ RSpec.describe Percy::Capybara::Loaders::SprocketsLoader do
10
+ let(:loader) do
11
+ described_class.new(
12
+ page: page,
13
+ sprockets_environment: environment,
14
+ sprockets_options: sprockets_options,
15
+ )
16
+ end
17
+ let(:environment) do
18
+ root = File.expand_path("../../client/testdata", __FILE__)
19
+ environment = Sprockets::Environment.new(root)
20
+ environment.append_path '.'
21
+ environment
22
+ end
23
+ let(:sprockets_options) do
24
+ options = double('options')
25
+ allow(options).to receive(:precompile).and_return([/(?:\/|\\|\A)base\.(css|js)$/])
26
+ options
27
+ end
28
+
29
+ describe '#snapshot_resources' do
30
+ context 'Rack::Test', type: :feature do
31
+ before(:each) { Capybara.app = SimpleRackApp }
32
+
33
+ it 'returns the root HTML resource' do
34
+ visit '/'
35
+ resources = loader.snapshot_resources
36
+ expect(resources.map { |r| r.resource_url }).to eq(["/"])
37
+ expect(resources.first.is_root).to eq(true)
38
+ expect(resources.first.content).to include('Hello World')
39
+ end
40
+ end
41
+ context 'Capybara::Webkit', type: :feature, js: true do
42
+ it 'returns the root HTML resource' do
43
+ visit '/'
44
+ resources = loader.snapshot_resources
45
+ expect(resources.map { |r| r.resource_url }).to eq(["/"])
46
+ expect(resources.first.is_root).to eq(true)
47
+ expect(resources.first.content).to include('Hello World!</body></html>')
48
+ end
49
+ end
50
+ end
51
+ describe '#build_resources', type: :feature do
52
+ it 'returns "build resources" from filtered sprockets paths' do
53
+ resources = loader.build_resources
54
+ expect(resources.map { |r| r.resource_url }).to eq(["/assets/css/base.css"])
55
+ expect(resources.first.content).to include('.colored-by-base')
56
+ end
57
+ end
58
+ end
@@ -10,7 +10,6 @@ RSpec.describe Percy::Capybara do
10
10
  ENV['PERCY_ENABLE'] = nil
11
11
  end
12
12
 
13
-
14
13
  describe '#capybara_client' do
15
14
  it 'returns the current client or creates a new one' do
16
15
  capybara_client = Percy::Capybara.capybara_client
data/spec/spec_helper.rb CHANGED
@@ -5,6 +5,11 @@ require 'support/test_helpers'
5
5
  require 'percy'
6
6
  require 'percy/capybara'
7
7
 
8
+ Capybara::Webkit.configure do |config|
9
+ # config.allow_url("*")
10
+ config.block_unknown_urls
11
+ end
12
+
8
13
  RSpec.configure do |config|
9
14
  config.include TestHelpers
10
15
 
@@ -41,4 +46,25 @@ RSpec.configure do |config|
41
46
  config.before(:each, type: :feature) do
42
47
  WebMock.disable_net_connect!(allow_localhost: true, allow: [/i.imgur.com/])
43
48
  end
49
+
50
+ # Start a temp webserver that serves the testdata directory.
51
+ # You can test this server manually by running:
52
+ # ruby -run -e httpd spec/lib/percy/capybara/client/testdata/ -p 9090
53
+ config.before(:all, type: :feature) do
54
+ port = get_random_open_port
55
+ Capybara.app = nil
56
+ Capybara.app_host = "http://localhost:#{port}"
57
+ Capybara.run_server = false
58
+
59
+ # Note: using this form of popen to keep stdout and stderr silent and captured.
60
+ dir = File.expand_path('../lib/percy/capybara/client/testdata/', __FILE__)
61
+ @process = IO.popen([
62
+ 'ruby', '-run', '-e', 'httpd', dir, '-p', port.to_s, err: [:child, :out]
63
+ ].flatten)
64
+
65
+ # Block until the server is up.
66
+ WebMock.disable_net_connect!(allow_localhost: true)
67
+ verify_server_up(Capybara.app_host)
68
+ end
69
+ config.after(:all, type: :feature) { Process.kill('INT', @process.pid) }
44
70
  end
@@ -1,5 +1,6 @@
1
1
  require 'socket'
2
2
  require 'timeout'
3
+ require 'sprockets'
3
4
 
4
5
  module TestHelpers
5
6
  class ServerDown < Exception; end
@@ -24,4 +25,24 @@ module TestHelpers
24
25
  end
25
26
  raise ServerDown, "Server failed to start: #{host}"
26
27
  end
28
+
29
+ def find_resource(resources, regex)
30
+ begin
31
+ resources.select { |resource| resource.resource_url.match(regex) }.fetch(0)
32
+ rescue IndexError
33
+ raise "Missing expected image with resource_url that matches: #{regex}"
34
+ end
35
+ end
36
+
37
+ def setup_sprockets(capybara_client)
38
+ root = File.expand_path("../../lib/percy/capybara/client/testdata", __FILE__)
39
+ environment = Sprockets::Environment.new(root)
40
+ environment.append_path '.'
41
+
42
+ sprockets_options = double('sprockets_options')
43
+ allow(sprockets_options).to receive(:precompile).and_return([/(?:\/|\\|\A)base\.(css|js)$/])
44
+
45
+ capybara_client.sprockets_environment = environment
46
+ capybara_client.sprockets_options = sprockets_options
47
+ end
27
48
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: percy-capybara
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Perceptual Inc.
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-07-31 00:00:00.000000000 Z
11
+ date: 2015-08-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: percy-client
@@ -84,16 +84,16 @@ dependencies:
84
84
  name: capybara-webkit
85
85
  requirement: !ruby/object:Gem::Requirement
86
86
  requirements:
87
- - - "~>"
87
+ - - ">="
88
88
  - !ruby/object:Gem::Version
89
- version: '1.5'
89
+ version: '1.6'
90
90
  type: :development
91
91
  prerelease: false
92
92
  version_requirements: !ruby/object:Gem::Requirement
93
93
  requirements:
94
- - - "~>"
94
+ - - ">="
95
95
  - !ruby/object:Gem::Version
96
- version: '1.5'
96
+ version: '1.6'
97
97
  - !ruby/object:Gem::Dependency
98
98
  name: selenium-webdriver
99
99
  requirement: !ruby/object:Gem::Requirement
@@ -136,6 +136,20 @@ dependencies:
136
136
  - - ">="
137
137
  - !ruby/object:Gem::Version
138
138
  version: '0.8'
139
+ - !ruby/object:Gem::Dependency
140
+ name: sprockets
141
+ requirement: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - ">="
144
+ - !ruby/object:Gem::Version
145
+ version: 3.2.0
146
+ type: :development
147
+ prerelease: false
148
+ version_requirements: !ruby/object:Gem::Requirement
149
+ requirements:
150
+ - - ">="
151
+ - !ruby/object:Gem::Version
152
+ version: 3.2.0
139
153
  description: ''
140
154
  email:
141
155
  - team@percy.io
@@ -157,6 +171,10 @@ files:
157
171
  - lib/percy/capybara/client/builds.rb
158
172
  - lib/percy/capybara/client/snapshots.rb
159
173
  - lib/percy/capybara/httpfetcher.rb
174
+ - lib/percy/capybara/loaders/base_loader.rb
175
+ - lib/percy/capybara/loaders/native_loader.rb
176
+ - lib/percy/capybara/loaders/sprockets_loader.rb
177
+ - lib/percy/capybara/rspec.rb
160
178
  - lib/percy/capybara/version.rb
161
179
  - percy-capybara.gemspec
162
180
  - spec/lib/percy/capybara/client/builds_spec.rb
@@ -181,6 +199,9 @@ files:
181
199
  - spec/lib/percy/capybara/client/testdata/test-images.html
182
200
  - spec/lib/percy/capybara/client_spec.rb
183
201
  - spec/lib/percy/capybara/httpfetcher_spec.rb
202
+ - spec/lib/percy/capybara/loaders/base_loader_spec.rb
203
+ - spec/lib/percy/capybara/loaders/native_loader_spec.rb
204
+ - spec/lib/percy/capybara/loaders/sprockets_loader_spec.rb
184
205
  - spec/lib/percy/capybara_spec.rb
185
206
  - spec/spec_helper.rb
186
207
  - spec/support/test_helpers.rb
@@ -231,6 +252,9 @@ test_files:
231
252
  - spec/lib/percy/capybara/client/testdata/test-images.html
232
253
  - spec/lib/percy/capybara/client_spec.rb
233
254
  - spec/lib/percy/capybara/httpfetcher_spec.rb
255
+ - spec/lib/percy/capybara/loaders/base_loader_spec.rb
256
+ - spec/lib/percy/capybara/loaders/native_loader_spec.rb
257
+ - spec/lib/percy/capybara/loaders/sprockets_loader_spec.rb
234
258
  - spec/lib/percy/capybara_spec.rb
235
259
  - spec/spec_helper.rb
236
260
  - spec/support/test_helpers.rb