diff --git a/README.md b/README.md index 78a674a..c0dfa63 100644 --- a/README.md +++ b/README.md @@ -47,7 +47,42 @@ upload_to_browserstack_app_live( ``` Check out the example [Fastfile](https://github.com/browserstack/browserstack-fastlane-plugin/blob/master/fastlane/Fastfile) to see how to use this plugin. Try it by including that in your project's Fastfile, running `fastlane install_plugins` and `bundle exec fastlane test`. Please refer to this [sample android project](https://github.com/browserstack/browserstack-android-sample-app), which demonstrates the use of this plugin. -Once the app upload is successful, the app id of the app will be stored in an environment variable, "BROWSERSTACK_APP_ID" and it can be accessed in your tests in the following way : +### Environment variable fallbacks (dotenv friendly) + +All action parameters support environment variable fallback. + +For `upload_to_browserstack_app_automate`: + +- `browserstack_username` -> `BROWSERSTACK_USERNAME` +- `browserstack_access_key` -> `BROWSERSTACK_ACCESS_KEY` +- `file_path` -> `BROWSERSTACK_FILE_PATH` +- `custom_id` -> `BROWSERSTACK_CUSTOM_ID` +- `ios_keychain_support` -> `BROWSERSTACK_IOS_KEYCHAIN_SUPPORT` + +For `upload_to_browserstack_app_live`: + +- `browserstack_username` -> `BROWSERSTACK_USERNAME` +- `browserstack_access_key` -> `BROWSERSTACK_ACCESS_KEY` +- `file_path` -> `BROWSERSTACK_FILE_PATH` + +### Upload outputs + +For `upload_to_browserstack_app_automate`: + +- `BROWSERSTACK_APP_ID` always contains `app_url` from BrowserStack upload response. +- `BROWSERSTACK_CUSTOM_ID` contains `custom_id` from BrowserStack upload response. +- If `custom_id` is absent in response, `BROWSERSTACK_CUSTOM_ID` is set to empty string (`""`). + +The same values are also exposed in lane context: + +- `SharedValues::BROWSERSTACK_APP_ID` +- `SharedValues::BROWSERSTACK_CUSTOM_ID` + +For `upload_to_browserstack_app_live`: + +- `BROWSERSTACK_LIVE_APP_ID` contains `app_url` from BrowserStack upload response. + +Once the app upload is successful, the app URL of the app will be stored in an environment variable, "BROWSERSTACK_APP_ID" and it can be accessed in your tests in the following way : ``` String app = System.getenv("BROWSERSTACK_APP_ID"); // Get app id from environment variable. capabilities.setCapability("app", app); // Add app id to driver capability. @@ -82,4 +117,3 @@ For more information about how the `fastlane` plugin system works, check out the ## About _fastlane_ _fastlane_ is the easiest way to automate beta deployments and releases for your iOS and Android apps. To learn more, check out [fastlane.tools](https://fastlane.tools). - diff --git a/lib/fastlane/plugin/browserstack/actions/upload_to_browserstack_app_automate_action.rb b/lib/fastlane/plugin/browserstack/actions/upload_to_browserstack_app_automate_action.rb index 86f9714..e760e0a 100644 --- a/lib/fastlane/plugin/browserstack/actions/upload_to_browserstack_app_automate_action.rb +++ b/lib/fastlane/plugin/browserstack/actions/upload_to_browserstack_app_automate_action.rb @@ -6,6 +6,7 @@ module Fastlane module Actions module SharedValues BROWSERSTACK_APP_ID ||= :BROWSERSTACK_APP_ID + BROWSERSTACK_CUSTOM_ID ||= :BROWSERSTACK_CUSTOM_ID end class UploadToBrowserstackAppAutomateAction < Action SUPPORTED_FILE_EXTENSIONS = ["apk", "ipa", "aab"] @@ -23,17 +24,22 @@ def self.run(params) UI.message("Uploading app to BrowserStack AppAutomate...") - browserstack_app_id = Helper::BrowserstackHelper.upload_file(browserstack_username, browserstack_access_key, file_path, UPLOAD_API_ENDPOINT, custom_id, ios_keychain_support) + upload_response = Helper::BrowserstackHelper.upload_file(browserstack_username, browserstack_access_key, file_path, UPLOAD_API_ENDPOINT, custom_id, ios_keychain_support) + browserstack_app_id = upload_response["app_url"] + browserstack_custom_id = upload_response["custom_id"] || "" # Set 'BROWSERSTACK_APP_ID' environment variable, if app upload was successful. ENV['BROWSERSTACK_APP_ID'] = browserstack_app_id + ENV['BROWSERSTACK_CUSTOM_ID'] = browserstack_custom_id UI.success("Successfully uploaded app " + file_path + " to BrowserStack AppAutomate with app_url : " + browserstack_app_id) UI.success("Setting Environment variable BROWSERSTACK_APP_ID = " + browserstack_app_id) + UI.success("Setting Environment variable BROWSERSTACK_CUSTOM_ID = " + browserstack_custom_id) # Setting app id in SharedValues, which can be used by other fastlane actions. Actions.lane_context[SharedValues::BROWSERSTACK_APP_ID] = browserstack_app_id + Actions.lane_context[SharedValues::BROWSERSTACK_CUSTOM_ID] = browserstack_custom_id end # Validate file_path. @@ -74,7 +80,8 @@ def self.details def self.output [ - ['BROWSERSTACK_APP_ID', 'App id of uploaded app.'] + ['BROWSERSTACK_APP_ID', 'App URL of uploaded app.'], + ['BROWSERSTACK_CUSTOM_ID', 'Custom id of uploaded app. Empty string when custom_id is not set.'] ] end @@ -100,6 +107,7 @@ def self.available_options [ FastlaneCore::ConfigItem.new(key: :browserstack_username, description: "BrowserStack's username", + env_name: "BROWSERSTACK_USERNAME", optional: false, is_string: true, verify_block: proc do |value| @@ -107,6 +115,7 @@ def self.available_options end), FastlaneCore::ConfigItem.new(key: :browserstack_access_key, description: "BrowserStack's access key", + env_name: "BROWSERSTACK_ACCESS_KEY", optional: false, is_string: true, verify_block: proc do |value| @@ -114,15 +123,18 @@ def self.available_options end), FastlaneCore::ConfigItem.new(key: :custom_id, description: "Custom id", + env_name: "BROWSERSTACK_CUSTOM_ID", optional: true, is_string: true), FastlaneCore::ConfigItem.new(key: :file_path, description: "Path to the app file", + env_name: "BROWSERSTACK_FILE_PATH", optional: true, is_string: true, default_value: default_file_path), FastlaneCore::ConfigItem.new(key: :ios_keychain_support, description: "Enable/disable support for iOS keychain", + env_name: "BROWSERSTACK_IOS_KEYCHAIN_SUPPORT", optional: true, is_string: true) ] diff --git a/lib/fastlane/plugin/browserstack/actions/upload_to_browserstack_app_live_action.rb b/lib/fastlane/plugin/browserstack/actions/upload_to_browserstack_app_live_action.rb index 30ea8ce..adb02cc 100644 --- a/lib/fastlane/plugin/browserstack/actions/upload_to_browserstack_app_live_action.rb +++ b/lib/fastlane/plugin/browserstack/actions/upload_to_browserstack_app_live_action.rb @@ -20,7 +20,8 @@ def self.run(params) UI.message("Uploading app to BrowserStack AppLive...") - browserstack_app_id = Helper::BrowserstackHelper.upload_file(browserstack_username, browserstack_access_key, file_path, UPLOAD_API_ENDPOINT) + upload_response = Helper::BrowserstackHelper.upload_file(browserstack_username, browserstack_access_key, file_path, UPLOAD_API_ENDPOINT) + browserstack_app_id = upload_response["app_url"] # Set 'BROWSERSTACK_APP_ID' environment variable, if app upload was successful. ENV['BROWSERSTACK_LIVE_APP_ID'] = browserstack_app_id @@ -58,7 +59,7 @@ def self.details def self.output [ - ['BROWSERSTACK_LIVE_APP_ID', 'App id of uploaded app.'] + ['BROWSERSTACK_LIVE_APP_ID', 'App URL of uploaded app.'] ] end @@ -84,6 +85,7 @@ def self.available_options [ FastlaneCore::ConfigItem.new(key: :browserstack_username, description: "BrowserStack's username", + env_name: "BROWSERSTACK_USERNAME", optional: false, is_string: true, verify_block: proc do |value| @@ -91,6 +93,7 @@ def self.available_options end), FastlaneCore::ConfigItem.new(key: :browserstack_access_key, description: "BrowserStack's access key", + env_name: "BROWSERSTACK_ACCESS_KEY", optional: false, is_string: true, verify_block: proc do |value| @@ -98,6 +101,7 @@ def self.available_options end), FastlaneCore::ConfigItem.new(key: :file_path, description: "Path to the app file", + env_name: "BROWSERSTACK_FILE_PATH", optional: true, is_string: true, default_value: default_file_path) diff --git a/lib/fastlane/plugin/browserstack/helper/browserstack_helper.rb b/lib/fastlane/plugin/browserstack/helper/browserstack_helper.rb index ff76706..850e935 100644 --- a/lib/fastlane/plugin/browserstack/helper/browserstack_helper.rb +++ b/lib/fastlane/plugin/browserstack/helper/browserstack_helper.rb @@ -20,6 +20,7 @@ def self.show_message # +custom_id+:: Custom id for app upload. # +file_path+:: Path to the file to be uploaded. # +url+:: BrowserStack's app upload endpoint. + # Returns:: Hash response from BrowserStack upload API. def self.upload_file(browserstack_username, browserstack_access_key, file_path, url, custom_id = nil, ios_keychain_support = nil) payload = { multipart: true, @@ -47,13 +48,7 @@ def self.upload_file(browserstack_username, browserstack_access_key, file_path, headers: headers ) - response_json = JSON.parse(response.to_s) - - if !response_json["custom_id"].nil? - return response_json["custom_id"] - else - return response_json["app_url"] - end + JSON.parse(response.to_s) rescue RestClient::ExceptionWithResponse => err begin error_response = JSON.parse(err.response.to_s)["error"] diff --git a/spec/upload_to_browserstack_app_automate_action_spec.rb b/spec/upload_to_browserstack_app_automate_action_spec.rb index c46a562..f04d099 100644 --- a/spec/upload_to_browserstack_app_automate_action_spec.rb +++ b/spec/upload_to_browserstack_app_automate_action_spec.rb @@ -3,8 +3,16 @@ let(:custom_id) { "customId" } before(:each) do + ENV['BROWSERSTACK_USERNAME'] = nil + ENV['BROWSERSTACK_ACCESS_KEY'] = nil + ENV['BROWSERSTACK_FILE_PATH'] = nil + ENV['BROWSERSTACK_CUSTOM_ID'] = nil + ENV['BROWSERSTACK_IOS_KEYCHAIN_SUPPORT'] = nil + ENV['BROWSERSTACK_APP_ID'] = nil Fastlane::Actions.lane_context[Fastlane::Actions::SharedValues::GRADLE_APK_OUTPUT_PATH] = nil Fastlane::Actions.lane_context[Fastlane::Actions::SharedValues::GRADLE_AAB_OUTPUT_PATH] = nil + Fastlane::Actions.lane_context[Fastlane::Actions::SharedValues::BROWSERSTACK_APP_ID] = nil + Fastlane::Actions.lane_context[Fastlane::Actions::SharedValues::BROWSERSTACK_CUSTOM_ID] = nil end it "raises an error if no browserstack_username is given" do @@ -102,9 +110,12 @@ upload_to_browserstack_app_automate({ browserstack_username: 'browserstack_username', browserstack_access_key: 'browserstack_access_key', - }) + }) end").runner.execute(:test) expect(ENV['BROWSERSTACK_APP_ID']).to eq("bs://app_url") + expect(ENV['BROWSERSTACK_CUSTOM_ID']).to eq("") + expect(Fastlane::Actions.lane_context[Fastlane::Actions::SharedValues::BROWSERSTACK_APP_ID]).to eq("bs://app_url") + expect(Fastlane::Actions.lane_context[Fastlane::Actions::SharedValues::BROWSERSTACK_CUSTOM_ID]).to eq("") end it "should work with correct params defaulting with aab on android" do @@ -120,7 +131,6 @@ end it "should work with correct params and specific file_path" do - ENV['BROWSERSTACK_APP_ID'] = nil expect(RestClient::Request).to receive(:execute).and_return({ "app_url" => "bs://app_url" }.to_json) Fastlane::FastFile.new.parse("lane :test do upload_to_browserstack_app_automate({ @@ -131,24 +141,27 @@ end").runner.execute(:test) expect(ENV['BROWSERSTACK_APP_ID']).to eq("bs://app_url") + expect(ENV['BROWSERSTACK_CUSTOM_ID']).to eq("") + expect(Fastlane::Actions.lane_context[Fastlane::Actions::SharedValues::BROWSERSTACK_CUSTOM_ID]).to eq("") end it "should work with custom id" do - ENV['BROWSERSTACK_APP_ID'] = nil expect(RestClient::Request).to receive(:execute).and_return({ "app_url" => "bs://app_url", "custom_id" => custom_id, "shareable_id" => "username/#{custom_id}" }.to_json) Fastlane::FastFile.new.parse("lane :test do upload_to_browserstack_app_automate({ browserstack_username: 'username', browserstack_access_key: 'access_key', custom_id: '#{custom_id}', - file_path: File.join(FIXTURE_PATH, 'HelloWorld.apk') - }) + file_path: File.join(FIXTURE_PATH, 'HelloWorld.apk') + }) end").runner.execute(:test) - expect(ENV['BROWSERSTACK_APP_ID']).to eq(custom_id) + expect(ENV['BROWSERSTACK_APP_ID']).to eq("bs://app_url") + expect(ENV['BROWSERSTACK_CUSTOM_ID']).to eq(custom_id) + expect(Fastlane::Actions.lane_context[Fastlane::Actions::SharedValues::BROWSERSTACK_APP_ID]).to eq("bs://app_url") + expect(Fastlane::Actions.lane_context[Fastlane::Actions::SharedValues::BROWSERSTACK_CUSTOM_ID]).to eq(custom_id) end it "should work with ios keychain support" do - ENV['BROWSERSTACK_APP_ID'] = nil Fastlane::Actions.lane_context[Fastlane::Actions::SharedValues::IPA_OUTPUT_PATH] = nil expect(RestClient::Request).to receive(:execute).and_return({ "app_url" => "bs://app_url", "ios_keychain_support" => true }.to_json) fastfile = Fastlane::FastFile.new.parse(" @@ -165,6 +178,31 @@ ") fastfile.runner.execute(:test, :ios) expect(ENV['BROWSERSTACK_APP_ID']).to eq("bs://app_url") + expect(ENV['BROWSERSTACK_CUSTOM_ID']).to eq("") + end + + it "should use env var fallback for automate params" do + ENV['BROWSERSTACK_USERNAME'] = 'username' + ENV['BROWSERSTACK_ACCESS_KEY'] = 'access_key' + ENV['BROWSERSTACK_FILE_PATH'] = File.join(FIXTURE_PATH, 'HelloWorld.apk') + ENV['BROWSERSTACK_CUSTOM_ID'] = 'env_custom_id' + ENV['BROWSERSTACK_IOS_KEYCHAIN_SUPPORT'] = 'false' + + expect(RestClient::Request).to receive(:execute).with( + hash_including( + payload: hash_including( + data: '{ "custom_id": "env_custom_id" }', + ios_keychain_support: 'false' + ) + ) + ).and_return({ "app_url" => "bs://app_url", "custom_id" => "response_custom_id" }.to_json) + + Fastlane::FastFile.new.parse("lane :test do + upload_to_browserstack_app_automate({}) + end").runner.execute(:test) + + expect(ENV['BROWSERSTACK_APP_ID']).to eq("bs://app_url") + expect(ENV['BROWSERSTACK_CUSTOM_ID']).to eq("response_custom_id") end end end diff --git a/spec/upload_to_browserstack_app_live_action_spec.rb b/spec/upload_to_browserstack_app_live_action_spec.rb index a425fbb..fa24ae3 100644 --- a/spec/upload_to_browserstack_app_live_action_spec.rb +++ b/spec/upload_to_browserstack_app_live_action_spec.rb @@ -1,6 +1,10 @@ describe Fastlane::Actions::UploadToBrowserstackAppLiveAction do describe 'Upload to BrowserStack AppLive' do before(:each) do + ENV['BROWSERSTACK_USERNAME'] = nil + ENV['BROWSERSTACK_ACCESS_KEY'] = nil + ENV['BROWSERSTACK_FILE_PATH'] = nil + ENV['BROWSERSTACK_LIVE_APP_ID'] = nil Fastlane::Actions.lane_context[Fastlane::Actions::SharedValues::GRADLE_APK_OUTPUT_PATH] = nil Fastlane::Actions.lane_context[Fastlane::Actions::SharedValues::GRADLE_AAB_OUTPUT_PATH] = nil end @@ -118,7 +122,6 @@ end it "should work with correct params and specific file_path" do - ENV['BROWSERSTACK_LIVE_APP_ID'] = nil expect(RestClient::Request).to receive(:execute).and_return({ "app_url" => "bs://app_url" }.to_json) Fastlane::FastFile.new.parse("lane :test do upload_to_browserstack_app_live({ @@ -129,5 +132,18 @@ end").runner.execute(:test) expect(ENV['BROWSERSTACK_LIVE_APP_ID']).to eq("bs://app_url") end + + it "should use env var fallback for live params" do + ENV['BROWSERSTACK_USERNAME'] = 'username' + ENV['BROWSERSTACK_ACCESS_KEY'] = 'access_key' + ENV['BROWSERSTACK_FILE_PATH'] = File.join(FIXTURE_PATH, 'HelloWorld.apk') + + expect(RestClient::Request).to receive(:execute).and_return({ "app_url" => "bs://app_url" }.to_json) + Fastlane::FastFile.new.parse("lane :test do + upload_to_browserstack_app_live({}) + end").runner.execute(:test) + + expect(ENV['BROWSERSTACK_LIVE_APP_ID']).to eq("bs://app_url") + end end end