RSpec.describe Percy::Capybara::Loaders::NativeLoader do let(:fake_page) { OpenStruct.new(current_url: "http://localhost/foo")} let(:loader) { described_class.new(page: fake_page) } describe '#build_resources' do it 'returns an empty list' do expect(loader.build_resources).to eq([]) end end describe '#snapshot_resources', type: :feature, js: true do it 'returns the root HTML' do visit '/' loader = described_class.new(page: page) expect(loader.snapshot_resources.collect(&:resource_url)).to match_array(['/']) end it 'returns the root HTML and CSS resources' do visit '/test-css.html' loader = described_class.new(page: page) resource_urls = loader.snapshot_resources.collect(&:resource_url) expect(resource_urls).to match_array([ "/test-css.html", "/css/base.css", "/css/imports.css", "/css/level0-imports.css", "/css/level1-imports.css", "/css/level2-imports.css", "/css/simple-imports.css", ]) end it 'returns the root HTML and image resources' do visit '/test-images.html' loader = described_class.new(page: page) resource_urls = loader.snapshot_resources.collect(&:resource_url) expect(resource_urls).to match_array([ "/test-images.html", "/images/img-relative.png", "/images/img-relative-to-root.png", "/images/percy.svg", "/images/srcset-base.png", "/images/srcset-first.png", "/images/srcset-second.png", "/images/bg-relative.png", "/images/bg-relative-to-root.png", "/images/bg-stacked.png" ]) end end describe "nonlocal.me", type: :feature, js: true do before :each do @orig_app_host = Capybara.app_host Capybara.app_host = Capybara.app_host.gsub('http://localhost:', 'http://localtest.me:') end after :each do Capybara.app_host = @orig_app_host end it 'returns the root HTML and image resources' do visit '/test-localtest-me-images.html' loader = described_class.new(page: page) resource_urls = loader.snapshot_resources.collect(&:resource_url) expect(resource_urls).to eq([ "/test-localtest-me-images.html", "/images/img-relative.png" ]) expect(loader.snapshot_resources.collect(&:is_root)).to eq([true,nil]) end end describe '#_should_include_url?' do it 'returns true for valid, local URLs' do expect(loader._should_include_url?('http://localhost/')).to eq(true) expect(loader._should_include_url?('http://localhost:123/')).to eq(true) expect(loader._should_include_url?('http://localhost/foo')).to eq(true) expect(loader._should_include_url?('http://localhost:123/foo')).to eq(true) expect(loader._should_include_url?('http://localhost/foo/test.html')).to eq(true) expect(loader._should_include_url?('http://127.0.0.1/')).to eq(true) expect(loader._should_include_url?('http://127.0.0.1:123/')).to eq(true) expect(loader._should_include_url?('http://127.0.0.1/foo')).to eq(true) expect(loader._should_include_url?('http://127.0.0.1:123/foo')).to eq(true) expect(loader._should_include_url?('http://127.0.0.1/foo/test.html')).to eq(true) expect(loader._should_include_url?('http://0.0.0.0/foo/test.html')).to eq(true) # Also works for paths: expect(loader._should_include_url?('/')).to eq(true) expect(loader._should_include_url?('/foo')).to eq(true) expect(loader._should_include_url?('/foo/test.png')).to eq(true) end it 'returns false for invalid URLs' do expect(loader._should_include_url?('')).to eq(false) expect(loader._should_include_url?('http://local host/foo')).to eq(false) expect(loader._should_include_url?('bad-url/')).to eq(false) expect(loader._should_include_url?('bad-url/foo/test.html')).to eq(false) end it 'returns false for data URLs' do expect(loader._should_include_url?('data:image/gif;base64,R0')).to eq(false) end it 'returns false for remote URLs' do expect(loader._should_include_url?('http://foo/')).to eq(false) expect(loader._should_include_url?('http://example.com/')).to eq(false) expect(loader._should_include_url?('http://example.com/foo')).to eq(false) expect(loader._should_include_url?('https://example.com/foo')).to eq(false) end context "for nonlocal hosts" do let(:fake_page) { OpenStruct.new(current_url: "http://foo:123/") } it "returns true for the same host port" do expect(loader._should_include_url?('http://foo:123/')).to eq(true) expect(loader._should_include_url?('http://foo:123/bar')).to eq(true) end it "returns false for different port" do expect(loader._should_include_url?('http://foo/')).to eq(false) expect(loader._should_include_url?('http://foo/bar')).to eq(false) expect(loader._should_include_url?('http://foo:1234/')).to eq(false) expect(loader._should_include_url?('http://foo:1234/bar')).to eq(false) end it "returns false for different host" do expect(loader._should_include_url?('http://afoo:123/')).to eq(false) expect(loader._should_include_url?('http://afoo:123/bar')).to eq(false) end end end describe '#_get_css_resources', type: :feature, js: true do it 'includes all linked and imported stylesheets' do visit '/test-css.html' loader = described_class.new(page: page) resources = loader.send(:_get_css_resources) resource = find_resource(resources, '/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, '/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, '/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, '/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, '/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, '/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' loader = described_class.new(page: page) resources = loader.send(:_get_image_resources) # The order of these is just for convenience, they match the order in test-images.html. resource = find_resource(resources, '/images/img-relative.png') path = File.expand_path('../../client/testdata/images/img-relative.png', __FILE__) content = File.read(path) 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, '/images/img-relative-to-root.png') path = File.expand_path('../../client/testdata/images/img-relative-to-root.png', __FILE__) content = File.read(path) 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, '/images/percy.svg') path = File.expand_path('../../client/testdata/images/percy.svg', __FILE__) content = File.read(path) # 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, '/images/bg-relative.png') path = File.expand_path('../../client/testdata/images/bg-relative.png', __FILE__) content = File.read(path) 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, '/images/bg-relative-to-root.png') path = File.expand_path('../../client/testdata/images/bg-relative-to-root.png', __FILE__) content = File.read(path) 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, '/images/bg-stacked.png') path = File.expand_path('../../client/testdata/images/bg-stacked.png', __FILE__) content = File.read(path) 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, '/images/srcset-base.png') path = File.expand_path('../../client/testdata/images/srcset-base.png', __FILE__) content = File.read(path) 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, '/images/srcset-first.png') path = File.expand_path('../../client/testdata/images/srcset-first.png', __FILE__) content = File.read(path) 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, '/images/srcset-second.png') path = File.expand_path('../../client/testdata/images/srcset-second.png', __FILE__) content = File.read(path) 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) expect(resource_urls).to match_array([ "/images/img-relative.png", "/images/img-relative-to-root.png", "/images/percy.svg", "/images/srcset-base.png", "/images/srcset-first.png", "/images/srcset-second.png", "/images/bg-relative.png", "/images/bg-relative-to-root.png", "/images/bg-stacked.png" ]) expect(resources.collect(&:is_root).uniq).to match_array([nil]) end end end