diff options
Diffstat (limited to 'qa/qa/support')
-rw-r--r-- | qa/qa/support/api.rb | 13 | ||||
-rw-r--r-- | qa/qa/support/formatters/feature_flag_formatter.rb | 36 | ||||
-rw-r--r-- | qa/qa/support/formatters/test_stats_formatter.rb | 1 | ||||
-rw-r--r-- | qa/qa/support/helpers/mask_token.rb | 19 | ||||
-rw-r--r-- | qa/qa/support/helpers/plan.rb | 5 | ||||
-rw-r--r-- | qa/qa/support/loglinking.rb | 4 | ||||
-rw-r--r-- | qa/qa/support/matchers/have_matcher.rb | 1 | ||||
-rw-r--r-- | qa/qa/support/page_error_checker.rb | 45 | ||||
-rw-r--r-- | qa/qa/support/wait_for_requests.rb | 6 |
9 files changed, 118 insertions, 12 deletions
diff --git a/qa/qa/support/api.rb b/qa/qa/support/api.rb index 976188e45c6..0c0a1a90ff2 100644 --- a/qa/qa/support/api.rb +++ b/qa/qa/support/api.rb @@ -20,9 +20,7 @@ module QA verify_ssl: false } - RestClient::Request.execute( - default_args.merge(args) - ) + RestClient::Request.execute(default_args.merge(args)) rescue RestClient::ExceptionWithResponse => e return_response_or_raise(e) end @@ -56,13 +54,16 @@ module QA end end - def put(url, payload = nil) + def put(url, payload = nil, args = {}) with_retry_on_too_many_requests do - RestClient::Request.execute( + default_args = { method: :put, url: url, payload: payload, - verify_ssl: false) + verify_ssl: false + } + + RestClient::Request.execute(default_args.merge(args)) rescue RestClient::ExceptionWithResponse => e return_response_or_raise(e) end diff --git a/qa/qa/support/formatters/feature_flag_formatter.rb b/qa/qa/support/formatters/feature_flag_formatter.rb new file mode 100644 index 00000000000..94c039586f9 --- /dev/null +++ b/qa/qa/support/formatters/feature_flag_formatter.rb @@ -0,0 +1,36 @@ +# frozen_string_literal: true + +module QA + module Support + module Formatters + class FeatureFlagFormatter < ::RSpec::Core::Formatters::BaseFormatter + include Specs::Helpers::FeatureFlag + + ::RSpec::Core::Formatters.register( + self, + :example_group_started, + :example_started + ) + + # Starts example group + # @param [RSpec::Core::Notifications::GroupNotification] example_group_notification + # @return [void] + def example_group_started(example_group_notification) + group = example_group_notification.group + + skip_or_run_feature_flag_tests_or_contexts(group) + end + + # Starts example + # @param [RSpec::Core::Notifications::ExampleNotification] example_notification + # @return [void] + def example_started(example_notification) + example = example_notification.example + + # if skip propagated from example_group, do not reset skip metadata + skip_or_run_feature_flag_tests_or_contexts(example) unless example.metadata[:skip] + end + end + end + end +end diff --git a/qa/qa/support/formatters/test_stats_formatter.rb b/qa/qa/support/formatters/test_stats_formatter.rb index 16fc0a50b1b..9d19c2e8bb5 100644 --- a/qa/qa/support/formatters/test_stats_formatter.rb +++ b/qa/qa/support/formatters/test_stats_formatter.rb @@ -64,6 +64,7 @@ module QA name: example.full_description, file_path: file_path, status: example.execution_result.status, + smoke: example.metadata.key?(:smoke).to_s, reliable: example.metadata.key?(:reliable).to_s, quarantined: quarantined(example.metadata), retried: ((example.metadata[:retry_attempts] || 0) > 0).to_s, diff --git a/qa/qa/support/helpers/mask_token.rb b/qa/qa/support/helpers/mask_token.rb new file mode 100644 index 00000000000..1f8161f7173 --- /dev/null +++ b/qa/qa/support/helpers/mask_token.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +module QA + module Support + module Helpers + module MaskToken + def use_ci_variable(name:, value:, project:) + Resource::CiVariable.fabricate_via_api! do |ci_variable| + ci_variable.project = project + ci_variable.key = name + ci_variable.value = value + ci_variable.protected = true + end + "$#{name}" + end + end + end + end +end diff --git a/qa/qa/support/helpers/plan.rb b/qa/qa/support/helpers/plan.rb index 298a6d3f036..b6950c6bacd 100644 --- a/qa/qa/support/helpers/plan.rb +++ b/qa/qa/support/helpers/plan.rb @@ -57,8 +57,9 @@ module QA }.freeze LICENSE_TYPE = { - license_file: 'license file', - cloud_license: 'cloud license' + legacy_license: 'legacy license', + online_cloud: 'online license', + offline_cloud: 'offline license' }.freeze end end diff --git a/qa/qa/support/loglinking.rb b/qa/qa/support/loglinking.rb index 89519e9537c..caf381912d3 100644 --- a/qa/qa/support/loglinking.rb +++ b/qa/qa/support/loglinking.rb @@ -5,7 +5,7 @@ module QA # Static address variables declared for mapping environment to logging URLs STAGING_ADDRESS = 'https://staging.gitlab.com' STAGING_REF_ADDRESS = 'https://staging-ref.gitlab.com' - PRODUCTION_ADDRESS = 'https://www.gitlab.com' + PRODUCTION_ADDRESS = 'https://gitlab.com' PRE_PROD_ADDRESS = 'https://pre.gitlab.com' SENTRY_ENVIRONMENTS = { staging: 'https://sentry.gitlab.net/gitlab/staginggitlabcom/?environment=gstg', @@ -30,7 +30,7 @@ module QA errors = ["Correlation Id: #{correlation_id}"] errors << "Sentry Url: #{sentry_uri}&query=correlation_id%3A%22#{correlation_id}%22" if sentry_uri - errors << "Kibana Url: #{kibana_uri}app/discover#/?_a=(query:(language:kuery,query:'json.correlation_id%20:%20#{correlation_id}'))" if kibana_uri + errors << "Kibana Url: #{kibana_uri}app/discover#/?_a=(query:(language:kuery,query:'json.correlation_id%20:%20#{correlation_id}'))&_g=(time:(from:now-24h%2Fh,to:now))" if kibana_uri errors.join("\n") end diff --git a/qa/qa/support/matchers/have_matcher.rb b/qa/qa/support/matchers/have_matcher.rb index a90d2df96ae..b96566a9e5d 100644 --- a/qa/qa/support/matchers/have_matcher.rb +++ b/qa/qa/support/matchers/have_matcher.rb @@ -10,6 +10,7 @@ module QA file_content assignee child_pipeline + linked_pipeline content design file diff --git a/qa/qa/support/page_error_checker.rb b/qa/qa/support/page_error_checker.rb index ede9b49bda6..192b8c147cd 100644 --- a/qa/qa/support/page_error_checker.rb +++ b/qa/qa/support/page_error_checker.rb @@ -49,7 +49,7 @@ module QA error_code = 404 if Nokogiri::HTML.parse(page.html).xpath("//img").map { |t| t[:alt] }.first.eql?('404') # 500 error page in header surrounded by newlines, try to match - five_hundred_test = Nokogiri::HTML.parse(page.html).xpath("//h1").map.first + five_hundred_test = Nokogiri::HTML.parse(page.html).xpath("//h1/text()").map.first unless five_hundred_test.nil? error_code = 500 if five_hundred_test.text.include?('500') end @@ -61,6 +61,39 @@ module QA end end + # Log request errors triggered from async api calls from the browser + # + # If any errors are found in the session, log them + # using QA::Runtime::Logger + # @param [Capybara::Session] page + def log_request_errors(page) + return if QA::Runtime::Browser.blank_page? + + url = page.driver.browser.current_url + QA::Runtime::Logger.debug "Fetching API error cache for #{url}" + + cache = page.execute_script <<~JS + return !(typeof(Interceptor)==="undefined") ? Interceptor.getCache() : null; + JS + + return unless cache&.dig('errors') + + grouped_errors = group_errors(cache['errors']) + + errors = grouped_errors.map do |error_metadata, request_id_string| + "#{error_metadata} -- #{request_id_string}" + end + + unless errors.nil? || errors.empty? + QA::Runtime::Logger.error "Interceptor Api Errors\n#{errors.join("\n")}" + end + + # clear the cache after logging the errors + page.execute_script <<~JS + Interceptor && Interceptor.saveCache({}); + JS + end + def error_report_for(logs) logs .map(&:message) @@ -70,6 +103,16 @@ module QA def logs(page) page.driver.browser.manage.logs.get(:browser) end + + private + + def group_errors(errors) + errors.each_with_object({}) do |error, memo| + url = error['url']&.split('?')&.first || 'Unknown url' + key = "[#{error['status']}] #{error['method']} #{url}" + memo[key] = "Correlation Id: #{error.dig('headers', 'x-request-id') || 'Correlation Id not found'}" + end + end end end end diff --git a/qa/qa/support/wait_for_requests.rb b/qa/qa/support/wait_for_requests.rb index 16af4bae521..89674a1d5c6 100644 --- a/qa/qa/support/wait_for_requests.rb +++ b/qa/qa/support/wait_for_requests.rb @@ -16,12 +16,16 @@ module QA Waiter.wait_until(log: false) do finished_all_ajax_requests? && (!skip_finished_loading_check ? finished_loading?(wait: 1) : true) end + QA::Support::PageErrorChecker.log_request_errors(Capybara.page) if QA::Runtime::Env.can_intercept? rescue Repeater::WaitExceededError raise $!, 'Page did not fully load. This could be due to an unending async request or loading icon.' end def finished_all_ajax_requests? - Capybara.page.evaluate_script('window.pendingRequests || window.pendingRailsUJSRequests || 0').zero? # rubocop:disable Style/NumericPredicate + requests = %w[window.pendingRequests window.pendingRailsUJSRequests 0] + requests.unshift('(window.Interceptor && window.Interceptor.activeFetchRequests)') if Runtime::Env.can_intercept? + script = requests.join(' || ') + Capybara.page.evaluate_script(script).zero? # rubocop:disable Style/NumericPredicate end def finished_loading?(wait: DEFAULT_MAX_WAIT_TIME) |