diff --git a/CHANGELOG.md b/CHANGELOG.md index 70e29f095..3e2cf2adf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ - Add new `buildkite_annotate` action to add/remove annotations from the current build. [#442] - Add new `buildkite_metadata` action to set/get metadata from the current build. [#442] +- Add new `prototype_build_details_comment` action to make it easier to generate the HTML comment about Prototype Builds in PRs. [#449] ### Bug Fixes diff --git a/lib/fastlane/plugin/wpmreleasetoolkit/actions/common/prototype_build_details_comment_action.rb b/lib/fastlane/plugin/wpmreleasetoolkit/actions/common/prototype_build_details_comment_action.rb new file mode 100644 index 000000000..5326c5507 --- /dev/null +++ b/lib/fastlane/plugin/wpmreleasetoolkit/actions/common/prototype_build_details_comment_action.rb @@ -0,0 +1,259 @@ +module Fastlane + module Actions + class PrototypeBuildDetailsCommentAction < Action + def self.run(params) + app_display_name = params[:app_display_name] + app_center_info = AppCenterInfo.from_params(params) + metadata = consolidate_metadata(params, app_center_info) + + qr_code_url, extra_metadata = build_install_links(app_center_info, params[:download_url]) + metadata.merge!(extra_metadata) + + # Build the comment parts + icon_img_tag = img_tag(params[:app_icon] || app_center_info.icon, alt: app_display_name) + metadata_rows = metadata.compact.map { |key, value| "#{key}#{value}" } + intro = "#{icon_img_tag}📲 You can test the changes from this Pull Request in #{app_display_name} by scanning the QR code below to install the corresponding build." + footnote = params[:footnote] || (app_center_info.org_name.nil? ? '' : DEFAULT_APP_CENTER_FOOTNOTE) + body = <<~COMMENT_BODY + + + + + + #{metadata_rows.join("\n")} +
App Name#{icon_img_tag} #{app_display_name}
+ #{footnote} + COMMENT_BODY + + if params[:fold] + "
#{intro}\n#{body}
\n" + else + "

#{intro}

\n#{body}" + end + end + + ##################################################### + # @!group Helpers + ##################################################### + + NO_INSTALL_URL_ERROR_MESSAGE = <<~NO_URL_ERROR.freeze + No URL provided to download or install the app. + - Either use this action right after using `appcenter_upload` and provide an `app_center_org_name` (so that this action can use the link to the App Center build) + - Or provide an explicit value for the `download_url` parameter + NO_URL_ERROR + + DEFAULT_APP_CENTER_FOOTNOTE = 'Automatticians: You can use our internal self-serve MC tool to give yourself access to App Center if needed.'.freeze + + # A small model struct to consolidate and pack all the values related to App Center + # + AppCenterInfo = Struct.new(:org_name, :app_name, :display_name, :release_id, :icon, :version, :short_version, :os, :bundle_id) do + # A method to construct an AppCenterInfo instance from the action params, and infer the rest from the `lane_context` if available + def self.from_params(params) + org_name = params[:app_center_org_name] + ctx = if org_name && defined?(SharedValues::APPCENTER_BUILD_INFORMATION) + Fastlane::Actions.lane_context[SharedValues::APPCENTER_BUILD_INFORMATION] || {} + else + {} + end + app_name = params[:app_center_app_name] || ctx['app_name'] + new( + org_name, + app_name, + ctx['app_display_name'] || app_name, + params[:app_center_release_id] || ctx['id'], + ctx['app_icon_url'], + ctx['version'], + ctx['short_version'], + ctx['app_os'], + ctx['bundle_identifier'] + ) + end + end + + # Builds the installation link, QR code URL and extra metadata for download links from the available info + # + # @param [AppCenterInfo] app_center_info The struct containing all the values related to App Center info + # @param [String] download_url The `download_url` parameter passed to the action, if one exists + # @return [(String, Hash)] A tuple containing: + # - The URL for the QR Code + # - A Hash of the extra metadata key/value pairs to add to the existing metadata, to enrich them with download/install links + # + def self.build_install_links(app_center_info, download_url) + install_url = nil + extra_metadata = {} + if download_url + install_url = download_url + extra_metadata['Direct Download'] = "#{File.basename(install_url)}" + end + if app_center_info.org_name && app_center_info.app_name + install_url = "https://install.appcenter.ms/orgs/#{app_center_info.org_name}/apps/#{app_center_info.app_name}/releases/#{app_center_info.release_id}" + extra_metadata['App Center Build'] = "#{app_center_info.display_name} \##{app_center_info.release_id}" + end + UI.user_error!(NO_INSTALL_URL_ERROR_MESSAGE) if install_url.nil? + qr_code_url = "https://chart.googleapis.com/chart?chs=500x500&cht=qr&chl=#{CGI.escape(install_url)}&choe=UTF-8" + [qr_code_url, extra_metadata] + end + + # A method to build the Hash of metadata, based on the explicit ones passed by the user as parameter + the implicit ones from `AppCenterInfo` + # + # @param [Hash] params The action's parameters, as received by `self.run` + # @param [AppCenterInfo] app_center_info The model object containing all the values related to App Center information + # @return [Hash] A hash of all the metadata, gathered from both the explicit and the implicit ones + # + def self.consolidate_metadata(params, app_center_info) + metadata = params[:metadata]&.transform_keys(&:to_s) || {} + metadata['Build Number'] ||= app_center_info.version + metadata['Version'] ||= app_center_info.short_version + metadata[app_center_info.os == 'Android' ? 'Application ID' : 'Bundle ID'] ||= app_center_info.bundle_id + # (Feel free to add more CI-specific env vars in the line below to support other CI providers if you need) + metadata['Commit'] ||= ENV.fetch('BUILDKITE_COMMIT', nil) || other_action.last_git_commit[:abbreviated_commit_hash] + metadata + end + + # Creates an HTML `` tag for an icon URL or the image URL to represent a given Buildkite emoji + # + # @param [String] url_or_emoji A `String` which can be: + # - Either a valid URI to an image + # - Or a string formatted like `:emojiname:`, using a valid Buildite emoji name as defined in https://github.com/buildkite/emojis + # @param [String] alt The alt text to use for the `` tag + # @return [String] The `` tag with the proper image and alt tag + # + def self.img_tag(url_or_emoji, alt: '') + return nil if url_or_emoji.nil? + + emoji = url_or_emoji.match(/:(.*):/)&.captures&.first + app_icon_url = if emoji + "https://raw.githubusercontent.com/buildkite/emojis/main/img-buildkite-64/#{emoji}.png" + elsif URI(url_or_emoji) + url_or_emoji + end + app_icon_url ? "#{alt}" : '' + end + + ##################################################### + # @!group Documentation + ##################################################### + + def self.description + 'Generates a string providing all the details of a prototype build, nicely-formatted and ready to be used as a PR comment (e.g. via `comment_on_pr`).' + end + + def self.details + <<~DESC + Generates a string providing all the details of a prototype build, nicely-formatted as HTML. + The returned string will typically be subsequently used by the `comment_on_pr` action to post that HTML as comment on a PR. + + If you used the `appcenter_upload` lane (to upload the Prototype build to App Center) before calling this action, and pass + a value to the `app_center_org_name` parameter, then many of the parameters and metadata will be automatically extracted + from the `lane_context` provided by `appcenter_upload`, including: + + - The `app_center_app_name`, `app_center_release_id` and installation URL to use for the QR code to point to that release in App Center + - The `app_icon` + - The app's Build Number / versionCode + - The app's Version / versionName + - The app's Bundle ID / Application ID + - A `footnote` mentioning the MC tool for Automatticians to add themselves to App Center + + This means that if you are using App Center to distribute your Prototype Build, the only parameters you *have* to provide + to this action are `app_display_name` and `app_center_org_name`; plus, for `metadata` most of the interesting values will already be pre-filled. + + Any of those implicit default values/metadata can of course be overridden by passing an explicit value to the appropriate parameter(s). + DESC + end + + def self.available_options + app_center_auto = '(will be automatically extracted from `lane_context if you used `appcenter_upload` to distribute your Prototype build)' + [ + FastlaneCore::ConfigItem.new( + key: :app_display_name, + env_name: 'FL_PROTOTYPE_BUILD_DETAILS_COMMENT_APP_DISPLAY_NAME', + description: 'The display name to use for the app in the comment message', + optional: false, + type: String + ), + FastlaneCore::ConfigItem.new( + key: :app_center_org_name, + env_name: 'APPCENTER_OWNER_NAME', # Intentionally the same as the one used by the `appcenter_upload` action + description: 'The name of the organization in App Center (if you used `appcenter_upload` to distribute your Prototype build)', + type: String, + optional: true + ), + FastlaneCore::ConfigItem.new( + key: :app_center_app_name, + env_name: 'APPCENTER_APP_NAME', # Intentionally the same as the one used by the `appcenter_upload` action + description: "The name of the app in App Center #{app_center_auto}", + type: String, + optional: true, + default_value_dynamic: true # As it will be extracted from the `lane_context`` if you used `appcenter_upload`` + ), + FastlaneCore::ConfigItem.new( + key: :app_center_release_id, + env_name: 'APPCENTER_RELEASE_ID', + description: "The release ID/Number in App Center #{app_center_auto}", + type: String, + optional: true, + default_value_dynamic: true # As it will be extracted from the `lane_context`` if you used `appcenter_upload`` + ), + FastlaneCore::ConfigItem.new( + key: :app_icon, + env_name: 'FL_PROTOTYPE_BUILD_DETAILS_COMMENT_APP_ICON', + description: "The name of an emoji from the https://github.com/buildkite/emojis list or the full image URL to use for the icon of the app in the message. #{app_center_auto}", + type: String, + optional: true, + default_value_dynamic: true # As it will be extracted from the `lane_context`` if you used `appcenter_upload`` + ), + FastlaneCore::ConfigItem.new( + key: :download_url, + env_name: 'FL_PROTOTYPE_BUILD_DETAILS_COMMENT_DOWNLOAD_URL', + description: 'The URL to download the build as a direct download. ' \ + + 'If you uploaded the build to App Center, we recommend leaving this nil (the comment will use the URL to the App Center build for the QR code)', + type: String, + optional: true, + default_value: nil + ), + FastlaneCore::ConfigItem.new( + key: :fold, + env_name: 'FL_PROTOTYPE_BUILD_DETAILS_COMMENT_FOLD', + description: 'If true, will wrap the HTML table inside a
block (hidden by default)', + type: Boolean, + default_value: false + ), + FastlaneCore::ConfigItem.new( + key: :metadata, + env_name: 'FL_PROTOTYPE_BUILD_DETAILS_COMMENT_METADATA', + description: 'All additional metadata (as key/value pairs) you want to include in the HTML table of the comment. ' \ + + 'If you are running this action after `appcenter_upload`, some metadata will automatically be added to this list too', + type: Hash, + optional: true, + default_value_dynamic: true # As some metadata will be auto-filled if you used `appcenter_upload` + ), + FastlaneCore::ConfigItem.new( + key: :footnote, + env_name: 'FL_PROTOTYPE_BUILD_DETAILS_COMMENT_FOOTNOTE', + description: 'Optional footnote to add below the HTML table of the comment. ' \ + + 'If you are running this action after `appcenter_upload`, a default footnote for Automatticians will be used unless you provide an explicit value', + type: String, + optional: true, + default_value_dynamic: true # We have a default footnote for the case when you used App Center + ), + ] + end + + def self.return_type + :string + end + + def self.return_value + 'The HTML comment containing all the relevant info about a Prototype build and links to install it' + end + + def self.authors + ['Automattic'] + end + + def self.is_supported?(platform) + true + end + end + end +end diff --git a/spec/prototype_build_details_comment_action_spec.rb b/spec/prototype_build_details_comment_action_spec.rb new file mode 100644 index 000000000..9a0a89bd2 --- /dev/null +++ b/spec/prototype_build_details_comment_action_spec.rb @@ -0,0 +1,574 @@ +require_relative './spec_helper' + +describe Fastlane::Actions::PrototypeBuildDetailsCommentAction do + before do + ENV['BUILDKITE_COMMIT'] = 'a1b2c3f' + end + + describe 'cases common to all operating modes' do + describe 'app_display_name' do + it 'includes the app display name as part of the intro text' do + comment = run_described_fastlane_action( + app_display_name: 'My Cool App', + download_url: 'https://localhost/foo.apk' + ) + expect(comment).to include '📲 You can test the changes from this Pull Request in My Cool App' + end + + it 'includes the app display name as part of implicit metadata' do + comment = run_described_fastlane_action( + app_display_name: 'My Cool App', + download_url: 'https://localhost/foo.apk' + ) + expect(comment).to include 'App Name My Cool App' + end + end + + describe 'app_icon' do + context 'when providing an URL' do + it 'includes the icon in the intro text' do + comment = run_described_fastlane_action( + app_display_name: 'My Cool App', + app_icon: 'https://localhost/foo.png', + download_url: 'https://localhost/foo.apk' + ) + expect(comment).to include "My Cool App📲 " + end + + it 'includes the icon next to the App Name in metadata' do + comment = run_described_fastlane_action( + app_display_name: 'My Cool App', + app_icon: 'https://localhost/foo.png', + download_url: 'https://localhost/foo.apk' + ) + expect(comment).to include "App NameMy Cool App My Cool App" + end + end + + context 'when providing an emoji code' do + it 'includes the icon in the intro text' do + comment = run_described_fastlane_action( + app_display_name: 'My Cool App', + app_icon: ':jetpack:', + download_url: 'https://localhost/foo.apk' + ) + expect(comment).to include "My Cool App📲 " + end + + it 'includes the icon next to the App Name in metadata' do + comment = run_described_fastlane_action( + app_display_name: 'My Cool App', + app_icon: ':jetpack:', + download_url: 'https://localhost/foo.apk' + ) + expect(comment).to include "App NameMy Cool App My Cool App" + end + end + end + + it 'includes the commit as part of the default rows' do + comment = run_described_fastlane_action( + app_display_name: 'My App', + download_url: 'https://localhost/foo.apk' + ) + expect(comment).to include 'Commita1b2c3f' + end + + it 'includes the provided footnote if one was provided explicitly' do + custom_footnote = 'Note that Google Sign-In in not available in those builds' + comment = run_described_fastlane_action( + app_display_name: 'My App', + download_url: 'https://localhost/foo.apk', + footnote: custom_footnote + ) + expect(comment).to include custom_footnote + end + end + + context 'when using App Center with explicit parameters' do + it 'raises an error if neither `app_center_app_name` nor `download_url` is provided' do + expect do + run_described_fastlane_action( + app_display_name: 'My App', + app_center_org_name: 'BestOrg' + ) + end.to raise_error(FastlaneCore::Interface::FastlaneError, described_class::NO_INSTALL_URL_ERROR_MESSAGE) + end + + describe 'checking specific content is present' do + it 'generates the proper App Center link and QR code given an org, app name and release ID' do + comment = run_described_fastlane_action( + app_display_name: 'My App', + app_center_org_name: 'My-Org', + app_center_app_name: 'My-App', + app_center_release_id: '1337' + ) + expect(comment).to include "My-App #1337" + expect(comment).to include 'https://chart.googleapis.com/chart?chs=500x500&cht=qr&chl=https%3A%2F%2Finstall.appcenter.ms%2Forgs%2FMy-Org%2Fapps%2FMy-App%2Freleases%2F1337&choe=UTF-8' + end + + it 'uses the App Center link for the QR code even if a `download_url` is provided' do + comment = run_described_fastlane_action( + app_display_name: 'My App', + app_center_org_name: 'My-Org', + app_center_app_name: 'My-App', + app_center_release_id: '1337', + download_url: 'https://foo.cloudfront.net/someuuid/myapp-prototype-build-pr1337-a1b2c3f.apk' + ) + expect(comment).to include "Direct Downloadmyapp-prototype-build-pr1337-a1b2c3f.apk" + expect(comment).to include 'https://chart.googleapis.com/chart?chs=500x500&cht=qr&chl=https%3A%2F%2Finstall.appcenter.ms%2Forgs%2FMy-Org%2Fapps%2FMy-App%2Freleases%2F1337&choe=UTF-8' + # Inferred metadata rows: App Name, Commit, Direct Download, App Center Build + expect(comment).to include "App Name My App' + expect(comment).to include 'Version:Short28.1' + expect(comment).to include 'Version:Long281003' + expect(comment).to include 'Build ConfigPrototype' + expect(comment).to include 'Commita1b2c3f' + expect(comment).to include "App Center BuildMy-App \#1337" + # Additional inferred metadata rows: App Name, Commit, App Center Build + expect(comment).to include "Note: Google Sign-In in not available in those builds' + ) + + expect(comment).to eq <<~EXPECTED_COMMENT +

📲 You can test the changes from this Pull Request in The Best App by scanning the QR code below to install the corresponding build.

+ + + + + + + + + + +
App Name The Best App
Version:Short28.2
Version:Long28.2.0.108
FlavorCelray
Commita1b2c3f
App Center BuildBestApp \#8888
+ Note: Google Sign-In in not available in those builds + EXPECTED_COMMENT + end + + it 'generates a HTML table comment including the direct link if provided' do + metadata = { + 'Version:Short': '28.2', + 'Version:Long': '28.2.0.108' + } + + comment = run_described_fastlane_action( + app_display_name: 'The Best App', + app_center_org_name: 'BestOrg', + app_center_app_name: 'BestApp', + app_center_release_id: '8888', + download_url: 'https://bestfront.cloudfront.net/feed42/bestapp-pr1357-a1b2c3f.apk', + metadata: metadata + ) + + expect(comment).to eq <<~EXPECTED_COMMENT +

📲 You can test the changes from this Pull Request in The Best App by scanning the QR code below to install the corresponding build.

+ + + + + + + + + + +
App Name The Best App
Version:Short28.2
Version:Long28.2.0.108
Commita1b2c3f
Direct Downloadbestapp-pr1357-a1b2c3f.apk
App Center BuildBestApp \#8888
+ Automatticians: You can use our internal self-serve MC tool to give yourself access to App Center if needed. + EXPECTED_COMMENT + end + + it 'generates a HTML table in a spoiler block if fold is true' do + metadata = { + 'Version:Short': '28.2', + 'Version:Long': '28.2.0.108', + Flavor: 'Celray', + Configuration: 'Debug' + } + + comment = run_described_fastlane_action( + app_display_name: 'The Best App', + app_center_org_name: 'BestOrg', + app_center_app_name: 'BestApp', + app_center_release_id: '1234', + fold: true, + metadata: metadata, + footnote: 'Note: Google Sign-In in not available in those builds' + ) + + expect(comment).to eq <<~EXPECTED_COMMENT +
📲 You can test the changes from this Pull Request in The Best App by scanning the QR code below to install the corresponding build. + + + + + + + + + + + +
App Name The Best App
Version:Short28.2
Version:Long28.2.0.108
FlavorCelray
ConfigurationDebug
Commita1b2c3f
App Center BuildBestApp \#1234
+ Note: Google Sign-In in not available in those builds +
+ EXPECTED_COMMENT + end + end + end + + context 'when using App Center and relying on implicit info from `lane_context`' do + let(:fake_lane_context) do |example| + { + app_name: 'My-App-Alpha', + app_display_name: 'My App (Alpha)', + id: '1337', + version: '1287003', + short_version: '28.7', + app_os: example.metadata[:app_os] || 'Android', + bundle_identifier: 'com.stubfactory.myapp', + app_icon_url: 'https://assets.appcenter.ms/My-App-Alpha/1337/icon.png' + }.transform_keys(&:to_s) + end + + before do + stub_const('Fastlane::Actions::SharedValues::APPCENTER_BUILD_INFORMATION', :fake_app_center_build_info) + allow(Fastlane::Actions).to receive(:lane_context).and_return({ fake_app_center_build_info: fake_lane_context }) + end + + describe 'checking specific content is present' do + it 'generates the proper App Center link and QR code given just an org' do + comment = run_described_fastlane_action( + app_display_name: 'My App', + app_center_org_name: 'My-Org' + ) + expect(comment).to include "My App (Alpha) #1337" + expect(comment).to include 'https://chart.googleapis.com/chart?chs=500x500&cht=qr&chl=https%3A%2F%2Finstall.appcenter.ms%2Forgs%2FMy-Org%2Fapps%2FMy-App-Alpha%2Freleases%2F1337&choe=UTF-8' + end + + it 'uses the App Center link for the QR code even if a `download_url` is provided' do + comment = run_described_fastlane_action( + app_display_name: 'My App', + app_center_org_name: 'My-Org', + download_url: 'https://foo.cloudfront.net/someuuid/myapp-prototype-build-pr1337-a1b2c3f.apk' + ) + expect(comment).to include 'https://chart.googleapis.com/chart?chs=500x500&cht=qr&chl=https%3A%2F%2Finstall.appcenter.ms%2Forgs%2FMy-Org%2Fapps%2FMy-App-Alpha%2Freleases%2F1337&choe=UTF-8' + # Inferred metadata rows: App Name, Build Number, Version, Application ID, Commit, Direct Download, App Center Build + expect(comment).to include "Version42.3' # explicitly provided, overriding the implicit value + expect(comment).not_to include 'Version28.7' # otherwise implicitly added if it were not overridden + expect(comment).to include 'Build Number4203008' # explicitly provided, overriding the implicit value + expect(comment).not_to include 'Build Number1287003' # otherwise implicitly added if it were not overridden + expect(comment).to include 'Build ConfigPrototype' # not overriding any implicit one + # Additional inferred metadata rows: App Name, Application ID, Commit, App Center Build + expect(comment).to include "Application IDcom.stubfactory.myapp' + expect(comment).not_to include 'Bundle ID' + end + + it 'uses "Bundle ID" as the name for the `bundle_identifier` value if using iOS', app_os: 'iOS' do + comment = run_described_fastlane_action( + app_display_name: 'My App', + app_center_org_name: 'My-Org' + ) + expect(comment).to include 'Bundle IDcom.stubfactory.myapp' + expect(comment).not_to include 'Application ID' + end + + it 'includes the direct link if one is provided' do + comment = run_described_fastlane_action( + app_display_name: 'My App', + app_center_org_name: 'My-Org', + download_url: 'https://foo.cloudfront.net/someuuid/myapp-prototype-build-pr1337-a1b2c3f.apk' + ) + expect(comment).to include "Direct Downloadmyapp-prototype-build-pr1337-a1b2c3f.apk" + # Inferred metadata rows: App Name, Build Number, Version, Application ID, Commit, Direct Download, App Center Build + expect(comment).to include "Note: Google Sign-In in not available in those builds' + ) + + expect(comment).to eq <<~EXPECTED_COMMENT +

The Best App📲 You can test the changes from this Pull Request in The Best App by scanning the QR code below to install the corresponding build.

+ + + + + + + + + + + +
App NameThe Best App The Best App
ConfigurationDebug
Build Number1287003
Version28.7
Application IDcom.stubfactory.myapp
Commita1b2c3f
App Center BuildMy App (Alpha) \#1337
+ Note: Google Sign-In in not available in those builds + EXPECTED_COMMENT + end + + it 'generates a HTML table comment including the direct link if provided' do + comment = run_described_fastlane_action( + app_display_name: 'The Best App', + app_center_org_name: 'BestOrg', + download_url: 'https://bestfront.cloudfront.net/feed42/bestapp-pr1357-a1b2c3f.apk' + ) + + expect(comment).to eq <<~EXPECTED_COMMENT +

The Best App📲 You can test the changes from this Pull Request in The Best App by scanning the QR code below to install the corresponding build.

+ + + + + + + + + + + +
App NameThe Best App The Best App
Build Number1287003
Version28.7
Application IDcom.stubfactory.myapp
Commita1b2c3f
Direct Downloadbestapp-pr1357-a1b2c3f.apk
App Center BuildMy App (Alpha) \#1337
+ Automatticians: You can use our internal self-serve MC tool to give yourself access to App Center if needed. + EXPECTED_COMMENT + end + + it 'generates a HTML table in a spoiler block if fold is true' do + metadata = { + 'Google Login': 'Disabled' + } + + comment = run_described_fastlane_action( + app_display_name: 'The Best App', + app_center_org_name: 'BestOrg', + fold: true, + metadata: metadata, + footnote: 'Note: Google Sign-In in not available in those builds' + ) + + expect(comment).to eq <<~EXPECTED_COMMENT +
The Best App📲 You can test the changes from this Pull Request in The Best App by scanning the QR code below to install the corresponding build. + + + + + + + + + + + +
App NameThe Best App The Best App
Google LoginDisabled
Build Number1287003
Version28.7
Application IDcom.stubfactory.myapp
Commita1b2c3f
App Center BuildMy App (Alpha) \#1337
+ Note: Google Sign-In in not available in those builds +
+ EXPECTED_COMMENT + end + end + end + + context 'when not using App Center' do + it 'raises an error if no `download_url` is provided' do + expect do + run_described_fastlane_action( + app_display_name: 'My App' + ) + end.to raise_error(FastlaneCore::Interface::FastlaneError, described_class::NO_INSTALL_URL_ERROR_MESSAGE) + end + + describe 'checking specific content is present' do + it 'generates the proper QR code from the download url' do + comment = run_described_fastlane_action( + app_display_name: 'My App', + download_url: 'https://foo.cloudfront.net/someuuid/myapp-prototype-build-pr1337-a1b2c3f.apk' + ) + expect(comment).to include 'https://chart.googleapis.com/chart?chs=500x500&cht=qr&chl=https%3A%2F%2Ffoo.cloudfront.net%2Fsomeuuid%2Fmyapp-prototype-build-pr1337-a1b2c3f.apk&choe=UTF-8' + end + + it 'includes the direct link as metadata' do + comment = run_described_fastlane_action( + app_display_name: 'My App', + download_url: 'https://foo.cloudfront.net/someuuid/myapp-prototype-build-pr1337-a1b2c3f.apk' + ) + expect(comment).to include "Direct Downloadmyapp-prototype-build-pr1337-a1b2c3f.apk" + end + + it 'does not include the App Center default footnote if no explicit footnote is provided' do + comment = run_described_fastlane_action( + app_display_name: 'My App', + download_url: 'https://localhost/foo.apk' + ) + expect(comment).not_to include described_class::DEFAULT_APP_CENTER_FOOTNOTE + end + + it 'includes the provided footnote if one was provided explicitly' do + comment = run_described_fastlane_action( + app_display_name: 'My App', + download_url: 'https://localhost/foo.apk', + footnote: 'The link to this APK might stop working after a retention delay of 30 days.' + ) + expect(comment).to include 'The link to this APK might stop working after a retention delay of 30 days.' + end + end + + describe 'validating full comment' do + it 'generates a standard HTML table comment by default, with all the information provided' do + metadata = { + 'Version Name': '28.2', + 'Version Code': '1280200108', + Flavor: 'Celray' + } + + comment = run_described_fastlane_action( + app_display_name: 'The Best App', + download_url: 'https://bestfront.cloudfront.net/feed42/bestapp-pr1357-a1b2c3f.apk', + metadata: metadata, + footnote: 'Note: Google Sign-In in not available in those builds' + ) + + expect(comment).to eq <<~EXPECTED_COMMENT +

📲 You can test the changes from this Pull Request in The Best App by scanning the QR code below to install the corresponding build.

+ + + + + + + + + + +
App Name The Best App
Version Name28.2
Version Code1280200108
FlavorCelray
Commita1b2c3f
Direct Downloadbestapp-pr1357-a1b2c3f.apk
+ Note: Google Sign-In in not available in those builds + EXPECTED_COMMENT + end + + it 'generates a HTML table in a spoiler block if fold is true' do + metadata = { + 'Version Name': '28.2', + 'Version Code': '1280200108', + Flavor: 'Celray', + Configuration: 'Debug' + } + + comment = run_described_fastlane_action( + app_display_name: 'The Best App', + download_url: 'https://bestfront.cloudfront.net/feed42/bestapp-pr1357-a1b2c3f.apk', + fold: true, + metadata: metadata, + footnote: 'Note: Google Sign-In in not available in those builds' + ) + + expect(comment).to eq <<~EXPECTED_COMMENT +
📲 You can test the changes from this Pull Request in The Best App by scanning the QR code below to install the corresponding build. + + + + + + + + + + + +
App Name The Best App
Version Name28.2
Version Code1280200108
FlavorCelray
ConfigurationDebug
Commita1b2c3f
Direct Downloadbestapp-pr1357-a1b2c3f.apk
+ Note: Google Sign-In in not available in those builds +
+ EXPECTED_COMMENT + end + end + end +end