diff --git a/.gitlab/ci/rails.gitlab-ci.yml b/.gitlab/ci/rails.gitlab-ci.yml index 494045afdf1462cd416eddf3e5fe54081c2efa32..da5cc989b8d1bf048e23b62205ffec0a5736d5c1 100644 --- a/.gitlab/ci/rails.gitlab-ci.yml +++ b/.gitlab/ci/rails.gitlab-ci.yml @@ -212,6 +212,8 @@ rspec integration pg16 praefect: - .rails:rules:praefect-with-db rspec system pg16: + tags: + - e2e extends: - .rspec-base-pg16 - .rspec-system-parallel @@ -956,6 +958,8 @@ rspec-ee integration pg16 single-db-sec-connection: - .rails:rules:single-db-sec-connection-ee rspec-ee system pg16: + tags: + - e2e extends: - .rspec-ee-base-pg16 - .rspec-ee-system-parallel @@ -1338,6 +1342,8 @@ rspec fail-fast: MATCHING_TESTS_PATH: "${GLCI_PREDICTIVE_RSPEC_MATCHING_TESTS_FOSS_PATH}" rspec-ee fail-fast: + tags: + - e2e extends: - .rspec-ee-base-pg16 - .rspec-fail-fast # extends from .rspec-fail-fast last to override script from .rspec-ee-base-pg16 diff --git a/.gitlab/ci/version.yml b/.gitlab/ci/version.yml index 3d7ac30ab2d579de2ff48a31e7c4c6daa57e80ba..aab3b3de2c6b5616cc4871066d3f770a542f490a 100644 --- a/.gitlab/ci/version.yml +++ b/.gitlab/ci/version.yml @@ -1,6 +1,6 @@ variables: BUILD_OS: "debian" - CHROME_VERSION: "123" + CHROME_VERSION: "138" DOCKER_VERSION: "27.4.1" EXIFTOOL_VERSION: "12.60" GCLOUD_VERSION: "413" diff --git a/qa/Dockerfile b/qa/Dockerfile index a10db24c2f0566b468584e9b6a7a173bb120e86c..4a66992bddabfa5a1654885a6a6a71b75b5538bc 100644 --- a/qa/Dockerfile +++ b/qa/Dockerfile @@ -1,5 +1,5 @@ ARG BUILD_OS=debian -ARG CHROME_VERSION=123 +ARG CHROME_VERSION=138 ARG DOCKER_VERSION=24.0.5 ARG GCLOUD_VERSION=413 ARG GIT_VERSION=2.51 diff --git a/spec/features/snippets/user_creates_snippet_spec.rb b/spec/features/snippets/user_creates_snippet_spec.rb index e49b4707e2ed865c2dddc8682120118d19d8813e..4245839cfd93b4f6ef5d9b6e280fdb78fac0ee30 100644 --- a/spec/features/snippets/user_creates_snippet_spec.rb +++ b/spec/features/snippets/user_creates_snippet_spec.rb @@ -146,9 +146,7 @@ def fill_form # not anymore as requests when they come straight from memory cache. # accept_confirm is needed because of https://gitlab.com/gitlab-org/gitlab/-/issues/262102 reqs = inspect_requests do - visit("#{link}?ran=#{SecureRandom.base64(20)}") do - page.driver.browser.switch_to.alert.accept - end + visit("#{link}?ran=#{SecureRandom.base64(20)}") end expect(reqs.first.status_code).to eq(200) end diff --git a/spec/features/work_items/detail/work_item_detail_spec.rb b/spec/features/work_items/detail/work_item_detail_spec.rb index 3a86cba20029ce8e61dde9627e0bf0b7c94907ef..1d85838112d19b6b291e0d394c5466f916ba39f7 100644 --- a/spec/features/work_items/detail/work_item_detail_spec.rb +++ b/spec/features/work_items/detail/work_item_detail_spec.rb @@ -8,17 +8,17 @@ let_it_be_with_refind(:user) { create(:user) } let_it_be_with_refind(:user2) { create(:user, name: 'John') } - let_it_be(:group) { create(:group) } - let_it_be(:project) { create(:project, :public, :repository, group: group) } - let_it_be(:label) { create(:label, project: project, title: "testing-label") } - let_it_be(:label2) { create(:label, project: project, title: "another-label") } - let_it_be(:work_item) { create(:work_item, project: project, labels: [label]) } - let_it_be(:task) { create(:work_item, :task, project: project) } - let_it_be(:emoji_upvote) { create(:award_emoji, :upvote, awardable: work_item, user: user2) } - let_it_be(:milestone) { create(:milestone, project: project) } - let_it_be(:milestones) { create_list(:milestone, 10, project: project) } - let_it_be(:note) { create(:note, noteable: work_item, project: work_item.project) } - let_it_be(:contact) { create(:contact, group: group) } + let_it_be_with_refind(:group) { create(:group) } + let_it_be_with_refind(:project) { create(:project, :public, :repository, group: group) } + let_it_be_with_refind(:label) { create(:label, project: project, title: "testing-label") } + let_it_be_with_refind(:label2) { create(:label, project: project, title: "another-label") } + let_it_be_with_refind(:work_item) { create(:work_item, project: project, labels: [label]) } + let_it_be_with_refind(:task) { create(:work_item, :task, project: project) } + let_it_be_with_refind(:emoji_upvote) { create(:award_emoji, :upvote, awardable: work_item, user: user2) } + let_it_be_with_refind(:milestone) { create(:milestone, project: project) } + let_it_be_with_refind(:milestones) { create_list(:milestone, 10, project: project) } + let_it_be_with_refind(:note) { create(:note, noteable: work_item, project: work_item.project) } + let_it_be_with_refind(:contact) { create(:contact, group: group) } let(:contact_name) { "#{contact.first_name} #{contact.last_name}" } let(:list_path) { project_issues_path(project) } let(:work_items_path) { project_work_item_path(project, work_item.iid) } @@ -96,10 +96,10 @@ end context 'for signed in admin' do - let_it_be(:admin) { create(:admin) } + let_it_be_with_refind(:admin) { create(:admin) } context 'with akismet integration' do - let_it_be(:user_agent_detail) { create(:user_agent_detail, subject: work_item) } + let_it_be_with_refind(:user_agent_detail) { create(:user_agent_detail, subject: work_item) } before_all do project.add_maintainer(admin) @@ -134,8 +134,8 @@ end context 'for work item authored by guest user' do - let_it_be(:key_result) { create(:work_item, :key_result, author: user, project: project) } - let_it_be(:note) { create(:note, noteable: key_result, project: key_result.project) } + let_it_be_with_refind(:key_result) { create(:work_item, :key_result, author: user, project: project) } + let_it_be_with_refind(:note) { create(:note, noteable: key_result, project: key_result.project) } before do sign_in(user) @@ -180,7 +180,7 @@ end context 'for development widget' do - let_it_be(:merge_request) do + let_it_be_with_refind(:merge_request) do create( :merge_request, source_project: project, diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 6066003137947b27c11a36ab12024dfdbd497540..83738fb426ba317ab374cedfff54587ee8c4349e 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -73,6 +73,8 @@ require_relative '../tooling/quality/test_level' +require Rails.root.join("spec/support/patches/selenium_chrome_patch.rb") + quality_level = Quality::TestLevel.new RSpec.configure do |config| diff --git a/spec/support/capybara.rb b/spec/support/capybara.rb index 7d58a81ede157753a7213b495b07f681a309e2eb..1fe2e1e9e043390ea32ba48a3b87ef69605f5ce1 100644 --- a/spec/support/capybara.rb +++ b/spec/support/capybara.rb @@ -60,9 +60,6 @@ options.add_argument("disable-backgrounding-occluded-windows") end - # Disable /dev/shm use in CI. See https://gitlab.com/gitlab-org/gitlab/issues/4252 - options.add_argument("disable-dev-shm-usage") if ENV['CI'] || ENV['CI_SERVER'] - # Set chrome default download path if ENV['DEFAULT_CHROME_DOWNLOAD_PATH'] options.add_preference("download.default_directory", ENV['DEFAULT_CHROME_DOWNLOAD_PATH']) @@ -84,6 +81,9 @@ options.add_argument("--proxy-server=http://127.0.0.1:#{@blackhole_tcp_server.addr[1]}") options.add_argument("--proxy-bypass-list=#{bypass_list}") + options.add_argument('--disable-gpu') + options.add_argument('--no-sandbox') + Capybara::Selenium::Driver.new( app, browser: :chrome, diff --git a/spec/support/patches/selenium_chrome_patch.rb b/spec/support/patches/selenium_chrome_patch.rb new file mode 100644 index 0000000000000000000000000000000000000000..c3ab2bcb11fe8c9f1e84e144d18a4f9e87f92ca6 --- /dev/null +++ b/spec/support/patches/selenium_chrome_patch.rb @@ -0,0 +1,41 @@ +# frozen_string_literal: true + +# NOTE: Taken from https://github.com/alphagov/forms-admin/blob/5ea98767d765a396ed06cf2cba8f9afb1b10fc0e/spec/support/selenium_error_patch.rb +# which in turn was taken from the following issue: +# https://github.com/teamcapybara/capybara/issues/2800#issuecomment-3049956982 + +# Monkey patch for a specific intermittent Selenium error. +# +# Intermittently, Selenium/Chromedriver raises `Selenium::WebDriver::Error::UnknownError` +# with the message "Node with given id does not belong to the document". + +# Capybara's automatic waiting/retrying mechanism doesn't catch it, +# leading to failure. +# +# We intercept the initialization of `UnknownError`. If the message matches this specific +# case, we raise a `StaleElementReferenceError` instead. This uses Capybara's +# retry logic which makes doesn't fail the test +# +# This can be removed once the following issue is resolved: +# https://github.com/teamcapybara/capybara/issues/2800 +# +# taken from the following issue: +# https://github.com/teamcapybara/capybara/issues/2800#issuecomment-3049956982 + +module Selenium + module WebDriver + module Error + class UnknownError + alias_method :old_initialize, :initialize + + # @param [String] msg + # @return [UnknownError] + def initialize(msg = '') + raise StaleElementReferenceError, msg if msg.include?("Node with given id does not belong to the document") + + old_initialize(msg) + end + end + end + end +end diff --git a/spec/support/shared_examples/features/work_items/work_items_shared_examples.rb b/spec/support/shared_examples/features/work_items/work_items_shared_examples.rb index 7ef6dd74d2ac9ff862b7c93636e2ac1083999bb2..9399c5f0f24a4a9201d34786211751dd1c6bfacd 100644 --- a/spec/support/shared_examples/features/work_items/work_items_shared_examples.rb +++ b/spec/support/shared_examples/features/work_items/work_items_shared_examples.rb @@ -52,7 +52,7 @@ def set_comment end context 'for work item note actions signed in user with developer role' do - let_it_be(:owner) { create(:user) } + let_it_be_with_refind(:owner) { create(:user) } before do project.add_owner(owner) @@ -301,7 +301,7 @@ def click_reply_and_enter_slash end context 'on conflict' do - let_it_be(:other_user) { create(:user) } + let_it_be_with_refind(:other_user) { create(:user) } let(:expected_warning) { 'Someone edited the description at the same time you did.' } before do @@ -624,8 +624,8 @@ def find_and_click_clear(selector, button_name = 'Clear') RSpec.shared_examples 'work items iteration' do include Features::IterationHelpers let(:work_item_iteration_selector) { '[data-testid="work-item-iteration"]' } - let_it_be(:iteration_cadence) { create(:iterations_cadence, group: group, active: true) } - let_it_be(:iteration) do + let_it_be_with_refind(:iteration_cadence) { create(:iterations_cadence, group: group, active: true) } + let_it_be_with_refind(:iteration) do create( :iteration, iterations_cadence: iteration_cadence, @@ -635,7 +635,7 @@ def find_and_click_clear(selector, button_name = 'Clear') ) end - let_it_be(:iteration2) do + let_it_be_with_refind(:iteration2) do create( :iteration, iterations_cadence: iteration_cadence,