spec/lib/percy/capybara/client/snapshots_spec.rb in percy-capybara-0.3.0 vs spec/lib/percy/capybara/client/snapshots_spec.rb in percy-capybara-0.4.0
- old
+ new
@@ -1,266 +1,77 @@
require 'json'
require 'digest'
+require 'capybara'
+require 'capybara/webkit'
RSpec.describe Percy::Capybara::Client::Snapshots, type: :feature do
let(:capybara_client) { Percy::Capybara::Client.new(enabled: true) }
- # Start a temp webserver that serves the testdata directory.
- # You can test this server manually by running:
- # ruby -run -e httpd spec/lib/percy/capybara/client/testdata/ -p 9090
- before(:all) do
- port = get_random_open_port
- Capybara.app_host = "http://localhost:#{port}"
- Capybara.run_server = false
-
- # Note: using this form of popen to keep stdout and stderr silent and captured.
- dir = File.expand_path('../testdata/', __FILE__)
- @process = IO.popen([
- 'ruby', '-run', '-e', 'httpd', dir, '-p', port.to_s, err: [:child, :out]
- ].flatten)
-
- # Block until the server is up.
- WebMock.disable_net_connect!(allow_localhost: true)
- verify_server_up(Capybara.app_host)
- end
- after(:all) { Process.kill('INT', @process.pid) }
-
- before(:each, js: true) do
- # Special setting for capybara-webkit. If clients are using capybara-webkit they would
- # also have to have this setting enabled since apparently all resources are blocked by default.
- page.driver.respond_to?(:allow_url) && page.driver.allow_url('*')
- end
-
- def find_resource(resources, regex)
- begin
- resources.select { |resource| resource.resource_url.match(regex) }.fetch(0)
- rescue IndexError
- raise "Missing expected image with resource_url that matches: #{regex}"
- end
- end
-
- describe '#_should_include_url?' do
- it 'returns true for valid, local URLs' do
- expect(capybara_client._should_include_url?('http://localhost/')).to eq(true)
- expect(capybara_client._should_include_url?('http://localhost:123/')).to eq(true)
- expect(capybara_client._should_include_url?('http://localhost/foo')).to eq(true)
- expect(capybara_client._should_include_url?('http://localhost:123/foo')).to eq(true)
- expect(capybara_client._should_include_url?('http://localhost/foo/test.html')).to eq(true)
- expect(capybara_client._should_include_url?('http://127.0.0.1/')).to eq(true)
- expect(capybara_client._should_include_url?('http://127.0.0.1:123/')).to eq(true)
- expect(capybara_client._should_include_url?('http://127.0.0.1/foo')).to eq(true)
- expect(capybara_client._should_include_url?('http://127.0.0.1:123/foo')).to eq(true)
- expect(capybara_client._should_include_url?('http://127.0.0.1/foo/test.html')).to eq(true)
- expect(capybara_client._should_include_url?('http://0.0.0.0/foo/test.html')).to eq(true)
- # Also works for paths:
- expect(capybara_client._should_include_url?('/')).to eq(true)
- expect(capybara_client._should_include_url?('/foo')).to eq(true)
- expect(capybara_client._should_include_url?('/foo/test.png')).to eq(true)
- end
- it 'returns false for invalid URLs' do
- expect(capybara_client._should_include_url?('')).to eq(false)
- expect(capybara_client._should_include_url?('http://local host/foo')).to eq(false)
- expect(capybara_client._should_include_url?('bad-url/')).to eq(false)
- expect(capybara_client._should_include_url?('bad-url/foo/test.html')).to eq(false)
- end
- it 'returns false for data URLs' do
- expect(capybara_client._should_include_url?('data:image/gif;base64,R0')).to eq(false)
- end
- it 'returns false for remote URLs' do
- expect(capybara_client._should_include_url?('http://foo/')).to eq(false)
- expect(capybara_client._should_include_url?('http://example.com/')).to eq(false)
- expect(capybara_client._should_include_url?('http://example.com/foo')).to eq(false)
- expect(capybara_client._should_include_url?('https://example.com/foo')).to eq(false)
- end
- end
- describe '#_get_root_html_resource', type: :feature, js: true do
- it 'includes the root DOM HTML' do
- visit '/'
- resource = capybara_client.send(:_get_root_html_resource, page)
-
- expect(resource.is_root).to be_truthy
- expect(resource.mimetype).to eq('text/html')
- expect(resource.resource_url).to match(/http:\/\/localhost:\d+\//)
- expect(resource.content).to include('Hello World!')
- expect(resource.sha).to eq(Digest::SHA256.hexdigest(resource.content))
- end
- end
- describe '#_get_css_resources', type: :feature, js: true do
- it 'includes all linked and imported stylesheets' do
- visit '/test-css.html'
- resources = capybara_client.send(:_get_css_resources, page)
-
- resource = find_resource(resources, /http:\/\/localhost:\d+\/css\/base\.css/)
-
- expect(resource.content).to include('.colored-by-base { color: red; }')
- expect(resource.sha).to eq(Digest::SHA256.hexdigest(resource.content))
-
- resource = find_resource(resources, /http:\/\/localhost:\d+\/css\/simple-imports\.css/)
- expect(resource.content).to include("@import url('imports.css');")
- expect(resource.sha).to eq(Digest::SHA256.hexdigest(resource.content))
-
- resource = find_resource(resources, /http:\/\/localhost:\d+\/css\/imports\.css/)
- expect(resource.content).to include('.colored-by-imports { color: red; }')
- expect(resource.sha).to eq(Digest::SHA256.hexdigest(resource.content))
-
- resource = find_resource(resources, /http:\/\/localhost:\d+\/css\/level0-imports\.css/)
- expect(resource.content).to include("@import url('level1-imports.css')")
- expect(resource.content).to include('.colored-by-level0-imports { color: red; }')
- expect(resource.sha).to eq(Digest::SHA256.hexdigest(resource.content))
-
- resource = find_resource(resources, /http:\/\/localhost:\d+\/css\/level1-imports\.css/)
- expect(resource.content).to include("@import url('level2-imports.css')")
- expect(resource.content).to include('.colored-by-level1-imports { color: red; }')
- expect(resource.sha).to eq(Digest::SHA256.hexdigest(resource.content))
-
- resource = find_resource(resources, /http:\/\/localhost:\d+\/css\/level2-imports\.css/)
- expect(resource.content).to include(".colored-by-level2-imports { color: red; }")
- expect(resource.sha).to eq(Digest::SHA256.hexdigest(resource.content))
-
- expect(resources.length).to eq(6)
- expect(resources.collect(&:mimetype).uniq).to eq(['text/css'])
- expect(resources.collect(&:is_root).uniq).to match_array([nil])
- end
- end
- describe '#_get_image_resources', type: :feature, js: true do
- it 'includes all images' do
- visit '/test-images.html'
- resources = capybara_client.send(:_get_image_resources, page)
-
- # The order of these is just for convenience, they match the order in test-images.html.
-
- resource = find_resource(resources, /http:\/\/localhost:\d+\/images\/img-relative\.png/)
- content = File.read(File.expand_path('../testdata/images/img-relative.png', __FILE__))
- expect(resource.mimetype).to eq('image/png')
- expected_sha = Digest::SHA256.hexdigest(content)
- expect(Digest::SHA256.hexdigest(resource.content)).to eq(expected_sha)
- expect(resource.sha).to eq(expected_sha)
-
- resource = find_resource(resources, /http:\/\/localhost:\d+\/images\/img-relative-to-root\.png/)
- content = File.read(File.expand_path('../testdata/images/img-relative-to-root.png', __FILE__))
- expect(resource.mimetype).to eq('image/png')
- expected_sha = Digest::SHA256.hexdigest(content)
- expect(Digest::SHA256.hexdigest(resource.content)).to eq(expected_sha)
- expect(resource.sha).to eq(expected_sha)
-
- resource = find_resource(resources, /http:\/\/localhost:\d+\/images\/percy\.svg/)
- content = File.read(File.expand_path('../testdata/images/percy.svg', __FILE__))
- # In Ruby 1.9.3 the SVG mimetype is not registered so our mini ruby webserver doesn't serve
- # the correct content type. Allow either to work here so we can test older Rubies fully.
- expect(resource.mimetype).to match(/image\/svg\+xml|application\/octet-stream/)
- expected_sha = Digest::SHA256.hexdigest(content)
- expect(Digest::SHA256.hexdigest(resource.content)).to eq(expected_sha)
- expect(resource.sha).to eq(expected_sha)
-
- resource = find_resource(resources, /http:\/\/localhost:\d+\/images\/bg-relative\.png/)
- content = File.read(File.expand_path('../testdata/images/bg-relative.png', __FILE__))
- expect(resource.mimetype).to eq('image/png')
- expected_sha = Digest::SHA256.hexdigest(content)
- expect(Digest::SHA256.hexdigest(resource.content)).to eq(expected_sha)
- expect(resource.sha).to eq(expected_sha)
-
- resource = find_resource(resources, /http:\/\/localhost:\d+\/images\/bg-relative-to-root\.png/)
- content = File.read(File.expand_path('../testdata/images/bg-relative-to-root.png', __FILE__))
- expect(resource.mimetype).to eq('image/png')
- expected_sha = Digest::SHA256.hexdigest(content)
- expect(Digest::SHA256.hexdigest(resource.content)).to eq(expected_sha)
- expect(resource.sha).to eq(expected_sha)
-
- resource = find_resource(resources, /http:\/\/localhost:\d+\/images\/bg-stacked\.png/)
- content = File.read(File.expand_path('../testdata/images/bg-stacked.png', __FILE__))
- expect(resource.mimetype).to eq('image/png')
- expected_sha = Digest::SHA256.hexdigest(content)
- expect(Digest::SHA256.hexdigest(resource.content)).to eq(expected_sha)
- expect(resource.sha).to eq(expected_sha)
-
- resource = find_resource(resources, /http:\/\/localhost:\d+\/images\/srcset-base\.png/)
- content = File.read(File.expand_path('../testdata/images/srcset-base.png', __FILE__))
- expect(resource.mimetype).to eq('image/png')
- expected_sha = Digest::SHA256.hexdigest(content)
- expect(Digest::SHA256.hexdigest(resource.content)).to eq(expected_sha)
- expect(resource.sha).to eq(expected_sha)
-
- resource = find_resource(resources, /http:\/\/localhost:\d+\/images\/srcset-first\.png/)
- content = File.read(File.expand_path('../testdata/images/srcset-first.png', __FILE__))
- expect(resource.mimetype).to eq('image/png')
- expected_sha = Digest::SHA256.hexdigest(content)
- expect(Digest::SHA256.hexdigest(resource.content)).to eq(expected_sha)
- expect(resource.sha).to eq(expected_sha)
-
- resource = find_resource(resources, /http:\/\/localhost:\d+\/images\/srcset-second\.png/)
- content = File.read(File.expand_path('../testdata/images/srcset-second.png', __FILE__))
- expect(resource.mimetype).to eq('image/png')
- expected_sha = Digest::SHA256.hexdigest(content)
- expect(Digest::SHA256.hexdigest(resource.content)).to eq(expected_sha)
- expect(resource.sha).to eq(expected_sha)
-
- resource_urls = resources.collect(&:resource_url).map do |url|
- url.gsub(/localhost:\d+/, 'localhost')
- end
- expect(resource_urls).to match_array([
- "http://localhost/images/img-relative.png",
- "http://localhost/images/img-relative-to-root.png",
- "http://localhost/images/percy.svg",
- "http://localhost/images/srcset-base.png",
- "http://localhost/images/srcset-first.png",
- "http://localhost/images/srcset-second.png",
- "http://localhost/images/bg-relative.png",
- "http://localhost/images/bg-relative-to-root.png",
- "http://localhost/images/bg-stacked.png"
- ])
- expect(resources.collect(&:is_root).uniq).to match_array([nil])
- end
- end
describe '#snapshot', type: :feature, js: true do
context 'simple page with no resources' do
- let(:content) { '<html><body>Hello World!</body><head></head></html>' }
+ before(:each) { setup_sprockets(capybara_client) }
- it 'creates a snapshot and uploads missing resource' do
+ it 'creates a snapshot and uploads missing build resources and missing snapshot resources' do
visit '/'
+ loader = capybara_client.initialize_loader(page: page)
+ build_resource_sha = loader.build_resources.first.sha
+ snapshot_resource_sha = loader.snapshot_resources.first.sha
+
mock_response = {
'data' => {
'id' => '123',
'type' => 'builds',
+ 'relationships' => {
+ 'self' => "/api/v1/snapshots/123",
+ 'missing-resources' => {
+ 'data' => [
+ {
+ 'type' => 'resources',
+ 'id' => build_resource_sha,
+ },
+ ],
+ },
+ },
},
}
stub_request(:post, 'https://percy.io/api/v1/repos/percy/percy-capybara/builds/')
.to_return(status: 201, body: mock_response.to_json)
- resource = capybara_client.send(:_get_root_html_resource, page)
mock_response = {
'data' => {
'id' => '256',
'type' => 'snapshots',
'relationships' => {
'self' => "/api/v1/snapshots/123",
'missing-resources' => {
'data' => [
{
'type' => 'resources',
- 'id' => resource.sha,
+ 'id' => snapshot_resource_sha,
},
],
},
},
},
}
stub_request(:post, 'https://percy.io/api/v1/builds/123/snapshots/')
.to_return(status: 201, body: mock_response.to_json)
-
+ build_resource_stub = stub_request(:post, "https://percy.io/api/v1/builds/123/resources/")
+ .with(body: /#{build_resource_sha}/)
+ .to_return(status: 201, body: {success: true}.to_json)
stub_request(:post, "https://percy.io/api/v1/builds/123/resources/")
- .with(body: /#{resource.sha}/).to_return(status: 201, body: {success: true}.to_json)
-
- expect(capybara_client).to receive(:_get_root_html_resource)
- .with(page).once.and_call_original
- expect(capybara_client).to receive(:_get_css_resources)
- .with(page).once.and_call_original
- expect(capybara_client).to receive(:_get_image_resources)
- .with(page).once.and_call_original
-
+ .with(body: /#{snapshot_resource_sha}/)
+ .to_return(status: 201, body: {success: true}.to_json)
stub_request(:post, "https://percy.io/api/v1/snapshots/256/finalize")
.to_return(status: 200, body: '{"success":true}')
+ expect(capybara_client.build_initialized?).to eq(false)
+ expect(capybara_client.snapshot(page)).to eq(true)
+ expect(capybara_client.build_initialized?).to eq(true)
+
+ # Second time, no build resources are uploaded.
+ remove_request_stub(build_resource_stub)
expect(capybara_client.snapshot(page)).to eq(true)
end
end
end
end