diff options
Diffstat (limited to 'spec/support')
20 files changed, 289 insertions, 93 deletions
diff --git a/spec/support/api/schema_matcher.rb b/spec/support/api/schema_matcher.rb index 6591d56e473..f7d74df0656 100644 --- a/spec/support/api/schema_matcher.rb +++ b/spec/support/api/schema_matcher.rb @@ -1,6 +1,6 @@ module SchemaPath def self.expand(schema, dir = '') - Rails.root.join('spec', dir, "fixtures/api/schemas/#{schema}.json").to_s + Rails.root.join(dir, 'spec', "fixtures/api/schemas/#{schema}.json").to_s end end diff --git a/spec/support/database_cleaner.rb b/spec/support/database_cleaner.rb new file mode 100644 index 00000000000..edd7de94203 --- /dev/null +++ b/spec/support/database_cleaner.rb @@ -0,0 +1,56 @@ +# frozen_string_literal: true + +require 'database_cleaner/active_record/deletion' +require_relative 'db_cleaner' + +module FakeInformationSchema + # Work around a bug in DatabaseCleaner when using the deletion strategy: + # https://github.com/DatabaseCleaner/database_cleaner/issues/347 + # + # On MySQL, if the information schema is said to exist, we use an inaccurate + # row count leading to some tables not being cleaned when they should + def information_schema_exists?(_connection) + false + end +end + +DatabaseCleaner::ActiveRecord::Deletion.prepend(FakeInformationSchema) + +RSpec.configure do |config| + include DbCleaner + + # Ensure all sequences are reset at the start of the suite run + config.before(:suite) do + setup_database_cleaner + DatabaseCleaner.clean_with(:truncation) + end + + config.append_after(:context) do + DatabaseCleaner.clean_with(:deletion, cache_tables: false) + end + + config.before do + setup_database_cleaner + DatabaseCleaner.strategy = :transaction + end + + config.before(:each, :js) do + DatabaseCleaner.strategy = :deletion, { except: deletion_except_tables, cache_tables: false } + end + + config.before(:each, :delete) do + DatabaseCleaner.strategy = :deletion, { except: deletion_except_tables, cache_tables: false } + end + + config.before(:each, :migration) do + DatabaseCleaner.strategy = :deletion, { cache_tables: false } + end + + config.before do + DatabaseCleaner.start + end + + config.append_after do + DatabaseCleaner.clean + end +end diff --git a/spec/support/db_cleaner.rb b/spec/support/db_cleaner.rb index 34b9efaaecd..c69fa322073 100644 --- a/spec/support/db_cleaner.rb +++ b/spec/support/db_cleaner.rb @@ -1,49 +1,9 @@ -require 'database_cleaner/active_record/deletion' - -module FakeInformationSchema - # Work around a bug in DatabaseCleaner when using the deletion strategy: - # https://github.com/DatabaseCleaner/database_cleaner/issues/347 - # - # On MySQL, if the information schema is said to exist, we use an inaccurate - # row count leading to some tables not being cleaned when they should - def information_schema_exists?(_connection) - false - end -end - -DatabaseCleaner::ActiveRecord::Deletion.prepend(FakeInformationSchema) - -RSpec.configure do |config| - # Ensure all sequences are reset at the start of the suite run - config.before(:suite) do - DatabaseCleaner.clean_with(:truncation) - end - - config.append_after(:context) do - DatabaseCleaner.clean_with(:deletion, cache_tables: false) - end - - config.before do - DatabaseCleaner.strategy = :transaction - end - - config.before(:each, :js) do - DatabaseCleaner.strategy = :deletion, { cache_tables: false } - end - - config.before(:each, :delete) do - DatabaseCleaner.strategy = :deletion, { cache_tables: false } - end - - config.before(:each, :migration) do - DatabaseCleaner.strategy = :deletion, { cache_tables: false } - end - - config.before do - DatabaseCleaner.start +module DbCleaner + def deletion_except_tables + [] end - config.append_after do - DatabaseCleaner.clean + def setup_database_cleaner + DatabaseCleaner[:active_record, { connection: ActiveRecord::Base }] end end diff --git a/spec/support/features/reportable_note_shared_examples.rb b/spec/support/features/reportable_note_shared_examples.rb index 8cfce49da8a..89dfbf931d2 100644 --- a/spec/support/features/reportable_note_shared_examples.rb +++ b/spec/support/features/reportable_note_shared_examples.rb @@ -41,7 +41,7 @@ shared_examples 'reportable note' do |type| def open_dropdown(dropdown) # make window wide enough that tooltip doesn't trigger horizontal scrollbar - resize_window(1200, 800) + restore_window_size dropdown.find('.more-actions-toggle').click dropdown.find('.dropdown-menu li', match: :first) diff --git a/spec/support/features/variable_list_shared_examples.rb b/spec/support/features/variable_list_shared_examples.rb index 0a464d77cb7..73156d18c1b 100644 --- a/spec/support/features/variable_list_shared_examples.rb +++ b/spec/support/features/variable_list_shared_examples.rb @@ -8,7 +8,7 @@ shared_examples 'variable list' do it 'adds new CI variable' do page.within('.js-ci-variable-list-section .js-row:last-child') do find('.js-ci-variable-input-key').set('key') - find('.js-ci-variable-input-value').set('key value') + find('.js-ci-variable-input-value').set('key_value') end click_button('Save variables') @@ -19,7 +19,7 @@ shared_examples 'variable list' do # We check the first row because it re-sorts to alphabetical order on refresh page.within('.js-ci-variable-list-section .js-row:nth-child(1)') do expect(find('.js-ci-variable-input-key').value).to eq('key') - expect(find('.js-ci-variable-input-value', visible: false).value).to eq('key value') + expect(find('.js-ci-variable-input-value', visible: false).value).to eq('key_value') end end @@ -44,7 +44,7 @@ shared_examples 'variable list' do it 'adds new protected variable' do page.within('.js-ci-variable-list-section .js-row:last-child') do find('.js-ci-variable-input-key').set('key') - find('.js-ci-variable-input-value').set('key value') + find('.js-ci-variable-input-value').set('key_value') find('.ci-variable-protected-item .js-project-feature-toggle').click expect(find('.js-ci-variable-input-protected', visible: false).value).to eq('true') @@ -58,7 +58,7 @@ shared_examples 'variable list' do # We check the first row because it re-sorts to alphabetical order on refresh page.within('.js-ci-variable-list-section .js-row:nth-child(1)') do expect(find('.js-ci-variable-input-key').value).to eq('key') - expect(find('.js-ci-variable-input-value', visible: false).value).to eq('key value') + expect(find('.js-ci-variable-input-value', visible: false).value).to eq('key_value') expect(find('.js-ci-variable-input-protected', visible: false).value).to eq('true') end end diff --git a/spec/support/helpers/file_mover_helpers.rb b/spec/support/helpers/file_mover_helpers.rb new file mode 100644 index 00000000000..1ba7cc03354 --- /dev/null +++ b/spec/support/helpers/file_mover_helpers.rb @@ -0,0 +1,12 @@ +# frozen_string_literal: true + +module FileMoverHelpers + def stub_file_mover(file_path, stub_real_path: nil) + file_name = File.basename(file_path) + allow(Pathname).to receive(:new).and_call_original + + expect_next_instance_of(Pathname, a_string_including(file_name)) do |pathname| + allow(pathname).to receive(:realpath) { stub_real_path || pathname.cleanpath } + end + end +end diff --git a/spec/support/helpers/graphql_helpers.rb b/spec/support/helpers/graphql_helpers.rb index e468ee4676d..6cdc19ac2e5 100644 --- a/spec/support/helpers/graphql_helpers.rb +++ b/spec/support/helpers/graphql_helpers.rb @@ -77,13 +77,23 @@ module GraphqlHelpers def query_graphql_field(name, attributes = {}, fields = nil) fields ||= all_graphql_fields_for(name.classify) attributes = attributes_to_graphql(attributes) + attributes = "(#{attributes})" if attributes.present? <<~QUERY - #{name}(#{attributes}) { - #{fields} - } + #{name}#{attributes} + #{wrap_fields(fields)} QUERY end + def wrap_fields(fields) + return unless fields.strip.present? + + <<~FIELDS + { + #{fields} + } + FIELDS + end + def all_graphql_fields_for(class_name, parent_types = Set.new) type = GitlabSchema.types[class_name.to_s] return "" unless type @@ -115,8 +125,8 @@ module GraphqlHelpers end.join(", ") end - def post_graphql(query, current_user: nil, variables: nil) - post api('/', current_user, version: 'graphql'), params: { query: query, variables: variables } + def post_graphql(query, current_user: nil, variables: nil, headers: {}) + post api('/', current_user, version: 'graphql'), params: { query: query, variables: variables }, headers: headers end def post_graphql_mutation(mutation, current_user: nil) diff --git a/spec/support/helpers/javascript_fixtures_helpers.rb b/spec/support/helpers/javascript_fixtures_helpers.rb index 89c5ec7a718..f525b2f945e 100644 --- a/spec/support/helpers/javascript_fixtures_helpers.rb +++ b/spec/support/helpers/javascript_fixtures_helpers.rb @@ -2,11 +2,12 @@ require 'action_dispatch/testing/test_request' require 'fileutils' module JavaScriptFixturesHelpers + extend ActiveSupport::Concern include Gitlab::Popen - FIXTURE_PATH = 'spec/javascripts/fixtures'.freeze + FIXTURE_PATHS = %w[spec/javascripts/fixtures ee/spec/javascripts/fixtures].freeze - def self.included(base) + included do |base| base.around do |example| # pick an arbitrary date from the past, so tests are not time dependent Timecop.freeze(Time.utc(2015, 7, 3, 10)) { example.run } @@ -15,26 +16,30 @@ module JavaScriptFixturesHelpers # Public: Removes all fixture files from given directory # - # directory_name - directory of the fixtures (relative to FIXTURE_PATH) + # directory_name - directory of the fixtures (relative to FIXTURE_PATHS) # def clean_frontend_fixtures(directory_name) - directory_name = File.expand_path(directory_name, FIXTURE_PATH) - Dir[File.expand_path('*.html.raw', directory_name)].each do |file_name| - FileUtils.rm(file_name) + FIXTURE_PATHS.each do |fixture_path| + directory_name = File.expand_path(directory_name, fixture_path) + Dir[File.expand_path('*.html.raw', directory_name)].each do |file_name| + FileUtils.rm(file_name) + end end end # Public: Store a response object as fixture file # # response - string or response object to store - # fixture_file_name - file name to store the fixture in (relative to FIXTURE_PATH) + # fixture_file_name - file name to store the fixture in (relative to FIXTURE_PATHS) # def store_frontend_fixture(response, fixture_file_name) - fixture_file_name = File.expand_path(fixture_file_name, FIXTURE_PATH) - fixture = response.respond_to?(:body) ? parse_response(response) : response + FIXTURE_PATHS.each do |fixture_path| + fixture_file_name = File.expand_path(fixture_file_name, fixture_path) + fixture = response.respond_to?(:body) ? parse_response(response) : response - FileUtils.mkdir_p(File.dirname(fixture_file_name)) - File.write(fixture_file_name, fixture) + FileUtils.mkdir_p(File.dirname(fixture_file_name)) + File.write(fixture_file_name, fixture) + end end def remove_repository(project) diff --git a/spec/support/helpers/kubernetes_helpers.rb b/spec/support/helpers/kubernetes_helpers.rb index 9dc89b483b2..cca11e112c9 100644 --- a/spec/support/helpers/kubernetes_helpers.rb +++ b/spec/support/helpers/kubernetes_helpers.rb @@ -9,6 +9,10 @@ module KubernetesHelpers kube_response(kube_pods_body) end + def kube_logs_response + kube_response(kube_logs_body) + end + def kube_deployments_response kube_response(kube_deployments_body) end @@ -34,6 +38,13 @@ module KubernetesHelpers WebMock.stub_request(:get, pods_url).to_return(response || kube_pods_response) end + def stub_kubeclient_logs(pod_name, response = nil) + stub_kubeclient_discover(service.api_url) + logs_url = service.api_url + "/api/v1/namespaces/#{service.actual_namespace}/pods/#{pod_name}/log?tailLines=#{Clusters::Platforms::Kubernetes::LOGS_LIMIT}" + + WebMock.stub_request(:get, logs_url).to_return(response || kube_logs_response) + end + def stub_kubeclient_deployments(response = nil) stub_kubeclient_discover(service.api_url) deployments_url = service.api_url + "/apis/extensions/v1beta1/namespaces/#{service.actual_namespace}/deployments" @@ -212,6 +223,10 @@ module KubernetesHelpers } end + def kube_logs_body + "Log 1\nLog 2\nLog 3" + end + def kube_deployments_body { "kind" => "DeploymentList", diff --git a/spec/support/helpers/login_helpers.rb b/spec/support/helpers/login_helpers.rb index 3fee6872498..4a0cf62a661 100644 --- a/spec/support/helpers/login_helpers.rb +++ b/spec/support/helpers/login_helpers.rb @@ -47,7 +47,7 @@ module LoginHelpers end def gitlab_sign_in_via(provider, user, uid, saml_response = nil) - mock_auth_hash(provider, uid, user.email, saml_response) + mock_auth_hash_with_saml_xml(provider, uid, user.email, saml_response) visit new_user_session_path click_link provider end @@ -87,7 +87,12 @@ module LoginHelpers click_link "oauth-login-#{provider}" end - def mock_auth_hash(provider, uid, email, saml_response = nil) + def mock_auth_hash_with_saml_xml(provider, uid, email, saml_response) + response_object = { document: saml_xml(saml_response) } + mock_auth_hash(provider, uid, email, response_object: response_object) + end + + def mock_auth_hash(provider, uid, email, response_object: nil) # The mock_auth configuration allows you to set per-provider (or default) # authentication hashes to return during integration testing. OmniAuth.config.mock_auth[provider.to_sym] = OmniAuth::AuthHash.new({ @@ -110,9 +115,7 @@ module LoginHelpers image: 'mock_user_thumbnail_url' } }, - response_object: { - document: saml_xml(saml_response) - } + response_object: response_object } }) Rails.application.env_config['omniauth.auth'] = OmniAuth.config.mock_auth[provider.to_sym] diff --git a/spec/support/import_export/export_file_helper.rb b/spec/support/import_export/export_file_helper.rb index ac320934f5a..388b88f0331 100644 --- a/spec/support/import_export/export_file_helper.rb +++ b/spec/support/import_export/export_file_helper.rb @@ -91,7 +91,7 @@ module ExportFileHelper loop do object_with_parent = deep_find_with_parent(sensitive_word, project_hash) - return nil unless object_with_parent && object_with_parent.object + return unless object_with_parent && object_with_parent.object if is_safe_hash?(object_with_parent.parent, sensitive_word) # It's in the safe list, remove hash and keep looking diff --git a/spec/support/matchers/access_matchers.rb b/spec/support/matchers/access_matchers.rb index 3e4ca8b7ab0..e6899e2d23c 100644 --- a/spec/support/matchers/access_matchers.rb +++ b/spec/support/matchers/access_matchers.rb @@ -7,29 +7,28 @@ module AccessMatchers extend RSpec::Matchers::DSL include Warden::Test::Helpers - def emulate_user(user, membership = nil) - case user - when :user - login_as(create(:user)) + def emulate_user(user_type_or_trait, membership = nil) + case user_type_or_trait + when :user, :admin + login_as(create(user_type_or_trait)) + when :external, :auditor + login_as(create(:user, user_type_or_trait)) when :visitor logout - when :admin - login_as(create(:admin)) - when :external - login_as(create(:user, external: true)) when User - login_as(user) + login_as(user_type_or_trait) when *Gitlab::Access.sym_options_with_owner.keys - raise ArgumentError, "cannot emulate #{user} without membership parent" unless membership - - role = user + raise ArgumentError, "cannot emulate #{user_type_or_trait} without membership parent" unless membership - if role == :owner && membership.owner - user = membership.owner - else - user = create(:user) - membership.public_send(:"add_#{role}", user) - end + role = user_type_or_trait + user = + if role == :owner && membership.owner + membership.owner + else + create(:user).tap do |new_user| + membership.public_send(:"add_#{role}", new_user) + end + end login_as(user) else diff --git a/spec/support/pg_stat_activity.rb b/spec/support/pg_stat_activity.rb new file mode 100644 index 00000000000..f93fba08a19 --- /dev/null +++ b/spec/support/pg_stat_activity.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +RSpec.configure do |config| + config.before do + if Gitlab::Database.postgresql? && ENV['PG_STAT_WARNING_THRESHOLD'] + warning_threshold = ENV['PG_STAT_WARNING_THRESHOLD'].to_i + results = ActiveRecord::Base.connection.execute('SELECT * FROM pg_stat_activity') + ntuples = results.ntuples + + warn("pg_stat_activity count: #{ntuples}") + + if ntuples > warning_threshold + results.each do |result| + warn result.inspect + end + end + end + end +end diff --git a/spec/support/shared_contexts/services_shared_context.rb b/spec/support/shared_contexts/services_shared_context.rb index d92e8318fa0..089f1798cd2 100644 --- a/spec/support/shared_contexts/services_shared_context.rb +++ b/spec/support/shared_contexts/services_shared_context.rb @@ -26,6 +26,14 @@ Service.available_services_names.each do |service| end end + before do + if service == 'github' && respond_to?(:stub_licensed_features) + stub_licensed_features(github_project_service_integration: true) + project.clear_memoization(:disabled_services) + project.clear_memoization(:licensed_feature_available) + end + end + def initialize_service(service) service_item = project.find_or_initialize_service(service) service_item.properties = service_attrs diff --git a/spec/support/shared_examples/issuable_shared_examples.rb b/spec/support/shared_examples/issuable_shared_examples.rb index c3d40c5b231..d97b21f71cd 100644 --- a/spec/support/shared_examples/issuable_shared_examples.rb +++ b/spec/support/shared_examples/issuable_shared_examples.rb @@ -31,7 +31,7 @@ shared_examples 'system notes for milestones' do context 'project milestones' do it 'creates a system note' do expect do - update_issuable(milestone: create(:milestone)) + update_issuable(milestone: create(:milestone, project: project)) end.to change { Note.system.count }.by(1) end end diff --git a/spec/support/shared_examples/notify_shared_examples.rb b/spec/support/shared_examples/notify_shared_examples.rb index a38354060cf..4fff1c4e228 100644 --- a/spec/support/shared_examples/notify_shared_examples.rb +++ b/spec/support/shared_examples/notify_shared_examples.rb @@ -252,3 +252,31 @@ shared_examples 'a note email' do end end end + +shared_examples 'appearance header and footer enabled' do + it "contains header and footer" do + create :appearance, header_message: "Foo", footer_message: "Bar", email_header_and_footer_enabled: true + + aggregate_failures do + expect(subject.html_part).to have_body_text("<div class=\"header-message\" style=\"\"><p>Foo</p></div>") + expect(subject.html_part).to have_body_text("<div class=\"footer-message\" style=\"\"><p>Bar</p></div>") + + expect(subject.text_part).to have_body_text(/^Foo/) + expect(subject.text_part).to have_body_text(/Bar$/) + end + end +end + +shared_examples 'appearance header and footer not enabled' do + it "does not contain header and footer" do + create :appearance, header_message: "Foo", footer_message: "Bar", email_header_and_footer_enabled: false + + aggregate_failures do + expect(subject.html_part).not_to have_body_text("<div class=\"header-message\" style=\"\"><p>Foo</p></div>") + expect(subject.html_part).not_to have_body_text("<div class=\"footer-message\" style=\"\"><p>Bar</p></div>") + + expect(subject.text_part).not_to have_body_text(/^Foo/) + expect(subject.text_part).not_to have_body_text(/Bar$/) + end + end +end diff --git a/spec/support/shared_examples/requests/api/discussions.rb b/spec/support/shared_examples/requests/api/discussions.rb index e44da4faa5a..eff8e401bad 100644 --- a/spec/support/shared_examples/requests/api/discussions.rb +++ b/spec/support/shared_examples/requests/api/discussions.rb @@ -86,6 +86,37 @@ shared_examples 'discussions API' do |parent_type, noteable_type, id_name| expect(response).to have_gitlab_http_status(404) end end + + context 'when a project is public with private repo access' do + let!(:parent) { create(:project, :public, :repository, :repository_private, :snippets_private) } + let!(:user_without_access) { create(:user) } + + context 'when user is not a team member of private repo' do + before do + project.team.truncate + end + + context "creating a new note" do + before do + post api("/#{parent_type}/#{parent.id}/#{noteable_type}/#{noteable[id_name]}/discussions", user_without_access), params: { body: 'hi!' } + end + + it 'raises 404 error' do + expect(response).to have_gitlab_http_status(404) + end + end + + context "fetching a discussion" do + before do + get api("/#{parent_type}/#{parent.id}/#{noteable_type}/#{noteable[id_name]}/discussions/#{note.discussion_id}", user_without_access) + end + + it 'raises 404 error' do + expect(response).to have_gitlab_http_status(404) + end + end + end + end end describe "POST /#{parent_type}/:id/#{noteable_type}/:noteable_id/discussions/:discussion_id/notes" do diff --git a/spec/support/shared_examples/requests/api/merge_requests_list.rb b/spec/support/shared_examples/requests/api/merge_requests_list.rb index 6713ec47ace..32e3b81c3c5 100644 --- a/spec/support/shared_examples/requests/api/merge_requests_list.rb +++ b/spec/support/shared_examples/requests/api/merge_requests_list.rb @@ -186,6 +186,37 @@ shared_examples 'merge requests list' do expect(json_response.length).to eq(0) end + it 'returns an array of labeled merge requests where all labels match' do + path = endpoint_path + "?labels[]=#{label.title}&labels[]=#{label2.title}" + + get api(path, user) + + expect(response).to have_gitlab_http_status(200) + expect(json_response).to be_an Array + expect(json_response.length).to eq(1) + expect(json_response.first['labels']).to eq([label2.title, label.title]) + end + + it 'returns an array of merge requests with any label when filtering by any label' do + get api(endpoint_path, user), params: { labels: [" #{label.title} ", " #{label2.title} "] } + + expect_paginated_array_response + expect(json_response).to be_an Array + expect(json_response.length).to eq(1) + expect(json_response.first['labels']).to eq([label2.title, label.title]) + expect(json_response.first['id']).to eq(merge_request.id) + end + + it 'returns an array of merge requests with any label when filtering by any label' do + get api(endpoint_path, user), params: { labels: ["#{label.title} , #{label2.title}"] } + + expect_paginated_array_response + expect(json_response).to be_an Array + expect(json_response.length).to eq(1) + expect(json_response.first['labels']).to eq([label2.title, label.title]) + expect(json_response.first['id']).to eq(merge_request.id) + end + it 'returns an array of merge requests with any label when filtering by any label' do get api(endpoint_path, user), params: { labels: IssuesFinder::FILTER_ANY } diff --git a/spec/support/shared_examples/views/nav_sidebar.rb b/spec/support/shared_examples/views/nav_sidebar.rb new file mode 100644 index 00000000000..6ac5abe275d --- /dev/null +++ b/spec/support/shared_examples/views/nav_sidebar.rb @@ -0,0 +1,11 @@ +# frozen_string_literal: true + +shared_examples 'has nav sidebar' do + it 'has collapsed nav sidebar on mobile' do + render + + expect(rendered).to have_selector('.nav-sidebar') + expect(rendered).not_to have_selector('.sidebar-collapsed-desktop') + expect(rendered).not_to have_selector('.sidebar-expanded-mobile') + end +end diff --git a/spec/support/webmock.rb b/spec/support/webmock.rb index af2906b7568..9ac7e7fc515 100644 --- a/spec/support/webmock.rb +++ b/spec/support/webmock.rb @@ -1,4 +1,12 @@ require 'webmock' require 'webmock/rspec' -WebMock.disable_net_connect!(allow_localhost: true) +def webmock_allowed_hosts + %w[elasticsearch registry.gitlab.com-gitlab-org-test-elastic-image].tap do |hosts| + if ENV.key?('ELASTIC_URL') + hosts << URI.parse(ENV['ELASTIC_URL']).host + end + end.uniq +end + +WebMock.disable_net_connect!(allow_localhost: true, allow: webmock_allowed_hosts) |