diff --git a/.travis.yml b/.travis.yml index c5f79c4..b742ac5 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,10 +1,11 @@ language: ruby -script: "rake travis:ci" +script: "bundle exec rake travis:ci" rvm: - 2.0.0 - 2.1.10 - - 2.2.1 - - 2.3.1 + - 2.2.6 + - 2.3.3 + - 2.4.0 gemfile: - gemfiles/cucumber.1.3.gemfile - gemfiles/cucumber.2.4.gemfile diff --git a/README.md b/README.md index 9c4feeb..a8eac09 100644 --- a/README.md +++ b/README.md @@ -223,6 +223,14 @@ Capybara::Screenshot.s3_object_configuration = { } ``` +You may optionally specify a `:key_prefix` when generating the S3 keys, which can be used to create virtual [folders](http://docs.aws.amazon.com/AmazonS3/latest/UG/FolderOperations.html) in S3, e.g.: + +```ruby +Capybara::Screenshot.s3_configuration = { + ... # other config here + key_prefix: "some/folder/" +} +``` Pruning old screenshots automatically -------------------------- diff --git a/lib/capybara-screenshot/s3_saver.rb b/lib/capybara-screenshot/s3_saver.rb index b461c22..eed5682 100644 --- a/lib/capybara-screenshot/s3_saver.rb +++ b/lib/capybara-screenshot/s3_saver.rb @@ -5,10 +5,11 @@ module Screenshot class S3Saver DEFAULT_REGION = 'us-east-1' - def initialize(saver, s3_client, bucket_name, object_configuration) + def initialize(saver, s3_client, bucket_name, object_configuration, options={}) @saver = saver @s3_client = s3_client @bucket_name = bucket_name + @key_prefix = options[:key_prefix] @object_configuration = object_configuration end @@ -24,7 +25,7 @@ def self.new_with_configuration(saver, configuration, object_configuration) s3_client = Aws::S3::Client.new(s3_client_credentials) bucket_name = configuration.fetch(:bucket_name) - new(saver, s3_client, bucket_name, object_configuration) + new(saver, s3_client, bucket_name, object_configuration, configuration) rescue KeyError raise "Invalid S3 Configuration #{configuration}. Please refer to the documentation for the necessary configurations." end @@ -34,7 +35,7 @@ def save_and_upload_screenshot File.open(local_file_path) do |file| object_payload = { bucket: bucket_name, - key: File.basename(local_file_path), + key: "#{@key_prefix}#{File.basename(local_file_path)}", body: file } @@ -60,6 +61,7 @@ def method_missing(method, *args) :s3_client, :bucket_name, :object_configuration + :key_prefix def save_and saver.save diff --git a/spec/unit/s3_saver_spec.rb b/spec/unit/s3_saver_spec.rb index 12a1668..8751ae7 100644 --- a/spec/unit/s3_saver_spec.rb +++ b/spec/unit/s3_saver_spec.rb @@ -6,8 +6,10 @@ let(:bucket_name) { double('bucket_name') } let(:s3_object_configuration) { {} } let(:s3_client) { double('s3_client') } + let(:key_prefix){ "some/path/" } - let(:s3_saver) { Capybara::Screenshot::S3Saver.new(saver, s3_client, bucket_name, s3_object_configuration) } + let(:s3_saver) { described_class.new(saver, s3_client, bucket_name, s3_object_configuration) } + let(:s3_saver_with_key_prefix) { described_class.new(saver, s3_client, bucket_name, s3_object_configuration, key_prefix: key_prefix) } describe '.new_with_configuration' do let(:access_key_id) { double('access_key_id') } @@ -24,26 +26,36 @@ s3_client_credentials_using_defaults.merge(region: region) } - it 'destructures the configuration into its components' do + before do allow(Aws::S3::Client).to receive(:new).and_return(s3_client) - allow(Capybara::Screenshot::S3Saver).to receive(:new) + allow(described_class).to receive(:new) + end - Capybara::Screenshot::S3Saver.new_with_configuration(saver, { + it 'destructures the configuration into its components' do + described_class.new_with_configuration(saver, { s3_client_credentials: s3_client_credentials, bucket_name: bucket_name }, s3_object_configuration) expect(Aws::S3::Client).to have_received(:new).with(s3_client_credentials) - expect(Capybara::Screenshot::S3Saver).to have_received(:new).with(saver, s3_client, bucket_name, s3_object_configuration) + expect(described_class).to have_received(:new).with(saver, s3_client, bucket_name, s3_object_configuration, hash_including({})) + end + + it 'passes key_prefix option if specified' do + described_class.new_with_configuration(saver, { + s3_client_credentials: s3_client_credentials, + bucket_name: bucket_name, + key_prefix: key_prefix, + }, s3_object_configuration) + + expect(Aws::S3::Client).to have_received(:new).with(s3_client_credentials) + expect(described_class).to have_received(:new).with(saver, s3_client, bucket_name, s3_object_configuration, hash_including(key_prefix: key_prefix)) end it 'defaults the region to us-east-1' do default_region = 'us-east-1' - allow(Aws::S3::Client).to receive(:new).and_return(s3_client) - allow(Capybara::Screenshot::S3Saver).to receive(:new) - - Capybara::Screenshot::S3Saver.new_with_configuration(saver, { + described_class.new_with_configuration(saver, { s3_client_credentials: s3_client_credentials_using_defaults, bucket_name: bucket_name }, s3_object_configuration) @@ -52,23 +64,31 @@ s3_client_credentials.merge(region: default_region) ) - expect(Capybara::Screenshot::S3Saver).to have_received(:new).with(saver, s3_client, bucket_name, s3_object_configuration) + expect(described_class).to have_received(:new).with(saver, s3_client, bucket_name, s3_object_configuration, hash_including({})) end it 'stores the object configuration when passed' do s3_object_configuration = { acl: 'public-read' } Capybara::Screenshot.s3_object_configuration = { acl: 'public-read' } - allow(Aws::S3::Client).to receive(:new).and_return(s3_client) - allow(Capybara::Screenshot::S3Saver).to receive(:new) - - Capybara::Screenshot::S3Saver.new_with_configuration(saver, { + described_class.new_with_configuration(saver, { s3_client_credentials: s3_client_credentials, bucket_name: bucket_name }, s3_object_configuration) expect(Aws::S3::Client).to have_received(:new).with(s3_client_credentials) - expect(Capybara::Screenshot::S3Saver).to have_received(:new).with(saver, s3_client, bucket_name, s3_object_configuration) + expect(described_class).to have_received(:new).with(saver, s3_client, bucket_name, s3_object_configuration, hash_including({})) + end + + it 'passes key_prefix option if specified' do + described_class.new_with_configuration(saver, { + s3_client_credentials: s3_client_credentials, + bucket_name: bucket_name, + key_prefix: key_prefix, + }, s3_object_configuration) + + expect(Aws::S3::Client).to have_received(:new).with(s3_client_credentials) + expect(described_class).to have_received(:new).with(saver, s3_client, bucket_name, s3_object_configuration, hash_including(key_prefix: key_prefix)) end end @@ -121,9 +141,9 @@ s3_saver.save end - describe 'save to s3 with object configuration' do + context 'with object configuration' do let(:s3_object_configuration) { { acl: 'public-read' } } - let(:s3_saver) { Capybara::Screenshot::S3Saver.new(saver, s3_client, bucket_name, s3_object_configuration) } + let(:s3_saver) { described_class.new(saver, s3_client, bucket_name, s3_object_configuration) } it 'uploads the html' do html_path = '/foo/bar.html' @@ -163,6 +183,86 @@ s3_saver.save end end + + context 'with key_prefix specified' do + it 'uploads the html with key prefix' do + html_path = '/foo/bar.html' + expect(saver).to receive(:html_path).and_return(html_path) + expect(saver).to receive(:html_saved?).and_return(true) + + html_file = double('html_file') + + expect(File).to receive(:open).with(html_path).and_yield(html_file) + + expect(s3_client).to receive(:put_object).with( + bucket: bucket_name, + key: 'some/path/bar.html', + body: html_file + ) + + s3_saver_with_key_prefix.save + end + + it 'uploads the screenshot with key prefix' do + screenshot_path = '/baz/bim.jpg' + expect(saver).to receive(:screenshot_path).and_return(screenshot_path) + expect(saver).to receive(:screenshot_saved?).and_return(true) + + screenshot_file = double('screenshot_file') + + expect(File).to receive(:open).with(screenshot_path).and_yield(screenshot_file) + + expect(s3_client).to receive(:put_object).with( + bucket: bucket_name, + key: 'some/path/bim.jpg', + body: screenshot_file + ) + + s3_saver_with_key_prefix.save + end + + context 'with object configuration' do + let(:s3_object_configuration) { { acl: 'public-read' } } + + it 'uploads the html' do + html_path = '/foo/bar.html' + expect(saver).to receive(:html_path).and_return(html_path) + expect(saver).to receive(:html_saved?).and_return(true) + + html_file = double('html_file') + + expect(File).to receive(:open).with(html_path).and_yield(html_file) + + expect(s3_client).to receive(:put_object).with( + bucket: bucket_name, + key: 'some/path/bar.html', + body: html_file, + acl: 'public-read' + ) + + s3_saver_with_key_prefix.save + end + + it 'uploads the screenshot' do + screenshot_path = '/baz/bim.jpg' + expect(saver).to receive(:screenshot_path).and_return(screenshot_path) + expect(saver).to receive(:screenshot_saved?).and_return(true) + + screenshot_file = double('screenshot_file') + + expect(File).to receive(:open).with(screenshot_path).and_yield(screenshot_file) + + expect(s3_client).to receive(:put_object).with( + bucket: bucket_name, + key: 'some/path/bim.jpg', + body: screenshot_file, + acl: 'public-read' + ) + + s3_saver_with_key_prefix.save + end + end + end end # Needed because we cannot depend on Verifying Doubles