diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2023-01-31 06:08:13 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2023-01-31 06:08:13 +0300 |
commit | 25805c16335ed6466f0e475417e3005cd09848c2 (patch) | |
tree | 59e83dff33c409d33b6cfac4c1bfd8e310eadb78 /qa | |
parent | 13ddda5208f9175e822af6d05a32600bc9cad091 (diff) |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'qa')
-rw-r--r-- | qa/lib/slack.rb | 9 | ||||
-rw-r--r-- | qa/lib/slack/mixins/browser.rb | 11 | ||||
-rw-r--r-- | qa/lib/slack/mixins/gitlab_app.rb | 52 | ||||
-rw-r--r-- | qa/lib/slack/page/chat.rb | 68 | ||||
-rw-r--r-- | qa/lib/slack/page/login.rb | 40 | ||||
-rw-r--r-- | qa/lib/slack/page/oauth.rb | 9 | ||||
-rw-r--r-- | qa/qa/flow/integrations/slack.rb | 54 | ||||
-rw-r--r-- | qa/qa/page/profile/chat_names/new.rb | 13 | ||||
-rw-r--r-- | qa/qa/page/project/settings/integrations.rb | 5 | ||||
-rw-r--r-- | qa/qa/runtime/browser.rb | 49 | ||||
-rw-r--r-- | qa/qa/runtime/env.rb | 21 |
11 files changed, 326 insertions, 5 deletions
diff --git a/qa/lib/slack.rb b/qa/lib/slack.rb new file mode 100644 index 00000000000..95e81b700f5 --- /dev/null +++ b/qa/lib/slack.rb @@ -0,0 +1,9 @@ +# frozen_string_literal: true + +require 'chemlab/library' + +module Slack + include Chemlab::Library + + self.base_url = 'https://slack.com' +end diff --git a/qa/lib/slack/mixins/browser.rb b/qa/lib/slack/mixins/browser.rb new file mode 100644 index 00000000000..853ff2c7130 --- /dev/null +++ b/qa/lib/slack/mixins/browser.rb @@ -0,0 +1,11 @@ +# frozen_string_literal: true + +module Slack + module Mixins + module Browser + def browser + ::Chemlab.configuration.browser.session.engine + end + end + end +end diff --git a/qa/lib/slack/mixins/gitlab_app.rb b/qa/lib/slack/mixins/gitlab_app.rb new file mode 100644 index 00000000000..66b456ef824 --- /dev/null +++ b/qa/lib/slack/mixins/gitlab_app.rb @@ -0,0 +1,52 @@ +# frozen_string_literal: true + +module Slack + module Mixins + module GitlabApp + # @param [QA::Resource::Project] project + # @param [String] channel + # @param [String] title + # @param [String] description + def create_issue(project, channel:, title:, description:) + lines = [ + "/staging-gitlab #{project.path_with_namespace} issue new #{title}", + description + ] + + send_message_to_channel(lines, channel: channel) + end + + # @param [QA::Resource::Project] project + # @param [QA::Resource::Project] target + # @param [String] id + # @param [String] channel + def move_issue(project, target, id:, channel:) + line = "/staging-gitlab #{project.path_with_namespace} issue move #{id} to #{target.path_with_namespace}" + send_message_to_channel([line], channel: channel) + end + + # @param [QA::Resource::Project] project + # @param [String] id + # @param [String] channel + def show_issue(project, id:, channel:) + send_message_to_channel(["/staging-gitlab #{project.path_with_namespace} issue show #{id}"], channel: channel) + end + + # @param [QA::Resource::Project] project + # @param [String] id + # @param [String] channel + def close_issue(project, id:, channel:) + send_message_to_channel(["/staging-gitlab #{project.path_with_namespace} issue close #{id}"], channel: channel) + end + + # @param [QA::Resource::Project] project + # @param [String] channel + # @param [String] id + # @param [String] comment + def comment_on_issue(project, channel:, id:, comment:) + command = "/staging-gitlab #{project.path_with_namespace} issue comment #{id}" + send_message_to_channel([command, comment], channel: channel) + end + end + end +end diff --git a/qa/lib/slack/page/chat.rb b/qa/lib/slack/page/chat.rb new file mode 100644 index 00000000000..5f8553fc425 --- /dev/null +++ b/qa/lib/slack/page/chat.rb @@ -0,0 +1,68 @@ +# frozen_string_literal: true + +module Slack + module Page + class Chat < Chemlab::Page + include Mixins::Browser + include Mixins::GitlabApp + + div :message_field, data_qa: 'message_input' + button :connect_gitlab_button, visible_text: /Connect your GitLab account/ + button :skip_download_slack_button, data_qa: 'continue_in_browser' + + def skip_download_screen + wait_for_text('download the Slack app') + + skip_download_slack_button_element.click if skip_download_slack_button_element.exists? + end + + # @param [Array<String>] lines - messages to send + # @param [String] channel to send message to + def send_message_to_channel(lines, channel:) + go_to_channel(channel) + + message_field_element.focus + message_field_element.click + + while line = lines.shift + browser.send_keys(line) + wait_for_text(line) + + browser.send_keys([:shift, :enter]) unless lines.empty? + end + + browser.send_keys(:enter) + end + + def wait_for_text(line) + QA::Support::Waiter.wait_until(max_duration: 3, raise_on_failure: false) do + browser.text.include?(line) + end + end + + def go_to_channel(channel) + menu_item = messages.find do |div| + div.text == channel + end + menu_item.click + end + + def click_connect_account_link + divs = messages(visible_text: /connect your GitLab account/i) + el = divs.last.a(href: /staging-ref/) + el.scroll.to(:top) + el.click + end + + def messages(**opts) + browser.divs(data_qa: 'virtual-list-item', **opts) + end + + def gitlab_app_home + browser.divs(data_qa: 'channel_item_container').find do |el| + el.text == 'GitLab' + end + end + end + end +end diff --git a/qa/lib/slack/page/login.rb b/qa/lib/slack/page/login.rb new file mode 100644 index 00000000000..a11b0d27fd0 --- /dev/null +++ b/qa/lib/slack/page/login.rb @@ -0,0 +1,40 @@ +# frozen_string_literal: true + +module Slack + module Page + class Login < Chemlab::Page + path '/workspace-signin' + + text_field :workspace_field, data_qa: 'signin_domain_input' + button :continue_button, data_qa: 'submit_team_domain_button' + + link :sign_in_with_password_link, data_qa: 'sign_in_password_link' + + text_field :email_address_field, data_qa: 'login_email' + text_field :password_field, data_qa: 'login_password', type: 'password' + button :sign_in_button, data_qa: 'signin_button' + + def sign_in + navigate_to_workspace + + # sign into with password if needed + sign_in_with_password_link_element.click if sign_in_with_password_link_element.exists? + + finish_sign_in + end + + def navigate_to_workspace + self.workspace_field = ::QA::Runtime::Env.slack_workspace + continue_button + end + + def finish_sign_in + return unless email_address_field_element.exists? + + self.email_address_field = ::QA::Runtime::Env.slack_email + self.password_field = ::QA::Runtime::Env.slack_password + sign_in_button + end + end + end +end diff --git a/qa/lib/slack/page/oauth.rb b/qa/lib/slack/page/oauth.rb new file mode 100644 index 00000000000..700d19f0c4c --- /dev/null +++ b/qa/lib/slack/page/oauth.rb @@ -0,0 +1,9 @@ +# frozen_string_literal: true + +module Slack + module Page + class Oauth < Chemlab::Page + button :submit_oauth, data_qa: 'oauth_submit_button' + end + end +end diff --git a/qa/qa/flow/integrations/slack.rb b/qa/qa/flow/integrations/slack.rb new file mode 100644 index 00000000000..8f18ccaa791 --- /dev/null +++ b/qa/qa/flow/integrations/slack.rb @@ -0,0 +1,54 @@ +# frozen_string_literal: true + +module QA + module Flow + module Integrations + module Slack + extend self + + # Need to sign in for this method + # @param [QA::Resource::Project] + def start_slack_install(project) + project.visit! + + Page::Project::Menu.perform do |project_menu_page| + project_menu_page.click_project + project_menu_page.go_to_integrations_settings + end + + Page::Project::Settings::Integrations.perform(&:click_slack_application_link) + + EE::Page::Project::Settings::Services::Slack.perform(&:start_slack_install) + ::Slack::Page::Oauth.perform(&:submit_oauth) + end + + # @param [QA::Resource::Project] project + # @option [String | Nil] channel + # @return [Boolean] is this account already authorized? + def start_gitlab_connect(project, channel: nil) + ::Slack::Page::Chat.perform do |chat_page| + # sometimes Slack will present a blocking page + # for downloading the app instead of using a browser + chat_page.skip_download_screen + + lines = ["/staging-gitlab #{project.path_with_namespace} issue show 1"] + chat_page.send_message_to_channel(lines, channel: channel) + + # The only way to know if we are authorized is to send a slash command to the channel. + # If the account / chat_name is already authorized, the Slack app will try to look up the issue + # and return a 404 because it doesn't exist + QA::Support::Waiter.wait_until(max_duration: 4, raise_on_failure: false) do + chat_page.messages.last.text =~ /connect your GitLab account|404 not found!/i + end + + break(true) if chat_page.messages.last.text =~ /404 not found!/i + + chat_page.click_connect_account_link + + false + end + end + end + end + end +end diff --git a/qa/qa/page/profile/chat_names/new.rb b/qa/qa/page/profile/chat_names/new.rb new file mode 100644 index 00000000000..351d36b87d5 --- /dev/null +++ b/qa/qa/page/profile/chat_names/new.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +module QA + module Page + module Profile + module ChatNames + class New < Chemlab::Page + button :authorize, value: /Authorize/i + end + end + end + end +end diff --git a/qa/qa/page/project/settings/integrations.rb b/qa/qa/page/project/settings/integrations.rb index 58b3badbb22..c97593312d1 100644 --- a/qa/qa/page/project/settings/integrations.rb +++ b/qa/qa/page/project/settings/integrations.rb @@ -10,6 +10,7 @@ module QA element :prometheus_link, %q(:data-qa-selector="`${item.name}_link`") # rubocop:disable QA/ElementWithPattern element :jira_link, %q(:data-qa-selector="`${item.name}_link`") # rubocop:disable QA/ElementWithPattern element :pipelines_email_link, %q(:data-qa-selector="`${item.name}_link`") # rubocop:disable QA/ElementWithPattern + element :gitlab_slack_application_link, %q(:data-qa-selector="`${item.name}_link`") # rubocop:disable QA/ElementWithPattern end def click_on_prometheus_integration @@ -27,6 +28,10 @@ module QA def click_jenkins_ci_link click_element :jenkins_link end + + def click_slack_application_link + click_element :gitlab_slack_application_link + end end end end diff --git a/qa/qa/runtime/browser.rb b/qa/qa/runtime/browser.rb index faf2023f7c2..f01657c8deb 100644 --- a/qa/qa/runtime/browser.rb +++ b/qa/qa/runtime/browser.rb @@ -104,7 +104,7 @@ module QA # Specify the user-agent to allow challenges to be bypassed # See https://gitlab.com/gitlab-com/gl-infra/infrastructure/-/issues/11938 - if QA::Runtime::Env.user_agent + unless QA::Runtime::Env.user_agent.blank? capabilities['goog:chromeOptions'][:args] << "user-agent=#{QA::Runtime::Env.user_agent}" end @@ -117,6 +117,38 @@ module QA capabilities['goog:chromeOptions'][:args] << 'window-size=1480,2200' end + # Slack tries to open an external URL handler + # The test needs to default the handler to always open Slack + # to prevent a blocking popup. + if QA::Runtime::Env.slack_workspace + slack_default_preference = { + 'protocol_handler' => { + 'allowed_origin_protocol_pairs' => { + "https://#{QA::Runtime::Env.slack_workspace}.slack.com" => { + 'slack' => true + } + } + } + } + + default_profile = File.join("#{chrome_profile_location}/Default") + FileUtils.mkdir_p(default_profile) unless Dir.exist?(default_profile) + preferences = slack_default_preference + + # mutate the preferences if it exists + # else write a new file + if File.exist?("#{default_profile}/Preferences") + begin + preferences = JSON.parse(File.read("#{default_profile}/Preferences")) + preferences.deep_merge!(slack_default_preference) + rescue JSON::ParserError => _ + end + end + + File.write("#{default_profile}/Preferences", preferences.to_json) + append_chrome_profile_to_capabilities(capabilities) + end + when :safari if QA::Runtime::Env.remote_mobile_device_name capabilities['platformName'] = 'iOS' @@ -131,10 +163,7 @@ module QA # Use the same profile on QA runs if CHROME_REUSE_PROFILE is true. # Useful to speed up local QA. - if QA::Runtime::Env.reuse_chrome_profile? - qa_profile_dir = ::File.expand_path('../../tmp/qa-profile', __dir__) - capabilities['goog:chromeOptions'][:args] << "user-data-dir=#{qa_profile_dir}" - end + append_chrome_profile_to_capabilities(capabilities) if QA::Runtime::Env.reuse_chrome_profile? selenium_options = { browser: QA::Runtime::Env.browser, @@ -193,6 +222,16 @@ module QA end # rubocop: enable Metrics/AbcSize + def self.append_chrome_profile_to_capabilities(capabilities) + return if capabilities['goog:chromeOptions'][:args].include?(chrome_profile_location) + + capabilities['goog:chromeOptions'][:args] << "user-data-dir=#{chrome_profile_location}" + end + + def self.chrome_profile_location + ::File.expand_path('../../tmp/qa-profile', __dir__) + end + class Session include Capybara::DSL diff --git a/qa/qa/runtime/env.rb b/qa/qa/runtime/env.rb index e952c0337f2..b53c2320537 100644 --- a/qa/qa/runtime/env.rb +++ b/qa/qa/runtime/env.rb @@ -293,6 +293,18 @@ module QA ENV['JIRA_HOSTNAME'] end + def slack_workspace + ENV['QA_SLACK_WORKSPACE'] + end + + def slack_email + ENV['QA_SLACK_EMAIL'] + end + + def slack_password + ENV['QA_SLACK_PASSWORD'] + end + def jenkins_admin_username ENV.fetch('QA_JENKINS_USER', 'administrator') end @@ -502,6 +514,15 @@ module QA ENV['DEFAULT_CHROME_DOWNLOAD_PATH'] || Dir.tmpdir end + def require_slack_env! + missing_env = %i[slack_workspace slack_email slack_password].select do |method| + ::QA::Runtime::Env.public_send(method).nil? + end + return unless missing_env.any? + + raise "Missing Slack env: #{missing_env.map(&:upcase).join(', ')}" + end + private def remote_grid_credentials |